@hostwebhook/node-types 1.83.0 → 1.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/airtable-operations.d.ts +90 -0
- package/dist/airtable-operations.js +274 -0
- package/dist/calendly-operations.d.ts +81 -0
- package/dist/calendly-operations.js +301 -0
- package/dist/connections.js +2 -0
- package/dist/credentials.d.ts +6 -0
- package/dist/credentials.js +20 -0
- package/dist/dispatch.js +2 -0
- package/dist/esm/airtable-operations.d.ts +90 -0
- package/dist/esm/airtable-operations.js +270 -0
- package/dist/esm/calendly-operations.d.ts +81 -0
- package/dist/esm/calendly-operations.js +297 -0
- package/dist/esm/connections.js +2 -0
- package/dist/esm/credentials.d.ts +6 -0
- package/dist/esm/credentials.js +20 -0
- package/dist/esm/dispatch.js +2 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/registry.js +35 -0
- package/dist/esm/types.d.ts +1 -1
- package/dist/esm/ui.js +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -2
- package/dist/registry.js +35 -0
- package/dist/types.d.ts +1 -1
- package/dist/ui.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CALENDLY_OPERATION_SPECS = exports.CALENDLY_ITERABLE_OPERATIONS = exports.CALENDLY_OPERATIONS = void 0;
|
|
4
|
+
exports.isCalendlyOperation = isCalendlyOperation;
|
|
5
|
+
/**
|
|
6
|
+
* El nodo de Calendly: lo que pasa alrededor de una reserva.
|
|
7
|
+
*
|
|
8
|
+
* ## Por qué existe
|
|
9
|
+
*
|
|
10
|
+
* [2026-09-15] Calendly expone una API v2 con OAuth 2.0 (PKCE + secret,
|
|
11
|
+
* refresh de un solo uso) y webhooks firmados. La reserva en sí la hace el
|
|
12
|
+
* invitado en Calendly; lo que HostWebhook necesita es leerla (el trigger la
|
|
13
|
+
* trae; este nodo la consulta), cancelarla, marcar el no-show, y las dos
|
|
14
|
+
* que cierran el círculo: un link de reserva que caduca al usarse y los
|
|
15
|
+
* huecos libres de un tipo de evento, para que un agente los ofrezca.
|
|
16
|
+
*
|
|
17
|
+
* ## Cómo se nombra todo
|
|
18
|
+
*
|
|
19
|
+
* Calendly identifica cada cosa por su URI completa
|
|
20
|
+
* (`https://api.calendly.com/scheduled_events/ABC…`), no por un id corto.
|
|
21
|
+
* Los parámetros que piden una la aceptan entera o sólo el uuid final; el
|
|
22
|
+
* servicio completa el prefijo. Los que piden «Mine / Whole organization»
|
|
23
|
+
* resuelven al `owner` y `organization` que la credencial guardó al
|
|
24
|
+
* conectar, así que el usuario no escribe URIs nunca.
|
|
25
|
+
*
|
|
26
|
+
* ## Por qué nueve
|
|
27
|
+
*
|
|
28
|
+
* Eventos (listar, uno, invitados, cancelar), no-show (poner y quitar),
|
|
29
|
+
* tipos de evento (listar), huecos libres y link de un solo uso. Fuera a
|
|
30
|
+
* propósito: crear tipos de evento, contactos, routing forms, recaps y toda
|
|
31
|
+
* la gestión de organización —nada de eso es «lo que pasa alrededor de una
|
|
32
|
+
* reserva». Los webhooks van en el trigger, no aquí.
|
|
33
|
+
*
|
|
34
|
+
* ⚠️ El orden de este array es el orden del desplegable.
|
|
35
|
+
*/
|
|
36
|
+
exports.CALENDLY_OPERATIONS = [
|
|
37
|
+
"listEvents",
|
|
38
|
+
"getEvent",
|
|
39
|
+
"listInvitees",
|
|
40
|
+
"cancelEvent",
|
|
41
|
+
"markNoShow",
|
|
42
|
+
"undoNoShow",
|
|
43
|
+
"listEventTypes",
|
|
44
|
+
"getAvailableTimes",
|
|
45
|
+
"createSchedulingLink",
|
|
46
|
+
];
|
|
47
|
+
function isCalendlyOperation(value) {
|
|
48
|
+
return (typeof value === "string" &&
|
|
49
|
+
exports.CALENDLY_OPERATIONS.includes(value));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Las que devuelven una COLECCIÓN sobre la que el nodo de abajo repite. Cada
|
|
53
|
+
* una itera SU campo (`iterateField` en la spec): `events`, `invitees`,
|
|
54
|
+
* `eventTypes`, `slots`. La salida lleva `_meta.iterable: true`.
|
|
55
|
+
*/
|
|
56
|
+
exports.CALENDLY_ITERABLE_OPERATIONS = [
|
|
57
|
+
"listEvents",
|
|
58
|
+
"listInvitees",
|
|
59
|
+
"listEventTypes",
|
|
60
|
+
"getAvailableTimes",
|
|
61
|
+
];
|
|
62
|
+
/* «Mine» es el usuario que conectó; «Whole organization» exige ser admin u
|
|
63
|
+
owner en Calendly, y si no lo es la API contesta 403 y el nodo lo dice. */
|
|
64
|
+
const owner = () => ({
|
|
65
|
+
name: "owner",
|
|
66
|
+
label: "Whose",
|
|
67
|
+
type: "select",
|
|
68
|
+
options: [
|
|
69
|
+
{ value: "mine", label: "Mine — the connected user" },
|
|
70
|
+
{ value: "organization", label: "Whole organization (admins only)" },
|
|
71
|
+
],
|
|
72
|
+
default: "mine",
|
|
73
|
+
description: "Mine uses the account that connected the credential. Whole organization needs an organization admin or owner.",
|
|
74
|
+
});
|
|
75
|
+
/* Un evento se nombra por su URI o por el uuid del final; da igual. */
|
|
76
|
+
const eventUri = () => ({
|
|
77
|
+
name: "eventUri",
|
|
78
|
+
label: "Event",
|
|
79
|
+
type: "template",
|
|
80
|
+
required: true,
|
|
81
|
+
placeholder: "{{payload.event.uri}}",
|
|
82
|
+
description: "The scheduled event — its URI, or just the ID at the end of it. Usually {{payload.event.uri}} from a Calendly trigger.",
|
|
83
|
+
});
|
|
84
|
+
const eventType = () => ({
|
|
85
|
+
name: "eventType",
|
|
86
|
+
label: "Event type",
|
|
87
|
+
type: "eventType",
|
|
88
|
+
required: true,
|
|
89
|
+
description: "One of the event types on the connected account (e.g. “30 min intro call”).",
|
|
90
|
+
});
|
|
91
|
+
/* La zona en la que se leen las fechas de pared de la operación. Sin elegir,
|
|
92
|
+
la del workspace: el dashboard lo dice debajo del selector y el nodo la lee
|
|
93
|
+
al ejecutar, como Calendar. */
|
|
94
|
+
const timezone = () => ({
|
|
95
|
+
name: "timezone",
|
|
96
|
+
label: "Time zone",
|
|
97
|
+
type: "timezone",
|
|
98
|
+
advanced: true,
|
|
99
|
+
description: "The zone the dates above are read in when they carry no offset. Empty = the workspace's time zone.",
|
|
100
|
+
});
|
|
101
|
+
const pageLimit = (max, fallback) => ({
|
|
102
|
+
name: "limit",
|
|
103
|
+
label: "Max results",
|
|
104
|
+
type: "number",
|
|
105
|
+
min: 1,
|
|
106
|
+
max,
|
|
107
|
+
default: fallback,
|
|
108
|
+
advanced: true,
|
|
109
|
+
description: `How many to bring into the flow, at most. Default ${fallback}.`,
|
|
110
|
+
});
|
|
111
|
+
exports.CALENDLY_OPERATION_SPECS = {
|
|
112
|
+
listEvents: {
|
|
113
|
+
label: "List events",
|
|
114
|
+
description: "Scheduled events in a time window — yours or the whole organization's. Each one comes with its event type, hosts and location; invitees are a separate step.",
|
|
115
|
+
apiRoute: "GET /scheduled_events",
|
|
116
|
+
iterateField: "events",
|
|
117
|
+
params: [
|
|
118
|
+
owner(),
|
|
119
|
+
{
|
|
120
|
+
name: "status",
|
|
121
|
+
label: "Status",
|
|
122
|
+
type: "select",
|
|
123
|
+
options: [
|
|
124
|
+
{ value: "active", label: "Active — upcoming and past, not canceled" },
|
|
125
|
+
{ value: "canceled", label: "Canceled" },
|
|
126
|
+
{ value: "all", label: "All" },
|
|
127
|
+
],
|
|
128
|
+
default: "active",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "minStartTime",
|
|
132
|
+
label: "Starting from",
|
|
133
|
+
type: "datetime",
|
|
134
|
+
placeholder: "{{payload.from}}",
|
|
135
|
+
description: "Only events that start at or after this moment. Empty = no lower bound.",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: "maxStartTime",
|
|
139
|
+
label: "Starting before",
|
|
140
|
+
type: "datetime",
|
|
141
|
+
placeholder: "{{payload.to}}",
|
|
142
|
+
description: "Only events that start before this moment. Empty = no upper bound.",
|
|
143
|
+
},
|
|
144
|
+
timezone(),
|
|
145
|
+
{
|
|
146
|
+
name: "inviteeEmail",
|
|
147
|
+
label: "Invitee email",
|
|
148
|
+
type: "template",
|
|
149
|
+
placeholder: "{{payload.email}}",
|
|
150
|
+
advanced: true,
|
|
151
|
+
description: "Only events booked by this email. Empty = everyone.",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "sort",
|
|
155
|
+
label: "Order",
|
|
156
|
+
type: "select",
|
|
157
|
+
options: [
|
|
158
|
+
{ value: "start_time:asc", label: "Soonest first" },
|
|
159
|
+
{ value: "start_time:desc", label: "Latest first" },
|
|
160
|
+
],
|
|
161
|
+
default: "start_time:asc",
|
|
162
|
+
advanced: true,
|
|
163
|
+
},
|
|
164
|
+
pageLimit(500, 100),
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
getEvent: {
|
|
168
|
+
label: "Get event",
|
|
169
|
+
description: "One scheduled event by its URI: name, start and end, event type, hosts, location, and how many invitees it has.",
|
|
170
|
+
apiRoute: "GET /scheduled_events/{uuid}",
|
|
171
|
+
params: [eventUri()],
|
|
172
|
+
},
|
|
173
|
+
listInvitees: {
|
|
174
|
+
label: "List invitees",
|
|
175
|
+
description: "Who booked an event: name, email, timezone, their answers to the booking questions, and whether they canceled or rescheduled.",
|
|
176
|
+
apiRoute: "GET /scheduled_events/{uuid}/invitees",
|
|
177
|
+
iterateField: "invitees",
|
|
178
|
+
params: [
|
|
179
|
+
eventUri(),
|
|
180
|
+
{
|
|
181
|
+
name: "status",
|
|
182
|
+
label: "Status",
|
|
183
|
+
type: "select",
|
|
184
|
+
options: [
|
|
185
|
+
{ value: "active", label: "Active" },
|
|
186
|
+
{ value: "canceled", label: "Canceled" },
|
|
187
|
+
{ value: "all", label: "All" },
|
|
188
|
+
],
|
|
189
|
+
default: "all",
|
|
190
|
+
advanced: true,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: "inviteeEmail",
|
|
194
|
+
label: "Invitee email",
|
|
195
|
+
type: "template",
|
|
196
|
+
placeholder: "{{payload.email}}",
|
|
197
|
+
advanced: true,
|
|
198
|
+
description: "Only the invitee with this email. Empty = all of them.",
|
|
199
|
+
},
|
|
200
|
+
pageLimit(100, 100),
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
cancelEvent: {
|
|
204
|
+
label: "Cancel event",
|
|
205
|
+
description: "Cancels a scheduled event and notifies its invitees, the same way the host would from Calendly.",
|
|
206
|
+
apiRoute: "POST /scheduled_events/{uuid}/cancellation",
|
|
207
|
+
params: [
|
|
208
|
+
eventUri(),
|
|
209
|
+
{
|
|
210
|
+
name: "reason",
|
|
211
|
+
label: "Reason",
|
|
212
|
+
type: "textarea",
|
|
213
|
+
placeholder: "Sorry — something came up. Please rebook with the link below.",
|
|
214
|
+
description: "Shown to the invitees in the cancellation email. Supports {{payload.x}}. Empty = no reason.",
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
markNoShow: {
|
|
219
|
+
label: "Mark invitee as no-show",
|
|
220
|
+
description: "Marks an invitee as a no-show on a past event. Returns the no-show record; keep its URI if you may need to undo it.",
|
|
221
|
+
apiRoute: "POST /invitee_no_shows",
|
|
222
|
+
params: [
|
|
223
|
+
{
|
|
224
|
+
name: "inviteeUri",
|
|
225
|
+
label: "Invitee",
|
|
226
|
+
type: "template",
|
|
227
|
+
required: true,
|
|
228
|
+
placeholder: "{{payload.invitee.uri}}",
|
|
229
|
+
description: "The invitee — its URI, from List invitees or a Calendly trigger.",
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
undoNoShow: {
|
|
234
|
+
label: "Undo no-show",
|
|
235
|
+
description: "Removes a no-show mark. Takes the URI that Mark invitee as no-show returned.",
|
|
236
|
+
apiRoute: "DELETE /invitee_no_shows/{uuid}",
|
|
237
|
+
params: [
|
|
238
|
+
{
|
|
239
|
+
name: "noShowUri",
|
|
240
|
+
label: "No-show",
|
|
241
|
+
type: "template",
|
|
242
|
+
required: true,
|
|
243
|
+
placeholder: "{{payload.uri}}",
|
|
244
|
+
description: "The no-show record to remove — its URI, or just the ID at the end.",
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
listEventTypes: {
|
|
249
|
+
label: "List event types",
|
|
250
|
+
description: "The event types you (or the organization) offer: name, duration, scheduling URL, and whether they are active. What you need before asking for available times or a link.",
|
|
251
|
+
apiRoute: "GET /event_types",
|
|
252
|
+
iterateField: "eventTypes",
|
|
253
|
+
params: [
|
|
254
|
+
owner(),
|
|
255
|
+
{
|
|
256
|
+
name: "activeOnly",
|
|
257
|
+
label: "Active only",
|
|
258
|
+
type: "booleanSelect",
|
|
259
|
+
options: [
|
|
260
|
+
{ value: "true", label: "Yes — only event types that can be booked" },
|
|
261
|
+
{ value: "false", label: "No — include hidden and inactive ones" },
|
|
262
|
+
],
|
|
263
|
+
default: "true",
|
|
264
|
+
advanced: true,
|
|
265
|
+
},
|
|
266
|
+
pageLimit(100, 100),
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
getAvailableTimes: {
|
|
270
|
+
label: "Get available times",
|
|
271
|
+
description: "The free slots of an event type in a window of up to 7 days, each with its start time and a scheduling URL that books exactly that slot. Made for an agent that offers times.",
|
|
272
|
+
apiRoute: "GET /event_type_available_times",
|
|
273
|
+
iterateField: "slots",
|
|
274
|
+
params: [
|
|
275
|
+
eventType(),
|
|
276
|
+
{
|
|
277
|
+
name: "startTime",
|
|
278
|
+
label: "From",
|
|
279
|
+
type: "datetime",
|
|
280
|
+
required: true,
|
|
281
|
+
placeholder: "{{payload.from}}",
|
|
282
|
+
description: "Start of the window. Must be in the future.",
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "endTime",
|
|
286
|
+
label: "To",
|
|
287
|
+
type: "datetime",
|
|
288
|
+
required: true,
|
|
289
|
+
placeholder: "{{payload.to}}",
|
|
290
|
+
description: "End of the window — at most 7 days after From. A longer window is cut to 7 days.",
|
|
291
|
+
},
|
|
292
|
+
timezone(),
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
createSchedulingLink: {
|
|
296
|
+
label: "Create single-use scheduling link",
|
|
297
|
+
description: "A booking link for one event type that stops working after one booking. Send it to someone instead of your public link.",
|
|
298
|
+
apiRoute: "POST /scheduling_links",
|
|
299
|
+
params: [eventType()],
|
|
300
|
+
},
|
|
301
|
+
};
|
package/dist/connections.js
CHANGED
|
@@ -83,6 +83,8 @@ exports.NODE_CONNECTIONS = {
|
|
|
83
83
|
jiraAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
84
84
|
bucketAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
85
85
|
apifyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
86
|
+
calendlyAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
87
|
+
airtableAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
86
88
|
googleContactsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
87
89
|
googleAnalyticsAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
|
88
90
|
notionAction: { acceptsInputFrom: ACTION_INPUTS, canOutputTo: PROCESSING_OUTPUTS },
|
package/dist/credentials.d.ts
CHANGED
|
@@ -72,6 +72,12 @@ export declare const CREDENTIAL_TYPES: readonly [{
|
|
|
72
72
|
readonly type: "firecrawl";
|
|
73
73
|
}, {
|
|
74
74
|
readonly type: "apify";
|
|
75
|
+
}, {
|
|
76
|
+
readonly type: "calendly_oauth2";
|
|
77
|
+
}, {
|
|
78
|
+
readonly type: "calendly";
|
|
79
|
+
}, {
|
|
80
|
+
readonly type: "airtable_oauth2";
|
|
75
81
|
}, {
|
|
76
82
|
readonly type: "http_auth";
|
|
77
83
|
}, {
|
package/dist/credentials.js
CHANGED
|
@@ -58,6 +58,26 @@ exports.CREDENTIAL_TYPES = [
|
|
|
58
58
|
/* [2026-09-13] Apify no tiene OAuth para terceros: es un API token, como
|
|
59
59
|
Firecrawl. Lo lee el nodo `apifyAction`. */
|
|
60
60
|
{ type: 'apify' },
|
|
61
|
+
/* [2026-09-15] El OAuth de HostWebhook contra Calendly. Se anota ANTES de
|
|
62
|
+
escribir el servicio, por lo que cuenta el bloque de Mailchimp de abajo:
|
|
63
|
+
sin esta línea el `create` de la credencial muere en runtime con un enum
|
|
64
|
+
de Mongoose que TypeScript no ve. Lleva `_oauth2` como Mailchimp y
|
|
65
|
+
Shopify. Guarda, además de los tokens, el `owner` y la `organization`
|
|
66
|
+
(URIs) que el token endpoint devuelve: cada lista y cada webhook los
|
|
67
|
+
necesita. El refresh token es de UN solo uso: se sobreescribe en el
|
|
68
|
+
mismo write que lo gasta. */
|
|
69
|
+
{ type: 'calendly_oauth2' },
|
|
70
|
+
/* El personal access token de Calendly (Integrations → API & webhooks),
|
|
71
|
+
para quien no quiera OAuth. Va aparte de `calendly_oauth2` por lo mismo
|
|
72
|
+
que `discord_bot` va aparte de `discord_oauth`: lo que guardan es
|
|
73
|
+
distinto (aquí un secreto suyo que no caduca y ninguna URI hasta que se
|
|
74
|
+
valida con `GET /users/me`). */
|
|
75
|
+
{ type: 'calendly' },
|
|
76
|
+
/* El OAuth de HostWebhook contra Airtable. PKCE obligatorio, secret por
|
|
77
|
+
Basic auth, access token de 60 minutos y refresh de 60 días que rota
|
|
78
|
+
en cada uso. El usuario elige en la pantalla de Airtable QUÉ bases
|
|
79
|
+
concede; los desplegables sólo listan ésas. */
|
|
80
|
+
{ type: 'airtable_oauth2' },
|
|
61
81
|
{ type: 'http_auth' },
|
|
62
82
|
{ type: 'aws_s3' },
|
|
63
83
|
/* [2026-09-14] `memory_contextwindow` se retiró: el backend de memoria
|
package/dist/dispatch.js
CHANGED
|
@@ -64,6 +64,8 @@ exports.NODE_DISPATCH = {
|
|
|
64
64
|
jiraAction: { service: 'jiraActionsService', collection: 'jiraactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
65
65
|
bucketAction: { service: 'bucketActionsService', collection: 'bucketactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
66
66
|
apifyAction: { service: 'apifyActionsService', collection: 'apifyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
67
|
+
calendlyAction: { service: 'calendlyActionsService', collection: 'calendlyactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
68
|
+
airtableAction: { service: 'airtableActionsService', collection: 'airtableactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
67
69
|
slackAction: { service: 'slackActionsService', collection: 'slackactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
68
70
|
googleContactsAction: { service: 'googleContactsActionsService', collection: 'googlecontactsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
69
71
|
googleAnalyticsAction: { service: 'googleAnalyticsActionsService', collection: 'googleanalyticsactions', hasFilters: true, outputFields: ['outputNodes'], customDispatch: true, pipelineDispatch: 'excluded', downstreamPayload: 'result' },
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* El nodo de Airtable: leer y escribir records de una tabla.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué existe
|
|
5
|
+
*
|
|
6
|
+
* [2026-09-15] Airtable expone una API REST con OAuth 2.0 (PKCE obligatorio,
|
|
7
|
+
* refresh que rota) y webhooks. Es «la hoja de cálculo con tipos» que mucha
|
|
8
|
+
* gente usa de base de datos ligera, así que este nodo se parece más a
|
|
9
|
+
* Postgres/Sheets que a un servicio de mensajería: casi todo es CRUD sobre
|
|
10
|
+
* una tabla.
|
|
11
|
+
*
|
|
12
|
+
* ## Cómo se nombra todo
|
|
13
|
+
*
|
|
14
|
+
* Una base es `appXXXXXXXXXXXXXX`, una tabla `tblXXXXXXXXXXXXXX`, una vista
|
|
15
|
+
* `viwXXXXXXXXXXXXXX`, un record `recXXXXXXXXXXXXXX`. Base y tabla se eligen
|
|
16
|
+
* en desplegables que lee el esquema (`GET /v0/meta/bases`,
|
|
17
|
+
* `GET /v0/meta/bases/{baseId}/tables`), y **los campos se escriben por
|
|
18
|
+
* nombre**, que es como los ve el usuario en Airtable. El esquema trae tipo
|
|
19
|
+
* y opciones de cada campo, y el editor de campos los usa para ofrecer lo
|
|
20
|
+
* que cabe: las opciones de un select, el formato de una fecha.
|
|
21
|
+
*
|
|
22
|
+
* ## Uno o un lote
|
|
23
|
+
*
|
|
24
|
+
* Cada operación de escritura trabaja sobre UN record —el caso normal, con
|
|
25
|
+
* el Loop node repitiendo si hacen falta varios— y tiene en Advanced un
|
|
26
|
+
* «Batch» que acepta un array del payload. Airtable admite 10 por llamada y
|
|
27
|
+
* 5 llamadas por segundo por base; el servicio trocea y espera, el usuario
|
|
28
|
+
* no tiene que saberlo.
|
|
29
|
+
*
|
|
30
|
+
* ## Por qué ocho
|
|
31
|
+
*
|
|
32
|
+
* Las seis de records (listar, uno, crear, editar, upsert, borrar) más las
|
|
33
|
+
* dos de esquema que un flujo puede necesitar (bases, tablas y campos).
|
|
34
|
+
* Fuera a propósito: comentarios, crear tablas o campos, y los webhooks,
|
|
35
|
+
* que son del trigger.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ El orden de este array es el orden del desplegable.
|
|
38
|
+
*/
|
|
39
|
+
export declare const AIRTABLE_OPERATIONS: readonly ["listRecords", "getRecord", "createRecord", "updateRecord", "upsertRecord", "deleteRecord", "listBases", "getBaseSchema"];
|
|
40
|
+
export type AirtableOperation = (typeof AIRTABLE_OPERATIONS)[number];
|
|
41
|
+
export declare function isAirtableOperation(value: unknown): value is AirtableOperation;
|
|
42
|
+
/**
|
|
43
|
+
* Las que devuelven una COLECCIÓN sobre la que el nodo de abajo repite. Cada
|
|
44
|
+
* una itera SU campo (`iterateField`): `records`, `bases`, `tables`. La
|
|
45
|
+
* salida lleva `_meta.iterable: true`.
|
|
46
|
+
*/
|
|
47
|
+
export declare const AIRTABLE_ITERABLE_OPERATIONS: ReadonlyArray<AirtableOperation>;
|
|
48
|
+
/**
|
|
49
|
+
* Tipos de parámetro. Los seis primeros son los de Apify. Los nuevos leen el
|
|
50
|
+
* esquema de la credencial y por eso son de Airtable:
|
|
51
|
+
*
|
|
52
|
+
* - `base`: desplegable de bases (`GET /airtable-actions/bases`); el valor es
|
|
53
|
+
* el `appXXX`.
|
|
54
|
+
* - `table`: desplegable de tablas de la base elegida; el valor es el `tblXXX`.
|
|
55
|
+
* - `view`: desplegable de vistas de la tabla (opcional); el valor es el `viwXXX`.
|
|
56
|
+
* - `field`: UN campo de la tabla, por nombre.
|
|
57
|
+
* - `fieldList`: VARIOS campos de la tabla, por nombre; se guarda como array.
|
|
58
|
+
* - `fields`: el editor de pares campo → valor, con el tipo y las opciones
|
|
59
|
+
* de cada campo delante; se guarda como objeto `{ "Nombre": valor }` y las
|
|
60
|
+
* cadenas admiten plantilla.
|
|
61
|
+
*
|
|
62
|
+
* Todos admiten plantilla como alternativa al desplegable, para cuando el id
|
|
63
|
+
* viene del payload.
|
|
64
|
+
*/
|
|
65
|
+
export type AirtableParamType = "template" | "textarea" | "json" | "select" | "number" | "booleanSelect" | "base" | "table" | "view" | "field" | "fieldList" | "fields";
|
|
66
|
+
export interface AirtableParamSpec {
|
|
67
|
+
name: string;
|
|
68
|
+
label: string;
|
|
69
|
+
type: AirtableParamType;
|
|
70
|
+
required?: boolean;
|
|
71
|
+
description?: string;
|
|
72
|
+
placeholder?: string;
|
|
73
|
+
default?: string | number;
|
|
74
|
+
options?: ReadonlyArray<{
|
|
75
|
+
value: string;
|
|
76
|
+
label: string;
|
|
77
|
+
}>;
|
|
78
|
+
min?: number;
|
|
79
|
+
max?: number;
|
|
80
|
+
advanced?: boolean;
|
|
81
|
+
}
|
|
82
|
+
export interface AirtableOperationSpec {
|
|
83
|
+
label: string;
|
|
84
|
+
description: string;
|
|
85
|
+
apiRoute: string;
|
|
86
|
+
params: AirtableParamSpec[];
|
|
87
|
+
/** El campo de la salida que se itera; sólo en las iterables. */
|
|
88
|
+
iterateField?: string;
|
|
89
|
+
}
|
|
90
|
+
export declare const AIRTABLE_OPERATION_SPECS: Record<AirtableOperation, AirtableOperationSpec>;
|