@funnelchat-hq/sdk 0.0.0-stage → 0.1.2

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/index.js ADDED
@@ -0,0 +1,1491 @@
1
+ import { createHmac, timingSafeEqual } from 'crypto';
2
+
3
+ // src/core/api-promise.ts
4
+ var REQUEST_ID_HEADER = "x-request-id";
5
+ function requestIdOf(headers) {
6
+ const value = headers.get(REQUEST_ID_HEADER);
7
+ return value !== null && value.trim() !== "" ? value.trim() : void 0;
8
+ }
9
+ var APIPromise = class extends Promise {
10
+ #response;
11
+ #data;
12
+ constructor(response) {
13
+ super((resolve) => resolve(void 0));
14
+ this.#response = response;
15
+ }
16
+ // Derived promises (`.then(...)`) are plain Promises, not APIPromises.
17
+ static get [Symbol.species]() {
18
+ return Promise;
19
+ }
20
+ /** The body, the `Response` and the `X-Request-Id`. */
21
+ withResponse() {
22
+ return this.#response;
23
+ }
24
+ #body() {
25
+ this.#data ??= this.#response.then((result) => result.data);
26
+ return this.#data;
27
+ }
28
+ then(onfulfilled, onrejected) {
29
+ return this.#body().then(onfulfilled, onrejected);
30
+ }
31
+ catch(onrejected) {
32
+ return this.#body().catch(onrejected);
33
+ }
34
+ finally(onfinally) {
35
+ return this.#body().finally(onfinally);
36
+ }
37
+ };
38
+
39
+ // src/core/errors.ts
40
+ var FunnelchatError = class extends Error {
41
+ constructor(message, options) {
42
+ super(message, options);
43
+ this.name = new.target.name;
44
+ }
45
+ };
46
+ var APIError = class _APIError extends FunnelchatError {
47
+ status;
48
+ /** The `error` of the body. The known codes autocomplete; the API can send others. */
49
+ code;
50
+ retryAfter;
51
+ /** `X-Request-Id` of the response: the API sets it on every response, errors included. */
52
+ requestId;
53
+ /** Per-field validation messages of a `422`, when the API sends them. */
54
+ errors;
55
+ /** The parsed body (or the raw text when it is not JSON). */
56
+ body;
57
+ headers;
58
+ method;
59
+ /** URL of the request, without the query string. */
60
+ url;
61
+ constructor(init) {
62
+ const body = init.body;
63
+ const record = isRecord(body) ? body : void 0;
64
+ const code = typeof record?.error === "string" && record.error !== "" ? record.error : void 0;
65
+ const apiMessage = typeof record?.message === "string" && record.message !== "" ? record.message : void 0;
66
+ const fallback = typeof body === "string" && body.trim() !== "" && body.length <= 300 ? body.trim() : void 0;
67
+ super(apiMessage ?? code ?? fallback ?? `The API answered ${init.status}.`);
68
+ this.status = init.status;
69
+ this.code = code;
70
+ this.retryAfter = parseRetryAfter(init.headers.get("retry-after"));
71
+ this.requestId = requestIdOf(init.headers);
72
+ this.errors = isRecord(record?.errors) ? record.errors : void 0;
73
+ this.body = body;
74
+ this.headers = init.headers;
75
+ this.method = init.method;
76
+ this.url = init.url;
77
+ }
78
+ /** Builds the subclass that matches the status. */
79
+ static from(init) {
80
+ const { status } = init;
81
+ if (status === 400) return new BadRequestError(init);
82
+ if (status === 401) return new AuthenticationError(init);
83
+ if (status === 403) return new PermissionDeniedError(init);
84
+ if (status === 404) return new NotFoundError(init);
85
+ if (status === 409) return new ConflictError(init);
86
+ if (status === 415) return new UnsupportedMediaTypeError(init);
87
+ if (status === 422) return new UnprocessableEntityError(init);
88
+ if (status === 429) return new RateLimitError(init);
89
+ if (status >= 500) return new InternalServerError(init);
90
+ return new _APIError(init);
91
+ }
92
+ };
93
+ var BadRequestError = class extends APIError {
94
+ };
95
+ var AuthenticationError = class extends APIError {
96
+ };
97
+ var PermissionDeniedError = class extends APIError {
98
+ };
99
+ var NotFoundError = class extends APIError {
100
+ };
101
+ var ConflictError = class extends APIError {
102
+ };
103
+ var UnsupportedMediaTypeError = class extends APIError {
104
+ };
105
+ var UnprocessableEntityError = class extends APIError {
106
+ };
107
+ var RateLimitError = class extends APIError {
108
+ };
109
+ var InternalServerError = class extends APIError {
110
+ };
111
+ var APIConnectionError = class extends FunnelchatError {
112
+ constructor(message = "Could not connect to the Funnelchat API.", options) {
113
+ super(message, options);
114
+ }
115
+ };
116
+ var APIConnectionTimeoutError = class extends APIConnectionError {
117
+ constructor(timeoutMs, options) {
118
+ super(`The Funnelchat API did not answer within ${timeoutMs} ms.`, options);
119
+ }
120
+ };
121
+ var APIUserAbortError = class extends FunnelchatError {
122
+ constructor(options) {
123
+ super("The request was aborted.", options);
124
+ }
125
+ };
126
+ var WebhookVerificationError = class extends FunnelchatError {
127
+ reason;
128
+ constructor(reason, message) {
129
+ super(message);
130
+ this.reason = reason;
131
+ }
132
+ };
133
+ function parseRetryAfter(value, now = Date.now()) {
134
+ if (value === null || value === void 0) return void 0;
135
+ const trimmed = value.trim();
136
+ if (trimmed === "") return void 0;
137
+ if (/^\d+(\.\d+)?$/.test(trimmed)) return Number(trimmed);
138
+ const date = Date.parse(trimmed);
139
+ if (Number.isNaN(date)) return void 0;
140
+ return Math.max(0, Math.ceil((date - now) / 1e3));
141
+ }
142
+ function isRecord(value) {
143
+ return typeof value === "object" && value !== null && !Array.isArray(value);
144
+ }
145
+
146
+ // src/core/pagination.ts
147
+ var Page = class _Page {
148
+ /** Items of this page. */
149
+ data;
150
+ /** The full response body of this page (with `meta` and `links` when the route sends them). */
151
+ body;
152
+ /** `X-Request-Id` of the response of this page. */
153
+ requestId;
154
+ /** The HTTP response of this page. Its body was already read: use `body`. */
155
+ response;
156
+ #fetchPage;
157
+ #query;
158
+ constructor(fetchPage, query, result) {
159
+ this.#fetchPage = fetchPage;
160
+ this.#query = query;
161
+ this.body = result.data;
162
+ this.requestId = result.requestId;
163
+ this.response = result.response;
164
+ this.data = extractItems(result.data);
165
+ }
166
+ /**
167
+ * The query keys that ask for the next page (`{ page }` or `{ cursor }`), or `null` on the last
168
+ * page. An empty page is always the last one.
169
+ */
170
+ nextPageQuery() {
171
+ if (this.data.length === 0) return null;
172
+ const next = nextPageQuery(this.body);
173
+ if (next === null) return null;
174
+ const unchanged = Object.entries(next).every(([key, value]) => String(this.#query[key]) === String(value));
175
+ return unchanged ? null : next;
176
+ }
177
+ hasNextPage() {
178
+ return this.nextPageQuery() !== null;
179
+ }
180
+ /** Fetches the next page. Throws when there is none: check {@link hasNextPage} first. */
181
+ async getNextPage() {
182
+ const next = this.nextPageQuery();
183
+ if (next === null) throw new Error("There are no more pages.");
184
+ const query = { ...this.#query, ...next };
185
+ if ("cursor" in next) delete query.page;
186
+ return new _Page(this.#fetchPage, query, await this.#fetchPage(query));
187
+ }
188
+ /** Walks this page and the following ones, page by page. */
189
+ async *iterPages() {
190
+ yield this;
191
+ let next = this.hasNextPage() ? await this.getNextPage() : null;
192
+ while (next !== null) {
193
+ yield next;
194
+ next = next.hasNextPage() ? await next.getNextPage() : null;
195
+ }
196
+ }
197
+ /** Walks every item of this page and the following ones. */
198
+ async *[Symbol.asyncIterator]() {
199
+ for await (const page of this.iterPages()) yield* page.data;
200
+ }
201
+ };
202
+ var PagePromise = class {
203
+ #load;
204
+ #promise;
205
+ constructor(fetchPage, query) {
206
+ this.#load = async () => new Page(fetchPage, query, await fetchPage(query));
207
+ }
208
+ #page() {
209
+ this.#promise ??= this.#load();
210
+ return this.#promise;
211
+ }
212
+ then(onfulfilled, onrejected) {
213
+ return this.#page().then(onfulfilled, onrejected);
214
+ }
215
+ catch(onrejected) {
216
+ return this.#page().catch(onrejected);
217
+ }
218
+ async *[Symbol.asyncIterator]() {
219
+ yield* await this.#page();
220
+ }
221
+ };
222
+ function extractItems(body) {
223
+ if (Array.isArray(body)) return body;
224
+ if (isRecord2(body) && Array.isArray(body.data)) return body.data;
225
+ return [];
226
+ }
227
+ function toInt(value) {
228
+ if (typeof value === "number" && Number.isInteger(value)) return value;
229
+ if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
230
+ return null;
231
+ }
232
+ function nextPageQuery(body) {
233
+ if (!isRecord2(body)) return null;
234
+ for (const holder of [body.meta, body]) {
235
+ if (!isRecord2(holder)) continue;
236
+ if ("next_cursor" in holder) {
237
+ return typeof holder.next_cursor === "string" && holder.next_cursor !== "" ? { cursor: holder.next_cursor } : null;
238
+ }
239
+ const current = toInt(holder.current_page);
240
+ const last = toInt(holder.last_page);
241
+ if (current !== null && last !== null) return current < last ? { page: current + 1 } : null;
242
+ }
243
+ const links = isRecord2(body.links) ? body.links : void 0;
244
+ const nextUrl = links?.next ?? body.next_page_url;
245
+ if (typeof nextUrl === "string" && nextUrl !== "") {
246
+ const params = new URL(nextUrl, "https://placeholder.invalid").searchParams;
247
+ const cursor = params.get("cursor");
248
+ if (cursor) return { cursor };
249
+ const page = toInt(params.get("page"));
250
+ if (page !== null) return { page };
251
+ }
252
+ return null;
253
+ }
254
+ function isRecord2(value) {
255
+ return typeof value === "object" && value !== null && !Array.isArray(value);
256
+ }
257
+
258
+ // src/core/error-codes.ts
259
+ var ACCESS_ERROR_CODES = {
260
+ /** 401: missing or invalid API key. */
261
+ unauthenticated: 401,
262
+ /** 403: the key lacks the permission the route needs (`message` names it). */
263
+ permission_denied: 403,
264
+ /** 403: the route is not open to API keys (or is turned off). */
265
+ route_not_available: 403,
266
+ /** 404: the path does not exist in the public API. Answered by the entry, before any area. */
267
+ route_not_found: 404,
268
+ /** 429, with `Retry-After`: the daily call cap of the key in that area. */
269
+ daily_cap_exceeded: 429,
270
+ /** 500: the entry is not configured to reach the area. Our problem: report it with the `X-Request-Id`. */
271
+ edge_misconfigured: 500,
272
+ /**
273
+ * 502: the entry could not reach the area. There is no guarantee the area did not get the call:
274
+ * after a write, read the state before repeating it.
275
+ */
276
+ origin_unreachable: 502,
277
+ /** 503, with `Retry-After`: the gate could not evaluate the call and let nothing through. Safe to retry. */
278
+ gate_unavailable: 503
279
+ };
280
+ var SEND_ERROR_CODES = {
281
+ /** 429, with `Retry-After`: the number's pace (per second, or per minute to the same contact). */
282
+ rate_limited: 429,
283
+ /** 429: the account is in the 24-hour lockout after using up its monthly active contacts (MAC). Not worth retrying. */
284
+ mac_lockout_24h: 429,
285
+ /** 429: the account used up its monthly active contacts (MAC). Not worth retrying. */
286
+ mac_limit_reached: 429,
287
+ /** 422: free text outside the 24-hour window on a WhatsApp Cloud number. Send a template. */
288
+ window_closed: 422,
289
+ /** 422: the same content sent too many times to a contact that did not answer. */
290
+ antiblock_duplicate_content: 422,
291
+ /** 422: too many messages in a row to a contact that did not answer. */
292
+ antiblock_unanswered_limit: 422,
293
+ /** 422: the `phone` / `whatsapp_id` of the body is not the contact of the route. */
294
+ recipient_mismatch: 422,
295
+ /** 422: the contact is a group, a broadcast list or a channel. */
296
+ recipient_unsupported: 422,
297
+ /** 422: the number cannot send this (see `capabilities` of the number). */
298
+ provider_unsupported: 422,
299
+ /** 422: the number is not connected or cannot send now. */
300
+ device_unavailable: 422,
301
+ /** 422: the message could not be registered for sending. */
302
+ not_sent: 422,
303
+ /** 422: the contact cannot be reached on WhatsApp (forms). */
304
+ recipient_unreachable: 422,
305
+ /** 422: a template variable points to a field that does not exist (`placeholders`, `count`). */
306
+ template_unresolved_placeholder: 422,
307
+ /** 404: the template is not one this account can send. */
308
+ template_not_found: 404,
309
+ /** 404: the form does not exist. */
310
+ form_not_found: 404,
311
+ /** 422: the form is not published. */
312
+ form_not_published: 422,
313
+ /** 422: the form is not published on that number. */
314
+ form_not_on_number: 422,
315
+ /** 422: the number cannot send forms. */
316
+ number_not_eligible: 422
317
+ };
318
+ var has = (table, code) => code !== void 0 && Object.prototype.hasOwnProperty.call(table, code);
319
+ function isSendRuleError(code) {
320
+ return has(SEND_ERROR_CODES, code);
321
+ }
322
+ var NOT_RETRYABLE_CODES = /* @__PURE__ */ new Set(["mac_lockout_24h", "mac_limit_reached"]);
323
+
324
+ // src/generated/routes.ts
325
+ var routes = [
326
+ { "method": "GET", "path": "/api/v1/agents", "operationId": "user.index", "domain": "conversations", "permission": "conversations:read", "tag": "User", "summary": "Listar agentes", "pending": false },
327
+ { "method": "GET", "path": "/api/v1/agents/{user}", "operationId": "user.show", "domain": "conversations", "permission": "conversations:read", "tag": "User", "summary": "Ver un agente", "pending": false },
328
+ { "method": "GET", "path": "/api/v1/alerts/campaigns", "operationId": "alertCampaign.index", "domain": "accounts", "permission": "accounts:read", "tag": "AlertCampaign", "summary": "Listar las campa\xF1as para alertas", "pending": false },
329
+ { "method": "GET", "path": "/api/v1/alerts/catalog", "operationId": "alertCatalog.index", "domain": "accounts", "permission": "accounts:read", "tag": "AlertCatalog", "summary": "Ver el cat\xE1logo de alertas", "pending": false },
330
+ { "method": "GET", "path": "/api/v1/alerts/daily-report", "operationId": "alertDailyReport.show", "domain": "accounts", "permission": "accounts:read", "tag": "AlertDailyReport", "summary": "Ver el reporte diario", "pending": false },
331
+ { "method": "PUT", "path": "/api/v1/alerts/daily-report", "operationId": "alertDailyReport.update", "domain": "accounts", "permission": "accounts:write", "tag": "AlertDailyReport", "summary": "Configurar el reporte diario", "pending": false },
332
+ { "method": "GET", "path": "/api/v1/alerts/destinations", "operationId": "alertDestination.index", "domain": "accounts", "permission": "accounts:read", "tag": "AlertDestination", "summary": "Listar los destinos de alertas", "pending": false },
333
+ { "method": "POST", "path": "/api/v1/alerts/destinations", "operationId": "alertDestination.store", "domain": "accounts", "permission": "accounts:write", "tag": "AlertDestination", "summary": "Crear un destino de alertas", "pending": false },
334
+ { "method": "DELETE", "path": "/api/v1/alerts/destinations/{id}", "operationId": "alertDestination.destroy", "domain": "accounts", "permission": "accounts:write", "tag": "AlertDestination", "summary": "Eliminar un destino de alertas", "pending": false },
335
+ { "method": "PATCH", "path": "/api/v1/alerts/destinations/{id}", "operationId": "alertDestination.update", "domain": "accounts", "permission": "accounts:write", "tag": "AlertDestination", "summary": "Actualizar un destino de alertas", "pending": false },
336
+ { "method": "POST", "path": "/api/v1/alerts/destinations/{id}/otp", "operationId": "alertOtp.send", "domain": "accounts", "permission": "accounts:write", "tag": "AlertOtp", "summary": "Enviar un c\xF3digo de verificaci\xF3n", "pending": false },
337
+ { "method": "POST", "path": "/api/v1/alerts/destinations/{id}/otp/verify", "operationId": "alertOtp.verify", "domain": "accounts", "permission": "accounts:write", "tag": "AlertOtp", "summary": "Verificar el c\xF3digo", "pending": false },
338
+ { "method": "PUT", "path": "/api/v1/alerts/destinations/{id}/settings", "operationId": "alertSetting.upsert", "domain": "accounts", "permission": "accounts:write", "tag": "AlertSetting", "summary": "Activar o desactivar una alerta", "pending": false },
339
+ { "method": "POST", "path": "/api/v1/alerts/destinations/{id}/test", "operationId": "alertWebhookTest.test", "domain": "accounts", "permission": "accounts:write", "tag": "AlertWebhookTest", "summary": "Probar un destino webhook", "pending": false },
340
+ { "method": "GET", "path": "/api/v1/alerts/quiet-hours", "operationId": "alertQuietHour.show", "domain": "accounts", "permission": "accounts:read", "tag": "AlertQuietHour", "summary": "Ver el horario de silencio", "pending": false },
341
+ { "method": "PUT", "path": "/api/v1/alerts/quiet-hours", "operationId": "alertQuietHour.update", "domain": "accounts", "permission": "accounts:write", "tag": "AlertQuietHour", "summary": "Configurar el horario de silencio", "pending": false },
342
+ { "method": "GET", "path": "/api/v1/broadcasts", "operationId": "broadcast.index", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Listar difusiones", "pending": false },
343
+ { "method": "DELETE", "path": "/api/v1/broadcasts/{broadcast}", "operationId": "broadcast.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Eliminar una difusi\xF3n", "pending": false },
344
+ { "method": "GET", "path": "/api/v1/broadcasts/{broadcast}", "operationId": "broadcast.show", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Ver una difusi\xF3n", "pending": false },
345
+ { "method": "POST", "path": "/api/v1/broadcasts/{broadcast}/cancel", "operationId": "broadcast.cancel", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Cancelar una difusi\xF3n", "pending": false },
346
+ { "method": "GET", "path": "/api/v1/broadcasts/{broadcast}/contacts", "operationId": "broadcast.broadcastContacts", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Listar los contactos de una difusi\xF3n", "pending": false },
347
+ { "method": "GET", "path": "/api/v1/broadcasts/{broadcast}/cost", "operationId": "broadcast.cost", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Ver el costo de una difusi\xF3n", "pending": false },
348
+ { "method": "GET", "path": "/api/v1/broadcasts/{broadcast}/messages", "operationId": "broadcast.broadcastMessages", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Listar los mensajes de una difusi\xF3n", "pending": false },
349
+ { "method": "GET", "path": "/api/v1/broadcasts/{broadcast}/statistics", "operationId": "broadcast.statistics", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Ver las estad\xEDsticas de una difusi\xF3n", "pending": false },
350
+ { "method": "POST", "path": "/api/v1/broadcasts/contacts", "operationId": "broadcast.contacts", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Buscar contactos para una difusi\xF3n", "pending": false },
351
+ { "method": "POST", "path": "/api/v1/broadcasts/contacts-count", "operationId": "broadcast.contactsCount", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Contar contactos para una difusi\xF3n", "pending": false },
352
+ { "method": "GET", "path": "/api/v1/broadcasts/cost-tiers", "operationId": "broadcast.costTiers", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Ver los tramos de costo", "pending": false },
353
+ { "method": "GET", "path": "/api/v1/broadcasts/folders", "operationId": "broadcastFolder.index", "domain": "conversations", "permission": "conversations:read", "tag": "Broadcast", "summary": "Listar carpetas de difusiones", "pending": false },
354
+ { "method": "POST", "path": "/api/v1/broadcasts/folders", "operationId": "broadcastFolder.store", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Crear una carpeta de difusiones", "pending": false },
355
+ { "method": "DELETE", "path": "/api/v1/broadcasts/folders/{broadcastFolder}", "operationId": "broadcastFolder.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Eliminar una carpeta de difusiones", "pending": false },
356
+ { "method": "PUT", "path": "/api/v1/broadcasts/folders/{broadcastFolder}", "operationId": "broadcastFolder.update", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Actualizar una carpeta de difusiones", "pending": false },
357
+ { "method": "POST", "path": "/api/v1/broadcasts/move-to-folder", "operationId": "broadcast.moveToFolder", "domain": "conversations", "permission": "conversations:write", "tag": "Broadcast", "summary": "Mover difusiones a una carpeta", "pending": false },
358
+ { "method": "GET", "path": "/api/v1/campaigns", "operationId": "campaign.index", "domain": "communities", "permission": "communities:read", "tag": "Campa\xF1as", "summary": "Listar campa\xF1as", "pending": false },
359
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}", "operationId": "campaign.show", "domain": "communities", "permission": "communities:read", "tag": "Campa\xF1as", "summary": "Ver una campa\xF1a", "pending": false },
360
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/actions", "operationId": "campaign.actions", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar las acciones de sesi\xF3n de una campa\xF1a", "pending": false },
361
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/actions/{action}/groups", "operationId": "groupManagement.actionGroup", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar los grupos alcanzados por una acci\xF3n", "pending": false },
362
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/available-groups", "operationId": "campaign.availableGroups", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar los grupos que se pueden sumar a una campa\xF1a", "pending": false },
363
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/campaign-participants", "operationId": "campaign.campaignParticipants", "domain": "communities", "permission": "communities:read", "tag": "Participantes", "summary": "Listar los participantes con su grupo actual", "pending": false },
364
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/conditionals", "operationId": "campaignConditional.index", "domain": "communities", "permission": "communities:read", "tag": "Campa\xF1as", "summary": "Ver las condicionales de una campa\xF1a", "pending": false },
365
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/groups", "operationId": "campaign.groups", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar los grupos de una campa\xF1a", "pending": false },
366
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/operation-plans", "operationId": "operationPlan.index", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar los planes de operaci\xF3n de una campa\xF1a", "pending": false },
367
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/operation-plans/{plan}", "operationId": "operationPlan.show", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Ver un plan de operaci\xF3n", "pending": false },
368
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/operation-plans/config", "operationId": "operationPlan.config", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Ver la configuraci\xF3n de los planes de operaci\xF3n", "pending": false },
369
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/participant-events", "operationId": "campaign.participantEvents", "domain": "communities", "permission": "communities:read", "tag": "Participantes", "summary": "Listar entradas y salidas de una campa\xF1a", "pending": false },
370
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/participants", "operationId": "campaign.participants", "domain": "communities", "permission": "communities:read", "tag": "Participantes", "summary": "Listar los participantes de una campa\xF1a", "pending": false },
371
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/pipelines", "operationId": "campaign.indexPipelines", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Listar los pipelines de una campa\xF1a", "pending": false },
372
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/pipelines/{pipeline}", "operationId": "campaign.showPipeline", "domain": "communities", "permission": "communities:read", "tag": "Grupos de una campa\xF1a y operaciones", "summary": "Ver un pipeline", "pending": false },
373
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/webhooks", "operationId": "campaignWebhook.index", "domain": "communities", "permission": "communities:read", "tag": "Webhooks de campa\xF1a (legado)", "summary": "Listar los webhooks de una campa\xF1a", "pending": false },
374
+ { "method": "GET", "path": "/api/v1/campaigns/{campaign}/webhooks/logs", "operationId": "campaignWebhook.logs", "domain": "communities", "permission": "communities:read", "tag": "Webhooks de campa\xF1a (legado)", "summary": "Listar las entregas de los webhooks de una campa\xF1a", "pending": false },
375
+ { "method": "PUT", "path": "/api/v1/contact-flows/{contactFlowId}/cancel", "operationId": "flow.cancelContactFlow", "domain": "conversations", "permission": "conversations:write", "tag": "Flow", "summary": "Cancelar un flujo en curso", "pending": false },
376
+ { "method": "GET", "path": "/api/v1/contact-flows/{contactId}", "operationId": "flow.contactFlowHistory", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Listar los flujos en curso de un contacto", "pending": false },
377
+ { "method": "GET", "path": "/api/v1/contacts", "operationId": "contact.index", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Listar contactos", "pending": false },
378
+ { "method": "POST", "path": "/api/v1/contacts", "operationId": "contact.store", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Crear un contacto", "pending": false },
379
+ { "method": "GET", "path": "/api/v1/contacts/{contact}", "operationId": "contact.show", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Ver un contacto", "pending": false },
380
+ { "method": "PUT", "path": "/api/v1/contacts/{contact}", "operationId": "contact.update", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Actualizar un contacto", "pending": false },
381
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/custom-fields", "operationId": "contact.customFields", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Listar los campos personalizados de un contacto", "pending": false },
382
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/flows", "operationId": "contactAutomation.flows", "domain": "conversations", "permission": "conversations:read", "tag": "ContactAutomation", "summary": "Listar los flujos de un contacto", "pending": false },
383
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/kanban-stages", "operationId": "kanbanStage.contactStages", "domain": "conversations", "permission": "conversations:read", "tag": "KanbanStage", "summary": "Ver las etapas de un contacto en los tableros", "pending": false },
384
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/lead-score", "operationId": "contactLeadScore.show", "domain": "conversations", "permission": "conversations:read", "tag": "ContactLeadScore", "summary": "Ver el lead score de un contacto", "pending": false },
385
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/lead-score/adjustments", "operationId": "leadScoreSettings.adjust", "domain": "conversations", "permission": "conversations:write", "tag": "LeadScoreSettings", "summary": "Ajustar el lead score de un contacto", "pending": false },
386
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/message", "operationId": "conversation.sendMessage", "domain": "conversations", "permission": "conversations:send", "tag": "Conversation", "summary": "Enviar un mensaje a un contacto", "pending": true },
387
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/message/contact-card", "operationId": "contactCard.store", "domain": "conversations", "permission": "conversations:send", "tag": "ContactCard", "summary": "Enviar una tarjeta de contacto", "pending": true },
388
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/message/event", "operationId": "interactiveMessage.storeEvent", "domain": "conversations", "permission": "conversations:send", "tag": "InteractiveMessage", "summary": "Enviar una invitaci\xF3n a un evento", "pending": false },
389
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/message/form", "operationId": "pendingForms.send", "domain": "conversations", "permission": "conversations:send", "tag": "Message", "summary": "Send a form to a contact", "pending": true },
390
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/message/poll", "operationId": "interactiveMessage.storePoll", "domain": "conversations", "permission": "conversations:send", "tag": "InteractiveMessage", "summary": "Enviar una encuesta", "pending": false },
391
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/messages/{message}", "operationId": "pendingMessage.show", "domain": "conversations", "permission": "conversations:read", "tag": "Message", "summary": "Read a message by id", "pending": true },
392
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/messages/{message}/forward", "operationId": "forwardMessage.store", "domain": "conversations", "permission": "conversations:send", "tag": "ForwardMessage", "summary": "Reenviar un mensaje", "pending": true },
393
+ { "method": "DELETE", "path": "/api/v1/contacts/{contact}/messages/{message}/reaction", "operationId": "messageReaction.destroy", "domain": "conversations", "permission": "conversations:send", "tag": "MessageReaction", "summary": "Quitar una reacci\xF3n", "pending": false },
394
+ { "method": "PUT", "path": "/api/v1/contacts/{contact}/messages/{message}/reaction", "operationId": "messageReaction.update", "domain": "conversations", "permission": "conversations:send", "tag": "MessageReaction", "summary": "Reaccionar a un mensaje", "pending": false },
395
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/meta-window", "operationId": "contact.metaWindow", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Ver la ventana de 24 horas de un contacto", "pending": false },
396
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/notes", "operationId": "contactNote.index", "domain": "conversations", "permission": "conversations:read", "tag": "ContactNote", "summary": "Listar las notas de un contacto", "pending": false },
397
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/notes", "operationId": "contactNote.store", "domain": "conversations", "permission": "conversations:write", "tag": "ContactNote", "summary": "Agregar una nota a un contacto", "pending": false },
398
+ { "method": "DELETE", "path": "/api/v1/contacts/{contact}/notes/{note}", "operationId": "contactNote.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "ContactNote", "summary": "Eliminar una nota", "pending": false },
399
+ { "method": "POST", "path": "/api/v1/contacts/{contact}/pause", "operationId": "contactAutomation.pause", "domain": "conversations", "permission": "conversations:write", "tag": "ContactAutomation", "summary": "Pausar un contacto", "pending": false },
400
+ { "method": "GET", "path": "/api/v1/contacts/{contact}/tags", "operationId": "contact.tags", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Listar las etiquetas de un contacto", "pending": false },
401
+ { "method": "PUT", "path": "/api/v1/contacts/{contact}/update", "operationId": "conversation.update", "domain": "conversations", "permission": "conversations:write", "tag": "Conversation", "summary": "Actualizar la conversaci\xF3n de un contacto", "pending": false },
402
+ { "method": "GET", "path": "/api/v1/contacts/{contactId}/messages", "operationId": "conversation.getLatestMessages", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Listar los mensajes de un contacto", "pending": false },
403
+ { "method": "POST", "path": "/api/v1/contacts/attach-custom-field", "operationId": "contact.attachCustomField", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Asignar un campo personalizado a contactos", "pending": false },
404
+ { "method": "POST", "path": "/api/v1/contacts/delete", "operationId": "contact.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Eliminar contactos", "pending": false },
405
+ { "method": "POST", "path": "/api/v1/contacts/detach-custom-field", "operationId": "contact.detachCustomField", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Quitar un campo personalizado de contactos", "pending": false },
406
+ { "method": "POST", "path": "/api/v1/contacts/update-or-create", "operationId": "contact.updateOrCreate", "domain": "conversations", "permission": "conversations:write", "tag": "Contact", "summary": "Crear o actualizar un contacto", "pending": false },
407
+ { "method": "GET", "path": "/api/v1/conversations", "operationId": "conversation.index", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Listar conversaciones", "pending": false },
408
+ { "method": "GET", "path": "/api/v1/conversations/count/all", "operationId": "conversation.countAll", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Contar todas las conversaciones", "pending": false },
409
+ { "method": "GET", "path": "/api/v1/conversations/count/closed", "operationId": "conversation.countClosed", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Contar conversaciones cerradas", "pending": false },
410
+ { "method": "GET", "path": "/api/v1/conversations/count/open", "operationId": "conversation.countOpen", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Contar conversaciones abiertas", "pending": false },
411
+ { "method": "GET", "path": "/api/v1/conversations/count/unread", "operationId": "conversation.countUnread", "domain": "conversations", "permission": "conversations:read", "tag": "Conversation", "summary": "Contar conversaciones sin leer", "pending": false },
412
+ { "method": "POST", "path": "/api/v1/conversations/mark-all-read", "operationId": "conversation.markAllAsRead", "domain": "conversations", "permission": "conversations:write", "tag": "Conversation", "summary": "Marcar todas como le\xEDdas", "pending": false },
413
+ { "method": "POST", "path": "/api/v1/conversations/mark-all-unread", "operationId": "conversation.markAllAsUnread", "domain": "conversations", "permission": "conversations:write", "tag": "Conversation", "summary": "Marcar todas como no le\xEDdas", "pending": false },
414
+ { "method": "GET", "path": "/api/v1/custom-domains", "operationId": "customDomain.index", "domain": "accounts", "permission": "accounts:read", "tag": "CustomDomain", "summary": "Listar dominios propios", "pending": false },
415
+ { "method": "POST", "path": "/api/v1/custom-domains", "operationId": "customDomain.store", "domain": "accounts", "permission": "accounts:write", "tag": "CustomDomain", "summary": "Registrar un dominio propio", "pending": false },
416
+ { "method": "DELETE", "path": "/api/v1/custom-domains/{customDomain}", "operationId": "customDomain.destroy", "domain": "accounts", "permission": "accounts:operate", "tag": "CustomDomain", "summary": "Eliminar un dominio propio", "pending": false },
417
+ { "method": "POST", "path": "/api/v1/custom-domains/{customDomain}/switch-validation", "operationId": "customDomain.switchValidationMethod", "domain": "accounts", "permission": "accounts:write", "tag": "CustomDomain", "summary": "Cambiar el m\xE9todo de validaci\xF3n SSL", "pending": false },
418
+ { "method": "POST", "path": "/api/v1/custom-domains/{customDomain}/verify", "operationId": "customDomain.verify", "domain": "accounts", "permission": "accounts:write", "tag": "CustomDomain", "summary": "Verificar un dominio propio", "pending": false },
419
+ { "method": "GET", "path": "/api/v1/custom-fields", "operationId": "customField.index", "domain": "conversations", "permission": "conversations:read", "tag": "CustomField", "summary": "Listar campos personalizados", "pending": false },
420
+ { "method": "POST", "path": "/api/v1/custom-fields", "operationId": "customField.store", "domain": "conversations", "permission": "conversations:write", "tag": "CustomField", "summary": "Crear un campo personalizado", "pending": false },
421
+ { "method": "GET", "path": "/api/v1/custom-fields/{customField}", "operationId": "customField.show", "domain": "conversations", "permission": "conversations:read", "tag": "CustomField", "summary": "Ver un campo personalizado", "pending": false },
422
+ { "method": "PUT", "path": "/api/v1/custom-fields/{customField}", "operationId": "customField.update", "domain": "conversations", "permission": "conversations:write", "tag": "CustomField", "summary": "Actualizar un campo personalizado", "pending": false },
423
+ { "method": "POST", "path": "/api/v1/custom-fields/delete", "operationId": "customField.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "CustomField", "summary": "Eliminar campos personalizados", "pending": false },
424
+ { "method": "GET", "path": "/api/v1/devices", "operationId": "device.index", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Listar n\xFAmeros", "pending": false },
425
+ { "method": "DELETE", "path": "/api/v1/devices/{device}", "operationId": "device.destroy", "domain": "accounts", "permission": "accounts:operate", "tag": "Device", "summary": "Eliminar un n\xFAmero", "pending": false },
426
+ { "method": "GET", "path": "/api/v1/devices/{device}", "operationId": "device.show", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Ver un n\xFAmero", "pending": false },
427
+ { "method": "PUT", "path": "/api/v1/devices/{device}", "operationId": "device.update", "domain": "accounts", "permission": "accounts:write", "tag": "Device", "summary": "Actualizar un n\xFAmero", "pending": false },
428
+ { "method": "GET", "path": "/api/v1/devices/{device}/ad-groups", "operationId": "group.adGroups", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar los grupos de anuncios de un n\xFAmero", "pending": false },
429
+ { "method": "POST", "path": "/api/v1/devices/{device}/capi/dataset", "operationId": "capiConnection.dataset", "domain": "accounts", "permission": "accounts:write", "tag": "CapiConnection", "summary": "Configurar el dataset de Conversions API", "pending": false },
430
+ { "method": "GET", "path": "/api/v1/devices/{device}/capi/test", "operationId": "capiConnection.test", "domain": "accounts", "permission": "accounts:read", "tag": "CapiConnection", "summary": "Probar la conexi\xF3n con Conversions API", "pending": false },
431
+ { "method": "GET", "path": "/api/v1/devices/{device}/channels", "operationId": "group.channels", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar los canales de un n\xFAmero", "pending": false },
432
+ { "method": "GET", "path": "/api/v1/devices/{device}/chats", "operationId": "device.chats", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Listar los chats de un n\xFAmero", "pending": false },
433
+ { "method": "GET", "path": "/api/v1/devices/{device}/check-phone", "operationId": "device.checkPhone", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Verificar si un tel\xE9fono tiene WhatsApp", "pending": false },
434
+ { "method": "POST", "path": "/api/v1/devices/{device}/clean", "operationId": "device.clean", "domain": "accounts", "permission": "accounts:operate", "tag": "Device", "summary": "Limpiar un n\xFAmero", "pending": false },
435
+ { "method": "GET", "path": "/api/v1/devices/{device}/connection-history", "operationId": "deviceConnectionEvent.index", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceConnectionEvent", "summary": "Ver el historial de conexi\xF3n", "pending": false },
436
+ { "method": "GET", "path": "/api/v1/devices/{device}/contacts", "operationId": "contact.contactsByDevice", "domain": "conversations", "permission": "conversations:read", "tag": "Contact", "summary": "Listar los contactos de un n\xFAmero", "pending": false },
437
+ { "method": "GET", "path": "/api/v1/devices/{device}/disconnect-survey", "operationId": "deviceDisconnectSurvey.show", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceDisconnectSurvey", "summary": "Ver la encuesta de desconexi\xF3n", "pending": false },
438
+ { "method": "POST", "path": "/api/v1/devices/{device}/disconnect-survey", "operationId": "deviceDisconnectSurvey.store", "domain": "accounts", "permission": "accounts:write", "tag": "DeviceDisconnectSurvey", "summary": "Responder la encuesta de desconexi\xF3n", "pending": false },
439
+ { "method": "POST", "path": "/api/v1/devices/{device}/extension-token", "operationId": "device.extensionToken", "domain": "accounts", "permission": "accounts:write", "tag": "Device", "summary": "Pedir un token para la extensi\xF3n de WhatsApp Web", "pending": false },
440
+ { "method": "GET", "path": "/api/v1/devices/{device}/groups", "operationId": "group.groups", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar los grupos que administra un n\xFAmero", "pending": false },
441
+ { "method": "GET", "path": "/api/v1/devices/{device}/health", "operationId": "deviceHealth.show", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceHealth", "summary": "Ver la salud de un n\xFAmero", "pending": false },
442
+ { "method": "POST", "path": "/api/v1/devices/{device}/health/refresh", "operationId": "deviceHealth.refresh", "domain": "accounts", "permission": "accounts:write", "tag": "DeviceHealth", "summary": "Actualizar la salud de un n\xFAmero", "pending": false },
443
+ { "method": "GET", "path": "/api/v1/devices/{device}/logs", "operationId": "deviceLogs.index", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceLogs", "summary": "Listar los registros de un n\xFAmero", "pending": false },
444
+ { "method": "GET", "path": "/api/v1/devices/{device}/logs/health", "operationId": "deviceLogs.health", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceLogs", "summary": "Ver la se\xF1al de salud de los registros", "pending": false },
445
+ { "method": "GET", "path": "/api/v1/devices/{device}/me", "operationId": "device.me", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Ver el perfil de WhatsApp de un n\xFAmero", "pending": false },
446
+ { "method": "DELETE", "path": "/api/v1/devices/{device}/message-queue", "operationId": "device.deleteMessageQueue", "domain": "accounts", "permission": "accounts:write", "tag": "Device", "summary": "Eliminar mensajes de la cola", "pending": false },
447
+ { "method": "GET", "path": "/api/v1/devices/{device}/message-queue", "operationId": "device.messageQueue", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Ver la cola de mensajes", "pending": false },
448
+ { "method": "DELETE", "path": "/api/v1/devices/{device}/message-queue/all", "operationId": "device.clearQueue", "domain": "accounts", "permission": "accounts:operate", "tag": "Device", "summary": "Vaciar la cola de mensajes", "pending": false },
449
+ { "method": "GET", "path": "/api/v1/devices/{device}/message-queue/count", "operationId": "device.getQueueCount", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Contar los mensajes en la cola", "pending": false },
450
+ { "method": "GET", "path": "/api/v1/devices/{device}/open-conversations", "operationId": "device.openConversations", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Ver qu\xE9 tel\xE9fonos tienen conversaci\xF3n abierta", "pending": false },
451
+ { "method": "PUT", "path": "/api/v1/devices/{device}/reset", "operationId": "device.reset", "domain": "accounts", "permission": "accounts:operate", "tag": "Device", "summary": "Reiniciar la sesi\xF3n de un n\xFAmero", "pending": false },
452
+ { "method": "GET", "path": "/api/v1/devices/{device}/status", "operationId": "device.status", "domain": "accounts", "permission": "accounts:read", "tag": "Device", "summary": "Ver el estado de conexi\xF3n", "pending": false },
453
+ { "method": "DELETE", "path": "/api/v1/devices/{device}/templates/{template}", "operationId": "messageTemplate.delete", "domain": "conversations", "permission": "conversations:write", "tag": "MessageTemplate", "summary": "Eliminar una plantilla de un n\xFAmero", "pending": false },
454
+ { "method": "POST", "path": "/api/v1/devices/{device}/templates/{template}/send-template", "operationId": "messageTemplate.send", "domain": "conversations", "permission": "conversations:send", "tag": "MessageTemplate", "summary": "Enviar una plantilla", "pending": true },
455
+ { "method": "PUT", "path": "/api/v1/devices/{device}/update-webhook-received", "operationId": "device.updateWebhookReceived", "domain": "accounts", "permission": "accounts:write", "tag": "Device", "summary": "Configurar el webhook de mensajes recibidos", "pending": false },
456
+ { "method": "PUT", "path": "/api/v1/devices/{device}/update-webhook-received-and-delivery", "operationId": "device.updateWebhookReceivedAndDelivery", "domain": "accounts", "permission": "accounts:write", "tag": "Device", "summary": "Configurar el webhook de recibidos y entregas", "pending": false },
457
+ { "method": "GET", "path": "/api/v1/devices/{device}/username", "operationId": "deviceUsername.show", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceUsername", "summary": "Ver el nombre de usuario", "pending": false },
458
+ { "method": "POST", "path": "/api/v1/devices/{device}/username", "operationId": "deviceUsername.store", "domain": "accounts", "permission": "accounts:write", "tag": "DeviceUsername", "summary": "Configurar el nombre de usuario", "pending": false },
459
+ { "method": "GET", "path": "/api/v1/devices/{device}/username/suggestions", "operationId": "deviceUsername.suggestions", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceUsername", "summary": "Sugerir nombres de usuario", "pending": false },
460
+ { "method": "GET", "path": "/api/v1/devices/capi/datasets", "operationId": "capiConnection.datasets", "domain": "accounts", "permission": "accounts:read", "tag": "CapiConnection", "summary": "Listar los datasets de Conversions API", "pending": false },
461
+ { "method": "GET", "path": "/api/v1/devices/logs", "operationId": "deviceFleetLogs.index", "domain": "accounts", "permission": "accounts:read", "tag": "DeviceFleetLogs", "summary": "Listar los registros de todos los n\xFAmeros", "pending": false },
462
+ { "method": "GET", "path": "/api/v1/flows", "operationId": "flow.index", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Listar flujos", "pending": false },
463
+ { "method": "GET", "path": "/api/v1/flows/{flow}", "operationId": "flow.show", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Ver un flujo", "pending": false },
464
+ { "method": "GET", "path": "/api/v1/flows/{flow}/active-contacts", "operationId": "flow.activeContacts", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Contar los contactos activos de un flujo", "pending": false },
465
+ { "method": "GET", "path": "/api/v1/flows/{flow}/active-contacts-list", "operationId": "flow.activeContactsList", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Listar los contactos activos de un flujo", "pending": false },
466
+ { "method": "GET", "path": "/api/v1/flows/{flow}/flow-contacts", "operationId": "flow.flowContactsList", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Listar los contactos de un flujo", "pending": false },
467
+ { "method": "GET", "path": "/api/v1/flows/{flow}/nodes", "operationId": "flow.nodes", "domain": "conversations", "permission": "conversations:read", "tag": "Flow", "summary": "Listar los nodos de un flujo", "pending": false },
468
+ { "method": "GET", "path": "/api/v1/folders", "operationId": "folder.index", "domain": "conversations", "permission": "conversations:read", "tag": "Folder", "summary": "Listar carpetas", "pending": false },
469
+ { "method": "POST", "path": "/api/v1/folders", "operationId": "folder.store", "domain": "conversations", "permission": "conversations:write", "tag": "Folder", "summary": "Crear una carpeta", "pending": false },
470
+ { "method": "DELETE", "path": "/api/v1/folders/{folder}", "operationId": "folder.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "Folder", "summary": "Eliminar una carpeta", "pending": false },
471
+ { "method": "GET", "path": "/api/v1/folders/{folder}", "operationId": "folder.show", "domain": "conversations", "permission": "conversations:read", "tag": "Folder", "summary": "Ver una carpeta", "pending": false },
472
+ { "method": "PUT", "path": "/api/v1/folders/{folder}", "operationId": "folder.update", "domain": "conversations", "permission": "conversations:write", "tag": "Folder", "summary": "Actualizar una carpeta", "pending": false },
473
+ { "method": "GET", "path": "/api/v1/forms", "operationId": "pendingForms.list", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "List forms", "pending": true },
474
+ { "method": "POST", "path": "/api/v1/forms", "operationId": "pendingForms.create", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Create a form from a template", "pending": true },
475
+ { "method": "DELETE", "path": "/api/v1/forms/{id}", "operationId": "pendingForms.delete", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Delete a draft", "pending": true },
476
+ { "method": "GET", "path": "/api/v1/forms/{id}", "operationId": "pendingForms.retrieve", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "One version of a form", "pending": true },
477
+ { "method": "PUT", "path": "/api/v1/forms/{id}", "operationId": "pendingForms.update", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Save the definition of a draft", "pending": true },
478
+ { "method": "GET", "path": "/api/v1/forms/{id}/numbers", "operationId": "pendingForms.numbers", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "The numbers of the form", "pending": true },
479
+ { "method": "POST", "path": "/api/v1/forms/{id}/numbers", "operationId": "pendingForms.addNumber", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Publish it on another number too", "pending": true },
480
+ { "method": "DELETE", "path": "/api/v1/forms/{id}/numbers/{deviceId}", "operationId": "pendingForms.removeNumber", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Take it off a number", "pending": true },
481
+ { "method": "POST", "path": "/api/v1/forms/{id}/numbers/retry", "operationId": "pendingForms.retryNumbers", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Retry the numbers that failed", "pending": true },
482
+ { "method": "POST", "path": "/api/v1/forms/{id}/publish", "operationId": "pendingForms.publish", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Publish on Meta", "pending": true },
483
+ { "method": "POST", "path": "/api/v1/forms/{id}/sync", "operationId": "pendingForms.sync", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Upload the draft to Meta without publishing it", "pending": true },
484
+ { "method": "POST", "path": "/api/v1/forms/{id}/versions", "operationId": "pendingForms.createVersion", "domain": "conversations", "permission": "conversations:write", "tag": "WhatsappForm", "summary": "Open a new version (draft) of a published form", "pending": true },
485
+ { "method": "GET", "path": "/api/v1/forms/families/{familyId}/definition", "operationId": "pendingForms.definition", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "The current definition of a form", "pending": true },
486
+ { "method": "GET", "path": "/api/v1/forms/families/{familyId}/metrics", "operationId": "pendingForms.metrics", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "The funnel of a form: sent, opened, completed", "pending": true },
487
+ { "method": "GET", "path": "/api/v1/forms/families/{familyId}/responses", "operationId": "pendingForms.responses", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "The answers of a form", "pending": true },
488
+ { "method": "GET", "path": "/api/v1/forms/families/{familyId}/responses/{runId}", "operationId": "pendingForms.response", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "One answer", "pending": true },
489
+ { "method": "GET", "path": "/api/v1/forms/families/{familyId}/versions", "operationId": "pendingForms.versions", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "The versions of a form", "pending": true },
490
+ { "method": "GET", "path": "/api/v1/forms/templates", "operationId": "pendingForms.templates", "domain": "conversations", "permission": "conversations:read", "tag": "WhatsappForm", "summary": "Templates to start a form", "pending": true },
491
+ { "method": "GET", "path": "/api/v1/groups", "operationId": "group.index", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar grupos", "pending": false },
492
+ { "method": "GET", "path": "/api/v1/groups/{group}", "operationId": "group.show", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Ver un grupo", "pending": false },
493
+ { "method": "GET", "path": "/api/v1/groups/{group}/actions", "operationId": "group.actions", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar las acciones de sesi\xF3n sobre un grupo", "pending": false },
494
+ { "method": "GET", "path": "/api/v1/groups/{group}/messages", "operationId": "group.messages", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar los mensajes enviados a un grupo", "pending": false },
495
+ { "method": "GET", "path": "/api/v1/groups/{group}/participant-events", "operationId": "group.participantEvents", "domain": "communities", "permission": "communities:read", "tag": "Participantes", "summary": "Listar entradas y salidas de un grupo", "pending": false },
496
+ { "method": "GET", "path": "/api/v1/groups/{group}/scheduled-messages", "operationId": "group.scheduledMessages", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Listar los env\xEDos programados que incluyen un grupo", "pending": false },
497
+ { "method": "GET", "path": "/api/v1/groups/{groupUid}/metadata", "operationId": "groupMetadata.show", "domain": "communities", "permission": "communities:read", "tag": "Grupos", "summary": "Ver los datos actuales de un grupo en WhatsApp", "pending": false },
498
+ { "method": "GET", "path": "/api/v1/groups/participants", "operationId": "group.participants", "domain": "communities", "permission": "communities:read", "tag": "Participantes", "summary": "Listar los participantes de un grupo", "pending": false },
499
+ { "method": "GET", "path": "/api/v1/kanban-settings", "operationId": "kanbanSetting.index", "domain": "conversations", "permission": "conversations:read", "tag": "KanbanSetting", "summary": "Listar tableros", "pending": false },
500
+ { "method": "POST", "path": "/api/v1/kanban-settings", "operationId": "kanbanSetting.store", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanSetting", "summary": "Crear un tablero", "pending": false },
501
+ { "method": "DELETE", "path": "/api/v1/kanban-settings/{kanbanSetting}", "operationId": "kanbanSetting.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanSetting", "summary": "Eliminar un tablero", "pending": false },
502
+ { "method": "GET", "path": "/api/v1/kanban-settings/{kanbanSetting}", "operationId": "kanbanSetting.show", "domain": "conversations", "permission": "conversations:read", "tag": "KanbanSetting", "summary": "Ver un tablero", "pending": false },
503
+ { "method": "PUT", "path": "/api/v1/kanban-settings/{kanbanSetting}", "operationId": "kanbanSetting.update", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanSetting", "summary": "Actualizar un tablero", "pending": false },
504
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/bulk-move", "operationId": "kanbanStage.bulkMove", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Mover contactos de etapa en bloque", "pending": false },
505
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/bulk-remove", "operationId": "kanbanStage.bulkRemove", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Sacar contactos del tablero en bloque", "pending": false },
506
+ { "method": "GET", "path": "/api/v1/kanban-settings/{kanbanSetting}/members", "operationId": "kanbanSetting.members", "domain": "conversations", "permission": "conversations:read", "tag": "KanbanSetting", "summary": "Ver los miembros de un tablero", "pending": false },
507
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/move-contact", "operationId": "kanbanStage.moveContact", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Mover un contacto de etapa", "pending": false },
508
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/remove-contact", "operationId": "kanbanStage.removeContact", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Sacar un contacto del tablero", "pending": false },
509
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/stages", "operationId": "kanbanStage.store", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Crear una etapa", "pending": false },
510
+ { "method": "DELETE", "path": "/api/v1/kanban-settings/{kanbanSetting}/stages/{stage}", "operationId": "kanbanStage.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Eliminar una etapa", "pending": false },
511
+ { "method": "PUT", "path": "/api/v1/kanban-settings/{kanbanSetting}/stages/{stage}", "operationId": "kanbanStage.update", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Actualizar una etapa", "pending": false },
512
+ { "method": "GET", "path": "/api/v1/kanban-settings/{kanbanSetting}/stages/{stage}/contacts", "operationId": "kanbanStage.contacts", "domain": "conversations", "permission": "conversations:read", "tag": "KanbanStage", "summary": "Listar los contactos de una etapa", "pending": false },
513
+ { "method": "POST", "path": "/api/v1/kanban-settings/{kanbanSetting}/stages/reorder", "operationId": "kanbanStage.reorder", "domain": "conversations", "permission": "conversations:write", "tag": "KanbanStage", "summary": "Reordenar las etapas", "pending": false },
514
+ { "method": "GET", "path": "/api/v1/lead-score/distribution", "operationId": "leadScoreDistribution.show", "domain": "conversations", "permission": "conversations:read", "tag": "LeadScoreDistribution", "summary": "Ver la distribuci\xF3n del lead score", "pending": false },
515
+ { "method": "POST", "path": "/api/v1/lead-score/events", "operationId": "leadScoreEventIngest.ingest", "domain": "conversations", "permission": "conversations:write", "tag": "LeadScoreEventIngest", "summary": "Registrar un evento de lead score", "pending": false },
516
+ { "method": "GET", "path": "/api/v1/lead-score/settings", "operationId": "leadScoreSettings.show", "domain": "conversations", "permission": "conversations:read", "tag": "LeadScoreSettings", "summary": "Ver la configuraci\xF3n del lead score", "pending": false },
517
+ { "method": "PUT", "path": "/api/v1/lead-score/settings", "operationId": "leadScoreSettings.update", "domain": "conversations", "permission": "conversations:write", "tag": "LeadScoreSettings", "summary": "Actualizar la configuraci\xF3n del lead score", "pending": false },
518
+ { "method": "GET", "path": "/api/v1/mac/contacts", "operationId": "mac.contacts", "domain": "conversations", "permission": "conversations:read", "tag": "Mac", "summary": "Listar los contactos que cuentan como MAC", "pending": false },
519
+ { "method": "GET", "path": "/api/v1/mac/contacts/history", "operationId": "mac.contactsHistory", "domain": "conversations", "permission": "conversations:read", "tag": "Mac", "summary": "Ver el historial de MACs", "pending": false },
520
+ { "method": "GET", "path": "/api/v1/mac/status", "operationId": "mac.status", "domain": "conversations", "permission": "conversations:read", "tag": "Mac", "summary": "Ver el consumo de MACs", "pending": false },
521
+ { "method": "GET", "path": "/api/v1/me", "operationId": null, "domain": "accounts", "permission": "accounts:read", "tag": "Account", "summary": "Ver mi cuenta", "pending": false },
522
+ { "method": "GET", "path": "/api/v1/me/whatsapp-costs", "operationId": "whatsappCost.index", "domain": "accounts", "permission": "accounts:read", "tag": "WhatsappCost", "summary": "Ver los costos de WhatsApp", "pending": false },
523
+ { "method": "POST", "path": "/api/v1/message/send-message", "operationId": "message.sendMessage", "domain": "conversations", "permission": "conversations:send", "tag": "Message", "summary": "Enviar un mensaje", "pending": true },
524
+ { "method": "GET", "path": "/api/v1/payments/products/{platform}", "operationId": "paymentProduct.index", "domain": "conversations", "permission": "conversations:read", "tag": "PaymentProduct", "summary": "Listar los productos de una plataforma de pagos", "pending": false },
525
+ { "method": "GET", "path": "/api/v1/quick-replies", "operationId": "quickReply.index", "domain": "conversations", "permission": "conversations:read", "tag": "QuickReply", "summary": "Listar respuestas r\xE1pidas", "pending": false },
526
+ { "method": "POST", "path": "/api/v1/quick-replies", "operationId": "quickReply.store", "domain": "conversations", "permission": "conversations:write", "tag": "QuickReply", "summary": "Crear una respuesta r\xE1pida", "pending": false },
527
+ { "method": "DELETE", "path": "/api/v1/quick-replies/{quickReply}", "operationId": "quickReply.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "QuickReply", "summary": "Eliminar una respuesta r\xE1pida", "pending": false },
528
+ { "method": "GET", "path": "/api/v1/quick-replies/{quickReply}", "operationId": "quickReply.show", "domain": "conversations", "permission": "conversations:read", "tag": "QuickReply", "summary": "Ver una respuesta r\xE1pida", "pending": false },
529
+ { "method": "PUT", "path": "/api/v1/quick-replies/{quickReply}", "operationId": "quickReply.update", "domain": "conversations", "permission": "conversations:write", "tag": "QuickReply", "summary": "Actualizar una respuesta r\xE1pida", "pending": false },
530
+ { "method": "GET", "path": "/api/v1/sales", "operationId": "salesDashboard.index", "domain": "conversations", "permission": "conversations:read", "tag": "SalesDashboard", "summary": "Listar ventas", "pending": false },
531
+ { "method": "GET", "path": "/api/v1/sales/by-platform", "operationId": "salesDashboard.byPlatform", "domain": "conversations", "permission": "conversations:read", "tag": "SalesDashboard", "summary": "Ver las ventas por plataforma", "pending": false },
532
+ { "method": "GET", "path": "/api/v1/sales/funnel", "operationId": "salesDashboard.funnel", "domain": "conversations", "permission": "conversations:read", "tag": "SalesDashboard", "summary": "Ver el embudo de ventas", "pending": false },
533
+ { "method": "GET", "path": "/api/v1/sales/metrics", "operationId": "salesDashboard.metrics", "domain": "conversations", "permission": "conversations:read", "tag": "SalesDashboard", "summary": "Ver las m\xE9tricas de ventas", "pending": false },
534
+ { "method": "GET", "path": "/api/v1/scheduled-messages", "operationId": "scheduledMessage.index", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Listar env\xEDos programados", "pending": false },
535
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}", "operationId": "scheduledMessage.show", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Ver un env\xEDo programado", "pending": false },
536
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/group-messages", "operationId": "scheduledMessage.groupMessages", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Ver el resultado por grupo de un env\xEDo", "pending": false },
537
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/groups", "operationId": "scheduledMessage.groups", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Listar los grupos destino de un env\xEDo", "pending": false },
538
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/readed-played-rates", "operationId": "scheduledMessage.readedAndPlayedRates", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Ver las tasas de lectura y reproducci\xF3n de un env\xEDo", "pending": false },
539
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/response-tags", "operationId": "scheduledMessage.responseTags", "domain": "communities", "permission": "communities:read", "tag": "Respuestas y carpetas", "summary": "Listar las etiquetas que se aplican a quien responde", "pending": false },
540
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/responses", "operationId": "scheduledMessage.responses", "domain": "communities", "permission": "communities:read", "tag": "Respuestas y carpetas", "summary": "Listar las respuestas a un env\xEDo", "pending": false },
541
+ { "method": "GET", "path": "/api/v1/scheduled-messages/{scheduledMessage}/runs", "operationId": "scheduledMessage.runs", "domain": "communities", "permission": "communities:read", "tag": "Mensajes programados", "summary": "Listar las ocurrencias de un env\xEDo recurrente", "pending": false },
542
+ { "method": "GET", "path": "/api/v1/scheduled-messages/folders", "operationId": "scheduledMessageFolder.index", "domain": "communities", "permission": "communities:read", "tag": "Respuestas y carpetas", "summary": "Listar las carpetas de env\xEDos", "pending": false },
543
+ { "method": "GET", "path": "/api/v1/stats/active-participants", "operationId": "stats.activeParticipants", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Ver participantes activos", "pending": false },
544
+ { "method": "GET", "path": "/api/v1/stats/clicks", "operationId": "stats.clickStats", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Ver clics en links", "pending": false },
545
+ { "method": "GET", "path": "/api/v1/stats/countries", "operationId": "stats.countryList", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Listar pa\xEDses presentes", "pending": false },
546
+ { "method": "GET", "path": "/api/v1/stats/country-metrics", "operationId": "stats.countryMetrics", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Ver m\xE9tricas por pa\xEDs", "pending": false },
547
+ { "method": "GET", "path": "/api/v1/stats/groups", "operationId": "stats.groupStats", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Ver grupos por estado", "pending": false },
548
+ { "method": "GET", "path": "/api/v1/stats/participants", "operationId": "stats.participantStats", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Ver participantes por per\xEDodo", "pending": false },
549
+ { "method": "GET", "path": "/api/v1/stats/records", "operationId": "stats.statsRecords", "domain": "communities", "permission": "communities:read", "tag": "Estad\xEDsticas", "summary": "Listar registros de clics", "pending": false },
550
+ { "method": "GET", "path": "/api/v1/tags", "operationId": "tag.index", "domain": "conversations", "permission": "conversations:read", "tag": "Tag", "summary": "Listar etiquetas", "pending": false },
551
+ { "method": "POST", "path": "/api/v1/tags", "operationId": "tag.store", "domain": "conversations", "permission": "conversations:write", "tag": "Tag", "summary": "Crear una etiqueta", "pending": false },
552
+ { "method": "GET", "path": "/api/v1/tags/{tag}", "operationId": "tag.show", "domain": "conversations", "permission": "conversations:read", "tag": "Tag", "summary": "Ver una etiqueta", "pending": false },
553
+ { "method": "PUT", "path": "/api/v1/tags/{tag}", "operationId": "tag.update", "domain": "conversations", "permission": "conversations:write", "tag": "Tag", "summary": "Actualizar una etiqueta", "pending": false },
554
+ { "method": "GET", "path": "/api/v1/tags/{tag}/contacts", "operationId": "tag.contacts", "domain": "conversations", "permission": "conversations:read", "tag": "Tag", "summary": "Listar los contactos con una etiqueta", "pending": false },
555
+ { "method": "POST", "path": "/api/v1/tags/delete", "operationId": "tag.destroy", "domain": "conversations", "permission": "conversations:write", "tag": "Tag", "summary": "Eliminar etiquetas", "pending": false },
556
+ { "method": "GET", "path": "/api/v1/templates", "operationId": "messageTemplate.templates", "domain": "conversations", "permission": "conversations:read", "tag": "MessageTemplate", "summary": "Listar plantillas", "pending": false },
557
+ { "method": "POST", "path": "/api/v1/templates", "operationId": "messageTemplate.store", "domain": "conversations", "permission": "conversations:write", "tag": "MessageTemplate", "summary": "Crear una plantilla", "pending": false },
558
+ { "method": "GET", "path": "/api/v1/templates/{template}", "operationId": "messageTemplate.template", "domain": "conversations", "permission": "conversations:read", "tag": "MessageTemplate", "summary": "Ver una plantilla", "pending": false },
559
+ { "method": "PUT", "path": "/api/v1/templates/{template}", "operationId": "messageTemplate.update", "domain": "conversations", "permission": "conversations:write", "tag": "MessageTemplate", "summary": "Actualizar una plantilla", "pending": false },
560
+ { "method": "GET", "path": "/api/v1/templates/{template}/quick-reply-buttons", "operationId": "messageTemplate.quickReplyButtons", "domain": "conversations", "permission": "conversations:read", "tag": "MessageTemplate", "summary": "Ver los botones de respuesta r\xE1pida de una plantilla", "pending": false },
561
+ { "method": "GET", "path": "/api/v1/tickets", "operationId": "ticket.index", "domain": "conversations", "permission": "conversations:read", "tag": "Ticket", "summary": "Listar tickets", "pending": false },
562
+ { "method": "POST", "path": "/api/v1/tickets", "operationId": "ticket.store", "domain": "conversations", "permission": "conversations:write", "tag": "Ticket", "summary": "Crear un ticket", "pending": false },
563
+ { "method": "GET", "path": "/api/v1/tickets/{id}", "operationId": "ticket.show", "domain": "conversations", "permission": "conversations:read", "tag": "Ticket", "summary": "Ver un ticket", "pending": false },
564
+ { "method": "PUT", "path": "/api/v1/tickets/{id}", "operationId": "ticket.update", "domain": "conversations", "permission": "conversations:write", "tag": "Ticket", "summary": "Actualizar un ticket", "pending": false },
565
+ { "method": "POST", "path": "/api/v1/tickets/{id}/close", "operationId": "ticket.closeTicket", "domain": "conversations", "permission": "conversations:write", "tag": "Ticket", "summary": "Cerrar un ticket", "pending": false },
566
+ { "method": "POST", "path": "/api/v1/tickets/{id}/reopen", "operationId": "ticket.reOpenTicket", "domain": "conversations", "permission": "conversations:write", "tag": "Ticket", "summary": "Reabrir un ticket", "pending": false },
567
+ { "method": "POST", "path": "/api/v1/tickets/assign", "operationId": "ticket.assignTicketToUser", "domain": "conversations", "permission": "conversations:write", "tag": "Ticket", "summary": "Asignar tickets", "pending": false },
568
+ { "method": "GET", "path": "/api/v1/webhooks", "operationId": "pendingWebhookSubscription.index", "domain": "accounts", "permission": "webhooks:read", "tag": "Webhooks", "summary": "List the webhook subscriptions", "pending": true },
569
+ { "method": "POST", "path": "/api/v1/webhooks", "operationId": "pendingWebhookSubscription.store", "domain": "accounts", "permission": "webhooks:write", "tag": "Webhooks", "summary": "Create a webhook subscription", "pending": true },
570
+ { "method": "DELETE", "path": "/api/v1/webhooks/{id}", "operationId": "pendingWebhookSubscription.destroy", "domain": "accounts", "permission": "webhooks:write", "tag": "Webhooks", "summary": "Delete a webhook subscription", "pending": true },
571
+ { "method": "GET", "path": "/api/v1/webhooks/{id}", "operationId": "pendingWebhookSubscription.show", "domain": "accounts", "permission": "webhooks:read", "tag": "Webhooks", "summary": "Read a webhook subscription", "pending": true },
572
+ { "method": "PUT", "path": "/api/v1/webhooks/{id}", "operationId": "pendingWebhookSubscription.update", "domain": "accounts", "permission": "webhooks:write", "tag": "Webhooks", "summary": "Update a webhook subscription", "pending": true },
573
+ { "method": "GET", "path": "/api/v1/webhooks/{id}/deliveries", "operationId": "pendingWebhookSubscription.deliveries", "domain": "accounts", "permission": "webhooks:read", "tag": "Webhooks", "summary": "Read the delivery history", "pending": true },
574
+ { "method": "POST", "path": "/api/v1/webhooks/{id}/rotate-secret", "operationId": "pendingWebhookSubscription.rotateSecret", "domain": "accounts", "permission": "webhooks:write", "tag": "Webhooks", "summary": "Generate a new secret", "pending": true },
575
+ { "method": "POST", "path": "/api/v1/webhooks/{id}/test", "operationId": "pendingWebhookSubscription.test", "domain": "accounts", "permission": "webhooks:write", "tag": "Webhooks", "summary": "Send a test event now", "pending": true },
576
+ { "method": "GET", "path": "/api/v1/webhooks/endpoints", "operationId": "webhookEndpoint.index", "domain": "accounts", "permission": "accounts:read", "tag": "WebhookEndpoint", "summary": "Listar webhooks", "pending": false },
577
+ { "method": "POST", "path": "/api/v1/webhooks/endpoints", "operationId": "webhookEndpoint.store", "domain": "accounts", "permission": "accounts:write", "tag": "WebhookEndpoint", "summary": "Crear un webhook", "pending": false },
578
+ { "method": "DELETE", "path": "/api/v1/webhooks/endpoints/{id}", "operationId": "webhookEndpoint.destroy", "domain": "accounts", "permission": "accounts:write", "tag": "WebhookEndpoint", "summary": "Eliminar un webhook", "pending": false },
579
+ { "method": "GET", "path": "/api/v1/webhooks/endpoints/{id}", "operationId": "webhookEndpoint.show", "domain": "accounts", "permission": "accounts:read", "tag": "WebhookEndpoint", "summary": "Ver un webhook", "pending": false },
580
+ { "method": "PATCH", "path": "/api/v1/webhooks/endpoints/{id}", "operationId": "webhookEndpoint.update", "domain": "accounts", "permission": "accounts:write", "tag": "WebhookEndpoint", "summary": "Actualizar un webhook", "pending": false },
581
+ { "method": "GET", "path": "/api/v1/webhooks/endpoints/{id}/deliveries", "operationId": "webhookDelivery.index", "domain": "accounts", "permission": "accounts:read", "tag": "WebhookDelivery", "summary": "Listar entregas de un webhook", "pending": false },
582
+ { "method": "PUT", "path": "/api/v1/webhooks/endpoints/{id}/events", "operationId": "webhookEndpointEvent.update", "domain": "accounts", "permission": "accounts:write", "tag": "WebhookEndpointEvent", "summary": "Elegir los eventos de un webhook", "pending": false },
583
+ { "method": "GET", "path": "/api/v1/webhooks/events", "operationId": "webhookEventCatalog.index", "domain": "accounts", "permission": "accounts:read", "tag": "WebhookEventCatalog", "summary": "Listar tipos de evento", "pending": false }
584
+ ];
585
+
586
+ // src/core/retry.ts
587
+ var INITIAL_RETRY_DELAY_MS = 500;
588
+ var MAX_RETRY_DELAY_MS = 8e3;
589
+ var MAX_RETRY_AFTER_SECONDS = 60;
590
+ var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS", "PUT", "DELETE"]);
591
+ function isRetryable(method, failure) {
592
+ if (IDEMPOTENT_METHODS.has(method.toUpperCase())) return failure === "network" || failure === 429 || failure >= 500;
593
+ return failure === 429 || failure === 503;
594
+ }
595
+ function retryDelayMs(attempt, retryAfterSeconds, random = Math.random) {
596
+ if (retryAfterSeconds !== void 0) {
597
+ if (retryAfterSeconds > MAX_RETRY_AFTER_SECONDS) return null;
598
+ return Math.max(0, Math.ceil(retryAfterSeconds * 1e3));
599
+ }
600
+ const base = Math.min(INITIAL_RETRY_DELAY_MS * 2 ** attempt, MAX_RETRY_DELAY_MS);
601
+ return Math.round(base * (1 - 0.25 * random()));
602
+ }
603
+ var placeholder = /\{[^}]+\}/g;
604
+ var escape = (text) => text.replace(/[.*+?^$()|[\]\\]/g, "\\$&");
605
+ var SEND_POST_PATTERNS = routes.filter((route) => route.method === "POST" && route.permission?.endsWith(":send")).map((route) => new RegExp(`^${route.path.split(placeholder).map(escape).join("[^/]+")}/?$`));
606
+ function isSendRequest(method, pathname) {
607
+ if (method.toUpperCase() !== "POST") return false;
608
+ return SEND_POST_PATTERNS.some((pattern) => pattern.test(pathname));
609
+ }
610
+
611
+ // src/core/url.ts
612
+ function serializeQuery(query) {
613
+ if (!query) return "";
614
+ const params = new URLSearchParams();
615
+ for (const [key, value] of Object.entries(query)) append(params, key, value);
616
+ return params.toString();
617
+ }
618
+ function append(params, key, value) {
619
+ if (value === void 0 || value === null) return;
620
+ if (Array.isArray(value)) {
621
+ const scalarKey = key.endsWith("[]") ? key : `${key}[]`;
622
+ const base = key.endsWith("[]") ? key.slice(0, -2) : key;
623
+ value.forEach((item, index) => {
624
+ if (isPlainObject(item) || Array.isArray(item)) append(params, `${base}[${index}]`, item);
625
+ else append(params, scalarKey, item);
626
+ });
627
+ return;
628
+ }
629
+ if (value instanceof Date) {
630
+ params.append(key, value.toISOString());
631
+ return;
632
+ }
633
+ if (isPlainObject(value)) {
634
+ for (const [child, childValue] of Object.entries(value)) append(params, `${key}[${child}]`, childValue);
635
+ return;
636
+ }
637
+ if (typeof value === "boolean") {
638
+ params.append(key, value ? "1" : "0");
639
+ return;
640
+ }
641
+ params.append(key, String(value));
642
+ }
643
+ function isPlainObject(value) {
644
+ if (typeof value !== "object" || value === null) return false;
645
+ const proto = Object.getPrototypeOf(value);
646
+ return proto === Object.prototype || proto === null;
647
+ }
648
+ function interpolatePath(template, params) {
649
+ return template.replace(/\{([^}]+)\}/g, (_, name) => {
650
+ const value = params?.[name];
651
+ if (value === void 0 || value === null || value === "") {
652
+ throw new TypeError(`Missing path parameter "${name}" for ${template}.`);
653
+ }
654
+ return encodeURIComponent(String(value));
655
+ });
656
+ }
657
+ function joinUrl(baseUrl, path, query) {
658
+ const base = baseUrl.replace(/\/+$/, "");
659
+ const suffix = path.startsWith("/") ? path : `/${path}`;
660
+ if (!query) return `${base}${suffix}`;
661
+ return `${base}${suffix}${suffix.includes("?") ? "&" : "?"}${query}`;
662
+ }
663
+
664
+ // src/resources/base.ts
665
+ var Resource = class {
666
+ // A private field behind a getter: the client is not an own enumerable property, so logging or
667
+ // serializing a resource neither recurses into the client nor fails on a circular structure.
668
+ #client;
669
+ constructor(client) {
670
+ this.#client = client;
671
+ }
672
+ get client() {
673
+ return this.#client;
674
+ }
675
+ };
676
+
677
+ // src/resources/contacts.ts
678
+ var Contacts = class extends Resource {
679
+ /** `GET /api/v1/contacts`, paginated. Filter by `phone` without the `+`. */
680
+ list(query, options) {
681
+ return this.client.paginate("/api/v1/contacts", { ...options, query });
682
+ }
683
+ /** `POST /api/v1/contacts`. */
684
+ create(body, options) {
685
+ return this.client.request("POST", "/api/v1/contacts", { ...options, body });
686
+ }
687
+ /** `POST /api/v1/contacts/update-or-create`: creates the contact or updates the one with that phone. */
688
+ upsert(body, options) {
689
+ return this.client.request("POST", "/api/v1/contacts/update-or-create", { ...options, body });
690
+ }
691
+ /** `GET /api/v1/contacts/{contact}`. */
692
+ retrieve(contact, options) {
693
+ return this.client.request("GET", "/api/v1/contacts/{contact}", { ...options, path: { contact } });
694
+ }
695
+ /** `PUT /api/v1/contacts/{contact}`. */
696
+ update(contact, body, options) {
697
+ return this.client.request("PUT", "/api/v1/contacts/{contact}", { ...options, path: { contact }, body });
698
+ }
699
+ /** `POST /api/v1/contacts/delete`: deletes the contacts of the body (`contactIds`, or a filter). */
700
+ delete(body, options) {
701
+ return this.client.request("POST", "/api/v1/contacts/delete", { ...options, body });
702
+ }
703
+ /** `GET /api/v1/contacts/{contact}/tags`, paginated. */
704
+ listTags(contact, query, options) {
705
+ return this.client.paginate("/api/v1/contacts/{contact}/tags", { ...options, path: { contact }, query });
706
+ }
707
+ /** `GET /api/v1/contacts/{contact}/custom-fields`, paginated. */
708
+ listCustomFields(contact, query, options) {
709
+ return this.client.paginate("/api/v1/contacts/{contact}/custom-fields", { ...options, path: { contact }, query });
710
+ }
711
+ /** `POST /api/v1/contacts/attach-custom-field`: sets a custom field on contacts. */
712
+ attachCustomField(body, options) {
713
+ return this.client.request("POST", "/api/v1/contacts/attach-custom-field", { ...options, body });
714
+ }
715
+ /** `POST /api/v1/contacts/detach-custom-field`. */
716
+ detachCustomField(body, options) {
717
+ return this.client.request("POST", "/api/v1/contacts/detach-custom-field", { ...options, body });
718
+ }
719
+ /**
720
+ * `GET /api/v1/contacts/{contact}/meta-window`: whether the 24-hour window with the contact is
721
+ * open on a WhatsApp Cloud number (`is_open`). Outside it, only templates can be sent.
722
+ */
723
+ metaWindow(contact, query, options) {
724
+ return this.client.request("GET", "/api/v1/contacts/{contact}/meta-window", { ...options, path: { contact }, query });
725
+ }
726
+ /** `GET /api/v1/devices/{device}/contacts`, paginated: the contacts of one number. */
727
+ listByDevice(device, query, options) {
728
+ return this.client.paginate("/api/v1/devices/{device}/contacts", { ...options, path: { device }, query });
729
+ }
730
+ /** `GET /api/v1/contacts/{contact}/notes`, paginated. */
731
+ listNotes(contact, query, options) {
732
+ return this.client.paginate("/api/v1/contacts/{contact}/notes", { ...options, path: { contact }, query });
733
+ }
734
+ /** `POST /api/v1/contacts/{contact}/notes`. */
735
+ createNote(contact, body, options) {
736
+ return this.client.request("POST", "/api/v1/contacts/{contact}/notes", { ...options, path: { contact }, body });
737
+ }
738
+ /** `DELETE /api/v1/contacts/{contact}/notes/{note}`. */
739
+ deleteNote(contact, note, options) {
740
+ return this.client.request("DELETE", "/api/v1/contacts/{contact}/notes/{note}", { ...options, path: { contact, note } });
741
+ }
742
+ };
743
+
744
+ // src/resources/conversations.ts
745
+ var Conversations = class extends Resource {
746
+ /** `GET /api/v1/conversations`, paginated by cursor. */
747
+ list(query, options) {
748
+ return this.client.paginate("/api/v1/conversations", { ...options, query });
749
+ }
750
+ /** `PUT /api/v1/contacts/{contact}/update`: changes the conversation with a contact (open, assigned…). */
751
+ update(contact, body, options) {
752
+ return this.client.request("PUT", "/api/v1/contacts/{contact}/update", { ...options, path: { contact }, body });
753
+ }
754
+ /** `GET /api/v1/conversations/count/unread`. */
755
+ countUnread(query, options) {
756
+ return this.client.request("GET", "/api/v1/conversations/count/unread", { ...options, query });
757
+ }
758
+ /** `GET /api/v1/conversations/count/open`. */
759
+ countOpen(query, options) {
760
+ return this.client.request("GET", "/api/v1/conversations/count/open", { ...options, query });
761
+ }
762
+ /** `GET /api/v1/conversations/count/closed`. */
763
+ countClosed(query, options) {
764
+ return this.client.request("GET", "/api/v1/conversations/count/closed", { ...options, query });
765
+ }
766
+ /** `GET /api/v1/conversations/count/all`. */
767
+ countAll(query, options) {
768
+ return this.client.request("GET", "/api/v1/conversations/count/all", { ...options, query });
769
+ }
770
+ /** `POST /api/v1/conversations/mark-all-read`. */
771
+ markAllRead(body, options) {
772
+ return this.client.request("POST", "/api/v1/conversations/mark-all-read", { ...options, body });
773
+ }
774
+ /** `POST /api/v1/conversations/mark-all-unread`. */
775
+ markAllUnread(body, options) {
776
+ return this.client.request("POST", "/api/v1/conversations/mark-all-unread", { ...options, body });
777
+ }
778
+ };
779
+
780
+ // src/resources/custom-fields.ts
781
+ var CustomFields = class extends Resource {
782
+ /** `GET /api/v1/custom-fields`, paginated. */
783
+ list(query, options) {
784
+ return this.client.paginate("/api/v1/custom-fields", { ...options, query });
785
+ }
786
+ /** `POST /api/v1/custom-fields`. */
787
+ create(body, options) {
788
+ return this.client.request("POST", "/api/v1/custom-fields", { ...options, body });
789
+ }
790
+ /** `GET /api/v1/custom-fields/{customField}`. */
791
+ retrieve(customField, options) {
792
+ return this.client.request("GET", "/api/v1/custom-fields/{customField}", { ...options, path: { customField } });
793
+ }
794
+ /** `PUT /api/v1/custom-fields/{customField}`. */
795
+ update(customField, body, options) {
796
+ return this.client.request("PUT", "/api/v1/custom-fields/{customField}", { ...options, path: { customField }, body });
797
+ }
798
+ /** `POST /api/v1/custom-fields/delete`. */
799
+ delete(body, options) {
800
+ return this.client.request("POST", "/api/v1/custom-fields/delete", { ...options, body });
801
+ }
802
+ };
803
+
804
+ // src/resources/devices.ts
805
+ var Devices = class extends Resource {
806
+ /** `GET /api/v1/devices`. Iterate it with `for await`, or `await` it for the first page. */
807
+ list(options) {
808
+ return this.client.paginate("/api/v1/devices", { ...options });
809
+ }
810
+ /**
811
+ * `GET /api/v1/devices/{device}`. `capabilities` says what the number can do by API: message
812
+ * types (`messages`) and the statuses `message.status_changed` reports (`statuses`).
813
+ */
814
+ retrieve(device, options) {
815
+ return this.client.request("GET", "/api/v1/devices/{device}", { ...options, path: { device } });
816
+ }
817
+ /** `PUT /api/v1/devices/{device}`. */
818
+ update(device, body, options) {
819
+ return this.client.request("PUT", "/api/v1/devices/{device}", { ...options, path: { device }, body });
820
+ }
821
+ /** `GET /api/v1/devices/{device}/status`: connection status. */
822
+ status(device, options) {
823
+ return this.client.request("GET", "/api/v1/devices/{device}/status", { ...options, path: { device } });
824
+ }
825
+ /** `GET /api/v1/devices/{device}/health`. */
826
+ health(device, options) {
827
+ return this.client.request("GET", "/api/v1/devices/{device}/health", { ...options, path: { device } });
828
+ }
829
+ /** `GET /api/v1/devices/{device}/me`: the WhatsApp profile of the number. */
830
+ me(device, options) {
831
+ return this.client.request("GET", "/api/v1/devices/{device}/me", { ...options, path: { device } });
832
+ }
833
+ /** `GET /api/v1/devices/{device}/check-phone`: whether a phone has WhatsApp. */
834
+ checkPhone(device, query, options) {
835
+ return this.client.request("GET", "/api/v1/devices/{device}/check-phone", { ...options, path: { device }, query });
836
+ }
837
+ /** `GET /api/v1/devices/{device}/connection-history`, paginated. */
838
+ listConnectionHistory(device, query, options) {
839
+ return this.client.paginate("/api/v1/devices/{device}/connection-history", { ...options, path: { device }, query });
840
+ }
841
+ };
842
+
843
+ // src/resources/forms.ts
844
+ var Forms = class extends Resource {
845
+ /** `GET /api/v1/forms`, paginated: filter by `q`, `status` or `device_id`. */
846
+ list(query, options) {
847
+ return this.client.paginate("/api/v1/forms", { ...options, query });
848
+ }
849
+ /** `GET /api/v1/forms/templates`: the starting points `create` accepts as `template_key`. */
850
+ templates(options) {
851
+ return this.client.request("GET", "/api/v1/forms/templates", { ...options });
852
+ }
853
+ /** `POST /api/v1/forms`: a new draft from a template, on `device_id` (and `device_ids`). */
854
+ create(body, options) {
855
+ return this.client.request("POST", "/api/v1/forms", { ...options, body });
856
+ }
857
+ /** `GET /api/v1/forms/{id}`: one version, with `lint`, `versions` and `numbers`. */
858
+ retrieve(id, options) {
859
+ return this.client.request("GET", "/api/v1/forms/{id}", { ...options, path: { id } });
860
+ }
861
+ /**
862
+ * `PUT /api/v1/forms/{id}`: saves the `definition` of a draft. Pass the `updated_at` you read as
863
+ * `expected_updated_at` to get `409 STALE` instead of overwriting someone else's change.
864
+ */
865
+ update(id, body, options) {
866
+ return this.client.request("PUT", "/api/v1/forms/{id}", { ...options, path: { id }, body });
867
+ }
868
+ /** `DELETE /api/v1/forms/{id}`: deletes a draft (a published version answers `409 PUBLISHED`). */
869
+ delete(id, options) {
870
+ return this.client.request("DELETE", "/api/v1/forms/{id}", { ...options, path: { id } });
871
+ }
872
+ /** `POST /api/v1/forms/{id}/versions`: opens a draft of a published form, or returns the open one (`created: false`). */
873
+ createVersion(id, options) {
874
+ return this.client.request("POST", "/api/v1/forms/{id}/versions", { ...options, path: { id } });
875
+ }
876
+ /** `POST /api/v1/forms/{id}/sync`: uploads the draft to Meta without publishing it. */
877
+ sync(id, options) {
878
+ return this.client.request("POST", "/api/v1/forms/{id}/sync", { ...options, path: { id } });
879
+ }
880
+ /** `POST /api/v1/forms/{id}/publish`: publishes the version on its numbers. */
881
+ publish(id, options) {
882
+ return this.client.request("POST", "/api/v1/forms/{id}/publish", { ...options, path: { id } });
883
+ }
884
+ /** `GET /api/v1/forms/{id}/numbers`: where the form is (or is being) published. */
885
+ numbers(id, options) {
886
+ return this.client.request("GET", "/api/v1/forms/{id}/numbers", { ...options, path: { id } });
887
+ }
888
+ /** `POST /api/v1/forms/{id}/numbers`: publishes it on one more number. */
889
+ addNumber(id, body, options) {
890
+ return this.client.request("POST", "/api/v1/forms/{id}/numbers", { ...options, path: { id }, body });
891
+ }
892
+ /** `DELETE /api/v1/forms/{id}/numbers/{deviceId}`: takes it off a number (not the primary one). */
893
+ removeNumber(id, deviceId, options) {
894
+ return this.client.request("DELETE", "/api/v1/forms/{id}/numbers/{deviceId}", { ...options, path: { id, deviceId } });
895
+ }
896
+ /** `POST /api/v1/forms/{id}/numbers/retry`: retries the numbers whose publication failed. */
897
+ retryNumbers(id, options) {
898
+ return this.client.request("POST", "/api/v1/forms/{id}/numbers/retry", { ...options, path: { id } });
899
+ }
900
+ /** `GET /api/v1/forms/families/{familyId}/definition`: the current definition of a form, or `data: null`. */
901
+ definition(familyId, options) {
902
+ return this.client.request("GET", "/api/v1/forms/families/{familyId}/definition", { ...options, path: { familyId } });
903
+ }
904
+ /** `GET /api/v1/forms/families/{familyId}/versions`: every version of a form. */
905
+ versions(familyId, options) {
906
+ return this.client.request("GET", "/api/v1/forms/families/{familyId}/versions", { ...options, path: { familyId } });
907
+ }
908
+ /** `GET /api/v1/forms/families/{familyId}/responses`, paginated: the answers, with their `columns`. */
909
+ listResponses(familyId, query, options) {
910
+ return this.client.paginate("/api/v1/forms/families/{familyId}/responses", { ...options, path: { familyId }, query });
911
+ }
912
+ /** `GET /api/v1/forms/families/{familyId}/responses/{runId}`: one answer. */
913
+ retrieveResponse(familyId, runId, options) {
914
+ return this.client.request("GET", "/api/v1/forms/families/{familyId}/responses/{runId}", { ...options, path: { familyId, runId } });
915
+ }
916
+ /** `GET /api/v1/forms/families/{familyId}/metrics`: the funnel (sent, opened, completed). */
917
+ metrics(familyId, query, options) {
918
+ return this.client.request("GET", "/api/v1/forms/families/{familyId}/metrics", { ...options, path: { familyId }, query });
919
+ }
920
+ };
921
+
922
+ // src/resources/groups.ts
923
+ var Groups = class extends Resource {
924
+ /** `GET /api/v1/groups`, paginated. */
925
+ list(query, options) {
926
+ return this.client.paginate("/api/v1/groups", { ...options, query });
927
+ }
928
+ /** `GET /api/v1/groups/{group}`. */
929
+ retrieve(group, options) {
930
+ return this.client.request("GET", "/api/v1/groups/{group}", { ...options, path: { group } });
931
+ }
932
+ /** `GET /api/v1/devices/{device}/groups`, paginated (`page`, `pageSize`): the groups of one number. */
933
+ listByDevice(device, query, options) {
934
+ return this.client.paginate("/api/v1/devices/{device}/groups", { ...options, path: { device }, query });
935
+ }
936
+ /** `GET /api/v1/groups/{groupUid}/metadata`: live metadata of a group by its WhatsApp uid, read through one number. */
937
+ metadata(groupUid, query, options) {
938
+ return this.client.request("GET", "/api/v1/groups/{groupUid}/metadata", { ...options, path: { groupUid }, query });
939
+ }
940
+ /** `GET /api/v1/groups/participants`, paginated: the participants of one group (`groupUid`). */
941
+ listParticipants(query, options) {
942
+ return this.client.paginate("/api/v1/groups/participants", { ...options, query });
943
+ }
944
+ /** `GET /api/v1/groups/{group}/participant-events`, paginated: joins and leaves. */
945
+ listParticipantEvents(group, query, options) {
946
+ return this.client.paginate("/api/v1/groups/{group}/participant-events", { ...options, path: { group }, query });
947
+ }
948
+ /** `GET /api/v1/groups/{group}/messages`, paginated. */
949
+ listMessages(group, query, options) {
950
+ return this.client.paginate("/api/v1/groups/{group}/messages", { ...options, path: { group }, query });
951
+ }
952
+ /** `GET /api/v1/groups/{group}/scheduled-messages`, paginated. */
953
+ listScheduledMessages(group, query, options) {
954
+ return this.client.paginate("/api/v1/groups/{group}/scheduled-messages", { ...options, path: { group }, query });
955
+ }
956
+ /** `GET /api/v1/groups/{group}/actions`, paginated. */
957
+ listActions(group, query, options) {
958
+ return this.client.paginate("/api/v1/groups/{group}/actions", { ...options, path: { group }, query });
959
+ }
960
+ };
961
+
962
+ // src/resources/messages.ts
963
+ var Messages = class extends Resource {
964
+ /**
965
+ * `POST /api/v1/contacts/{contact}/message`: text, a file or buttons and lists (`options`).
966
+ * Resolves to `{ message }`: the registered message, with its `id` and `status: 'queued'`.
967
+ *
968
+ * - Send `phone` (or `whatsapp_id`) of the same contact: the API checks it (`422 recipient_mismatch`).
969
+ * - `file` is a URL. When it does not end in a known extension (a signed URL, or one without
970
+ * extension), send `file_type` (`image`, `video`, `audio`, `document`, `sticker`), and
971
+ * `file_name` for a document: otherwise `422 errors.file_type` / `errors.file_name`. An
972
+ * unsupported extension without `file_type` is a `415` ({@link UnsupportedMediaTypeError}).
973
+ * - `client_reference` (1–64 visible ASCII characters) comes back in the history and in
974
+ * `message.status_changed`.
975
+ */
976
+ send(contact, body, options) {
977
+ const fullBody = { ...body, contact_id: body.contact_id ?? contact };
978
+ return this.client.request("POST", "/api/v1/contacts/{contact}/message", { ...options, path: { contact }, body: fullBody });
979
+ }
980
+ /**
981
+ * `POST /api/v1/message/send-message`: the same send with `device_id` and `contact_id` in the
982
+ * body and no `phone` check. Resolves to `{ message }`.
983
+ */
984
+ sendDirect(body, options) {
985
+ return this.client.request("POST", "/api/v1/message/send-message", { ...options, body });
986
+ }
987
+ /**
988
+ * `POST /api/v1/contacts/{contact}/message/form`: sends a published WhatsApp form (`form_id` is
989
+ * the form's `family_id`). Resolves to `{ message }` with `type: 'whatsapp_form'`; the answers
990
+ * arrive by `whatsapp_form.completed`. It is a send: same rules as {@link Messages.send}.
991
+ */
992
+ sendForm(contact, body, options) {
993
+ return this.client.request("POST", "/api/v1/contacts/{contact}/message/form", { ...options, path: { contact }, body });
994
+ }
995
+ /**
996
+ * `GET /api/v1/contacts/{contact}/messages/{message}`: one message by the id a send or a webhook
997
+ * gave you (`message_id` of `message.status_changed`). `status` moves from `queued` to `sent`,
998
+ * `delivered`, `read` or `failed` (then `error` says why).
999
+ */
1000
+ get(contact, message, options) {
1001
+ return this.client.request("GET", "/api/v1/contacts/{contact}/messages/{message}", { ...options, path: { contact, message } });
1002
+ }
1003
+ /** `GET /api/v1/contacts/{contactId}/messages`, paginated by cursor: the history of a contact. */
1004
+ list(contactId, query, options) {
1005
+ return this.client.paginate("/api/v1/contacts/{contactId}/messages", { ...options, path: { contactId }, query });
1006
+ }
1007
+ /**
1008
+ * `POST /api/v1/contacts/{contact}/message/contact-card`: sends up to 5 contact cards. Resolves to
1009
+ * `{ messages }`, one message per card in the order of `contact_card_ids`.
1010
+ */
1011
+ sendContactCard(contact, body, options) {
1012
+ return this.client.request("POST", "/api/v1/contacts/{contact}/message/contact-card", { ...options, path: { contact }, body });
1013
+ }
1014
+ /**
1015
+ * `POST /api/v1/contacts/{contact}/messages/{message}/forward`: forwards a message to up to 5
1016
+ * contacts. Resolves to one item per destination, in order: `status` (`queued` or `blocked`, with
1017
+ * `reason`) and its `message`, or `null` when nothing was registered for that destination.
1018
+ */
1019
+ forward(contact, message, body, options) {
1020
+ return this.client.request("POST", "/api/v1/contacts/{contact}/messages/{message}/forward", {
1021
+ ...options,
1022
+ path: { contact, message },
1023
+ body
1024
+ });
1025
+ }
1026
+ /** `PUT /api/v1/contacts/{contact}/messages/{message}/reaction`: reacts with an emoji. */
1027
+ react(contact, message, body, options) {
1028
+ return this.client.request("PUT", "/api/v1/contacts/{contact}/messages/{message}/reaction", {
1029
+ ...options,
1030
+ path: { contact, message },
1031
+ body
1032
+ });
1033
+ }
1034
+ /** `DELETE /api/v1/contacts/{contact}/messages/{message}/reaction`: removes your reaction. */
1035
+ removeReaction(contact, message, options) {
1036
+ return this.client.request("DELETE", "/api/v1/contacts/{contact}/messages/{message}/reaction", {
1037
+ ...options,
1038
+ path: { contact, message }
1039
+ });
1040
+ }
1041
+ };
1042
+
1043
+ // src/resources/tags.ts
1044
+ var Tags = class extends Resource {
1045
+ /** `GET /api/v1/tags`, paginated. */
1046
+ list(query, options) {
1047
+ return this.client.paginate("/api/v1/tags", { ...options, query });
1048
+ }
1049
+ /** `POST /api/v1/tags`. */
1050
+ create(body, options) {
1051
+ return this.client.request("POST", "/api/v1/tags", { ...options, body });
1052
+ }
1053
+ /** `GET /api/v1/tags/{tag}`. */
1054
+ retrieve(tag, options) {
1055
+ return this.client.request("GET", "/api/v1/tags/{tag}", { ...options, path: { tag } });
1056
+ }
1057
+ /** `PUT /api/v1/tags/{tag}`. */
1058
+ update(tag, body, options) {
1059
+ return this.client.request("PUT", "/api/v1/tags/{tag}", { ...options, path: { tag }, body });
1060
+ }
1061
+ /** `POST /api/v1/tags/delete`: deletes the tags in `tagIds`. */
1062
+ delete(body, options) {
1063
+ return this.client.request("POST", "/api/v1/tags/delete", { ...options, body });
1064
+ }
1065
+ /** `GET /api/v1/tags/{tag}/contacts`, paginated: the contacts with a tag. */
1066
+ listContacts(tag, query, options) {
1067
+ return this.client.paginate("/api/v1/tags/{tag}/contacts", { ...options, path: { tag }, query });
1068
+ }
1069
+ };
1070
+
1071
+ // src/resources/templates.ts
1072
+ var Templates = class extends Resource {
1073
+ /** `GET /api/v1/templates`, paginated. */
1074
+ list(query, options) {
1075
+ return this.client.paginate("/api/v1/templates", { ...options, query });
1076
+ }
1077
+ /** `POST /api/v1/templates`: creates a template and submits it to Meta. */
1078
+ create(body, options) {
1079
+ return this.client.request("POST", "/api/v1/templates", { ...options, body });
1080
+ }
1081
+ /** `GET /api/v1/templates/{template}`. */
1082
+ retrieve(template, options) {
1083
+ return this.client.request("GET", "/api/v1/templates/{template}", { ...options, path: { template } });
1084
+ }
1085
+ /** `PUT /api/v1/templates/{template}`. */
1086
+ update(template, body, options) {
1087
+ return this.client.request("PUT", "/api/v1/templates/{template}", { ...options, path: { template }, body });
1088
+ }
1089
+ /** `GET /api/v1/templates/{template}/quick-reply-buttons`. */
1090
+ quickReplyButtons(template, options) {
1091
+ return this.client.request("GET", "/api/v1/templates/{template}/quick-reply-buttons", { ...options, path: { template } });
1092
+ }
1093
+ /**
1094
+ * `POST /api/v1/devices/{device}/templates/{template}/send-template`: sends an approved template
1095
+ * (the way to write to a contact outside the 24-hour window). `phone` and `contact_id` are
1096
+ * required; `template_variables.body` fills the body variables in order.
1097
+ *
1098
+ * Resolves to `{ message }`, the registered message with `status: 'queued'`. A variable that
1099
+ * points to a field that does not exist is a `422 template_unresolved_placeholder` with
1100
+ * `placeholders` and `count` in `APIError.body`; a template this account cannot send,
1101
+ * `404 template_not_found`. Like every send, it is never retried automatically.
1102
+ */
1103
+ send(device, template, body, options) {
1104
+ return this.client.request("POST", "/api/v1/devices/{device}/templates/{template}/send-template", {
1105
+ ...options,
1106
+ path: { device, template },
1107
+ body
1108
+ });
1109
+ }
1110
+ /** `DELETE /api/v1/devices/{device}/templates/{template}`. */
1111
+ delete(device, template, options) {
1112
+ return this.client.request("DELETE", "/api/v1/devices/{device}/templates/{template}", { ...options, path: { device, template } });
1113
+ }
1114
+ };
1115
+ var SIGNATURE_HEADER = "X-Funnelchat-Signature";
1116
+ var DEFAULT_TOLERANCE_SECONDS = 300;
1117
+ var fail = (reason, message) => ({ ok: false, reason, message });
1118
+ function toBytes(body) {
1119
+ if (typeof body === "string") return Buffer.from(body, "utf8");
1120
+ if (Buffer.isBuffer(body)) return body;
1121
+ if (body instanceof Uint8Array) return Buffer.from(body.buffer, body.byteOffset, body.byteLength);
1122
+ if (body instanceof ArrayBuffer) return Buffer.from(body);
1123
+ return null;
1124
+ }
1125
+ function checkSignature(rawBody, signatureHeader, secret, options = {}) {
1126
+ if (typeof secret !== "string" || secret === "") {
1127
+ return fail("missing_secret", "Missing the subscription secret.");
1128
+ }
1129
+ const body = toBytes(rawBody);
1130
+ if (body === null) {
1131
+ return fail(
1132
+ "invalid_body",
1133
+ 'The body must be the raw one (string, Buffer or Uint8Array), exactly as it arrived. With Express, read it with express.raw({ type: "application/json" }), not express.json().'
1134
+ );
1135
+ }
1136
+ if (typeof signatureHeader !== "string" || signatureHeader === "") {
1137
+ return fail("missing_header", `Missing the ${SIGNATURE_HEADER} header.`);
1138
+ }
1139
+ let t = null;
1140
+ const signatures = [];
1141
+ for (const part of signatureHeader.split(",")) {
1142
+ const i = part.indexOf("=");
1143
+ if (i === -1) continue;
1144
+ const key = part.slice(0, i).trim();
1145
+ const value = part.slice(i + 1).trim();
1146
+ if (key === "t") t = value;
1147
+ if (key === "v1") signatures.push(value);
1148
+ }
1149
+ if (t === null || !/^\d+$/.test(t) || signatures.length === 0) {
1150
+ return fail("malformed_header", `The ${SIGNATURE_HEADER} header is not t=<unix>,v1=<hex>.`);
1151
+ }
1152
+ const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
1153
+ if (Math.abs(Math.floor(Date.now() / 1e3) - Number(t)) > tolerance) {
1154
+ return fail("timestamp_out_of_tolerance", `The signature is more than ${tolerance} seconds away from the local clock.`);
1155
+ }
1156
+ const expected = Buffer.from(createHmac("sha256", secret).update(`${t}.`).update(body).digest("hex"), "utf8");
1157
+ const matched = signatures.some((v1) => {
1158
+ const candidate = Buffer.from(v1, "utf8");
1159
+ return candidate.length === expected.length && timingSafeEqual(candidate, expected);
1160
+ });
1161
+ return matched ? { ok: true } : fail("signature_mismatch", "The signature does not match the body and the secret.");
1162
+ }
1163
+ function verify(rawBody, signatureHeader, secret, options) {
1164
+ return checkSignature(rawBody, signatureHeader, secret, options).ok;
1165
+ }
1166
+ function assertSignature(rawBody, signatureHeader, secret, options) {
1167
+ const result = checkSignature(rawBody, signatureHeader, secret, options);
1168
+ if (!result.ok) throw new WebhookVerificationError(result.reason, result.message);
1169
+ }
1170
+
1171
+ // src/webhooks/events.ts
1172
+ function constructEvent(rawBody, signatureHeader, secret, options) {
1173
+ assertSignature(rawBody, signatureHeader, secret, options);
1174
+ const text = typeof rawBody === "string" ? rawBody : Buffer.from(rawBody instanceof ArrayBuffer ? new Uint8Array(rawBody) : rawBody).toString("utf8");
1175
+ let parsed;
1176
+ try {
1177
+ parsed = JSON.parse(text);
1178
+ } catch {
1179
+ throw new WebhookVerificationError("invalid_json", "The signature is valid but the body is not JSON.");
1180
+ }
1181
+ if (!isEnvelope(parsed)) {
1182
+ throw new WebhookVerificationError("invalid_event", "The signature is valid but the body is not an event (id, type, data).");
1183
+ }
1184
+ return parsed;
1185
+ }
1186
+ function isEnvelope(value) {
1187
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
1188
+ const v = value;
1189
+ return typeof v.id === "string" && typeof v.type === "string" && "data" in v;
1190
+ }
1191
+
1192
+ // src/resources/webhooks.ts
1193
+ var Webhooks = class extends Resource {
1194
+ /** Same as `Funnelchat.webhooks.verify`. */
1195
+ verify = verify;
1196
+ /** Same as `Funnelchat.webhooks.constructEvent`. */
1197
+ constructEvent = constructEvent;
1198
+ /** `GET /api/v1/webhooks/events`: the event types you can subscribe to, each with `status`. */
1199
+ events(options) {
1200
+ return this.client.request("GET", "/api/v1/webhooks/events", { ...options });
1201
+ }
1202
+ /** `GET /api/v1/webhooks`: every subscription of the account (at most 50), not only yours. */
1203
+ list(options) {
1204
+ return this.client.request("GET", "/api/v1/webhooks", { ...options });
1205
+ }
1206
+ /** `POST /api/v1/webhooks`: the response brings `secret` once. Store it, and store the `id`. */
1207
+ create(body, options) {
1208
+ return this.client.request("POST", "/api/v1/webhooks", { ...options, body });
1209
+ }
1210
+ /** `GET /api/v1/webhooks/{id}` (without the secret). */
1211
+ retrieve(id, options) {
1212
+ return this.client.request("GET", "/api/v1/webhooks/{id}", { ...options, path: { id } });
1213
+ }
1214
+ /** `PUT /api/v1/webhooks/{id}`: replaces `url`, `events`, `description` and `filters`. The secret stays. */
1215
+ update(id, body, options) {
1216
+ return this.client.request("PUT", "/api/v1/webhooks/{id}", { ...options, path: { id }, body });
1217
+ }
1218
+ /** `DELETE /api/v1/webhooks/{id}`. */
1219
+ delete(id, options) {
1220
+ return this.client.request("DELETE", "/api/v1/webhooks/{id}", { ...options, path: { id } });
1221
+ }
1222
+ /**
1223
+ * `POST /api/v1/webhooks/{id}/rotate-secret`: the new secret comes once; the old one stops
1224
+ * working. **Not open to API keys** (`403 route_not_available`): rotate it from the panel.
1225
+ */
1226
+ rotateSecret(id, options) {
1227
+ return this.client.request("POST", "/api/v1/webhooks/{id}/rotate-secret", { ...options, path: { id } });
1228
+ }
1229
+ /** `POST /api/v1/webhooks/{id}/test`: delivers one signed `webhook.test` now. Check `succeeded`. */
1230
+ test(id, options) {
1231
+ return this.client.request("POST", "/api/v1/webhooks/{id}/test", { ...options, path: { id } });
1232
+ }
1233
+ /** `GET /api/v1/webhooks/{id}/deliveries`, paginated: one row per attempt, newest first. */
1234
+ listDeliveries(id, query, options) {
1235
+ return this.client.paginate("/api/v1/webhooks/{id}/deliveries", { ...options, path: { id }, query });
1236
+ }
1237
+ };
1238
+
1239
+ // src/version.ts
1240
+ var VERSION = "0.1.2";
1241
+
1242
+ // src/client.ts
1243
+ var BASE_URLS = {
1244
+ production: "https://api.funnelchat.com",
1245
+ staging: "https://api-staging.funnelchat.com"
1246
+ };
1247
+ var DEFAULT_TIMEOUT_MS = 3e4;
1248
+ var DEFAULT_MAX_RETRIES = 2;
1249
+ var webhookHelpers = { verify, constructEvent };
1250
+ var Funnelchat = class {
1251
+ /** `Funnelchat.webhooks.verify(...)` and `Funnelchat.webhooks.constructEvent(...)`, without a client. */
1252
+ static webhooks = webhookHelpers;
1253
+ static VERSION = VERSION;
1254
+ baseUrl;
1255
+ timeoutMs;
1256
+ maxRetries;
1257
+ contacts;
1258
+ conversations;
1259
+ messages;
1260
+ /** WhatsApp forms: build, publish, read the answers. Send one with `messages.sendForm`. */
1261
+ forms;
1262
+ templates;
1263
+ tags;
1264
+ customFields;
1265
+ groups;
1266
+ devices;
1267
+ /** Webhook subscriptions, plus `verify` and `constructEvent`. */
1268
+ webhooks;
1269
+ // Private fields: the key never shows up in `console.log(client)` or in JSON.
1270
+ #apiKey;
1271
+ #fetch;
1272
+ #defaultHeaders;
1273
+ constructor(options = {}) {
1274
+ const apiKey = options.apiKey ?? readEnv("FUNNELCHAT_API_KEY");
1275
+ if (!apiKey) {
1276
+ throw new FunnelchatError("Missing API key: pass it as `apiKey` or in the FUNNELCHAT_API_KEY environment variable.");
1277
+ }
1278
+ const environment = options.environment ?? "production";
1279
+ if (!(environment in BASE_URLS)) {
1280
+ throw new FunnelchatError(`Unknown environment: ${String(environment)}. Use "production" or "staging".`);
1281
+ }
1282
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1283
+ if (typeof fetchImpl !== "function") {
1284
+ throw new FunnelchatError("No fetch available: use Node 18 or later, or pass `fetch` in the options.");
1285
+ }
1286
+ this.#apiKey = apiKey;
1287
+ this.#fetch = fetchImpl;
1288
+ this.#defaultHeaders = { ...options.defaultHeaders };
1289
+ this.baseUrl = (options.baseUrl ?? BASE_URLS[environment]).replace(/\/+$/, "");
1290
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
1291
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
1292
+ this.contacts = new Contacts(this);
1293
+ this.conversations = new Conversations(this);
1294
+ this.messages = new Messages(this);
1295
+ this.forms = new Forms(this);
1296
+ this.templates = new Templates(this);
1297
+ this.tags = new Tags(this);
1298
+ this.customFields = new CustomFields(this);
1299
+ this.groups = new Groups(this);
1300
+ this.devices = new Devices(this);
1301
+ this.webhooks = new Webhooks(this);
1302
+ }
1303
+ /** What `JSON.stringify(client)` shows: the configuration, never the key. */
1304
+ toJSON() {
1305
+ return { baseUrl: this.baseUrl, timeoutMs: this.timeoutMs, maxRetries: this.maxRetries };
1306
+ }
1307
+ request(method, path, options) {
1308
+ return new APIPromise(this.#send(method, path, options ?? {}));
1309
+ }
1310
+ paginate(path, options) {
1311
+ const { query, ...rest } = options ?? {};
1312
+ return new PagePromise((pageQuery) => this.#send("GET", path, { ...rest, query: pageQuery }), { ...query });
1313
+ }
1314
+ async #send(method, template, options) {
1315
+ const verb = method.toUpperCase();
1316
+ const pathname = interpolatePath(template, options.path);
1317
+ const url = joinUrl(this.baseUrl, pathname, serializeQuery(options.query));
1318
+ const errorUrl = joinUrl(this.baseUrl, pathname.split("?")[0] ?? pathname, "");
1319
+ const maxRetries = Math.max(0, options.maxRetries ?? this.maxRetries);
1320
+ const retriesAllowed = options.retrySend === true || !isSendRequest(verb, pathname.split("?")[0] ?? pathname);
1321
+ const timeoutMs = options.timeoutMs ?? this.timeoutMs;
1322
+ const { body, contentType } = encodeBody(options.body);
1323
+ const headers = {
1324
+ Accept: "application/json",
1325
+ "User-Agent": `funnelchat-node/${VERSION} node/${typeof process !== "undefined" ? process.version : "unknown"}`,
1326
+ ...contentType ? { "Content-Type": contentType } : {},
1327
+ ...this.#defaultHeaders,
1328
+ ...options.headers,
1329
+ Authorization: `Bearer ${this.#apiKey}`
1330
+ };
1331
+ for (let attempt = 0; ; attempt++) {
1332
+ const canRetry = retriesAllowed && attempt < maxRetries;
1333
+ let result;
1334
+ try {
1335
+ result = await this.#attempt(verb, url, headers, body, timeoutMs, options.signal);
1336
+ } catch (error2) {
1337
+ if (error2 instanceof APIConnectionError && canRetry && isRetryable(verb, "network")) {
1338
+ await sleep(retryDelayMs(attempt, void 0) ?? 0, options.signal);
1339
+ continue;
1340
+ }
1341
+ throw error2;
1342
+ }
1343
+ const { response, text } = result;
1344
+ const parsed = parseBody(response, text);
1345
+ if (response.ok) return { data: parsed, response, requestId: requestIdOf(response.headers) };
1346
+ const error = APIError.from({ status: response.status, body: parsed, headers: response.headers, method: verb, url: errorUrl });
1347
+ if (canRetry && isRetryable(verb, response.status) && !NOT_RETRYABLE_CODES.has(error.code ?? "")) {
1348
+ const delay = retryDelayMs(attempt, error.retryAfter);
1349
+ if (delay !== null) {
1350
+ await sleep(delay, options.signal);
1351
+ continue;
1352
+ }
1353
+ }
1354
+ throw error;
1355
+ }
1356
+ }
1357
+ async #attempt(method, url, headers, body, timeoutMs, signal) {
1358
+ if (signal?.aborted) throw new APIUserAbortError({ cause: signal.reason });
1359
+ const controller = new AbortController();
1360
+ const onAbort = () => controller.abort(signal?.reason);
1361
+ signal?.addEventListener("abort", onAbort, { once: true });
1362
+ let timedOut = false;
1363
+ const timer = setTimeout(() => {
1364
+ timedOut = true;
1365
+ controller.abort();
1366
+ }, timeoutMs);
1367
+ const classify = (cause) => {
1368
+ if (signal?.aborted) return new APIUserAbortError({ cause });
1369
+ if (timedOut) return new APIConnectionTimeoutError(timeoutMs, { cause });
1370
+ return new APIConnectionError(void 0, { cause });
1371
+ };
1372
+ try {
1373
+ let response;
1374
+ try {
1375
+ response = await this.#fetch(url, { method, headers, body, signal: controller.signal });
1376
+ } catch (error) {
1377
+ throw classify(error);
1378
+ }
1379
+ try {
1380
+ return { response, text: await response.text() };
1381
+ } catch (error) {
1382
+ throw classify(error);
1383
+ }
1384
+ } finally {
1385
+ clearTimeout(timer);
1386
+ signal?.removeEventListener("abort", onAbort);
1387
+ }
1388
+ }
1389
+ };
1390
+ function readEnv(name) {
1391
+ if (typeof process === "undefined" || !process.env) return void 0;
1392
+ const value = process.env[name];
1393
+ return value && value.trim() !== "" ? value.trim() : void 0;
1394
+ }
1395
+ function encodeBody(body) {
1396
+ if (body === void 0) return { body: void 0, contentType: void 0 };
1397
+ if (typeof FormData !== "undefined" && body instanceof FormData) return { body, contentType: void 0 };
1398
+ if (body instanceof URLSearchParams) return { body, contentType: void 0 };
1399
+ if (typeof body === "string") return { body, contentType: "application/json" };
1400
+ return { body: JSON.stringify(body), contentType: "application/json" };
1401
+ }
1402
+ function parseBody(response, text) {
1403
+ if (text === "") return null;
1404
+ const type = response.headers.get("content-type") ?? "";
1405
+ if (type.includes("json") || /^\s*[[{]/.test(text)) {
1406
+ try {
1407
+ return JSON.parse(text);
1408
+ } catch {
1409
+ return text;
1410
+ }
1411
+ }
1412
+ return text;
1413
+ }
1414
+ function sleep(ms, signal) {
1415
+ return new Promise((resolve, reject) => {
1416
+ if (signal?.aborted) {
1417
+ reject(new APIUserAbortError({ cause: signal.reason }));
1418
+ return;
1419
+ }
1420
+ const onAbort = () => {
1421
+ clearTimeout(timer);
1422
+ reject(new APIUserAbortError({ cause: signal?.reason }));
1423
+ };
1424
+ const timer = setTimeout(() => {
1425
+ signal?.removeEventListener("abort", onAbort);
1426
+ resolve();
1427
+ }, ms);
1428
+ signal?.addEventListener("abort", onAbort, { once: true });
1429
+ });
1430
+ }
1431
+
1432
+ // src/generated/events.ts
1433
+ var webhookEvents = [
1434
+ { "type": "account.blocked", "domains": ["accounts"], "status": "live", "pending": true, "description": "The account was blocked by usage: the 24-hour grace period after using up the monthly active contacts ended." },
1435
+ { "type": "ai_quota.low_balance", "domains": ["accounts"], "status": "planned", "pending": true, "description": "The AI message quota is about to run out. Planned: not emitted yet." },
1436
+ { "type": "ai_quota.topup_failed", "domains": ["accounts"], "status": "planned", "pending": true, "description": "An AI quota top-up could not be processed. Planned: not emitted yet." },
1437
+ { "type": "ai_quota.topup_succeeded", "domains": ["accounts"], "status": "planned", "pending": true, "description": "An AI quota top-up was credited. Planned: not emitted yet." },
1438
+ { "type": "billing.payment_failed", "domains": ["accounts"], "status": "live", "pending": true, "description": "A charge of the subscription could not be processed (Stripe subscriptions only)." },
1439
+ { "type": "billing.plan_blocked", "domains": ["accounts"], "status": "planned", "pending": true, "description": "The plan was blocked by an unprocessed charge. Planned: not emitted yet." },
1440
+ { "type": "billing.upcoming_charge", "domains": ["accounts"], "status": "live", "pending": true, "description": "The next charge of the subscription is coming up (Stripe subscriptions only)." },
1441
+ { "type": "broadcast.finished", "domains": ["conversations"], "status": "live", "pending": false, "description": "Una campa\xF1a lleg\xF3 a su estado final (enviada, fallida o con fallos parciales), con sus conteos.\n\n`status` es `success`, `failed` o `partially_failed`. `reason` dice por qu\xE9 termin\xF3: `completed` (se enviaron todas las p\xE1ginas), `cancelled` (se cerr\xF3 una campa\xF1a pausada), `template_rejected` (Meta rechaz\xF3 la plantilla, o su categor\xEDa cambi\xF3, mientras la campa\xF1a esperaba), `approval_expired` (la plantilla no se aprob\xF3 a tiempo), `mac_lockout` (la cuenta alcanz\xF3 su l\xEDmite de contactos activos), `no_recipients` (la audiencia qued\xF3 vac\xEDa) o `error` (el env\xEDo no pudo despacharse). `totals` son los conteos del env\xEDo: `processed` (destinatarios procesados), `failed` y `skipped_unreachable` (destinatarios que el canal no puede alcanzar); valen 0 si la campa\xF1a termin\xF3 antes de enviar. Se emite una sola vez por campa\xF1a." },
1442
+ { "type": "broadcast.paused", "domains": ["conversations"], "status": "live", "pending": false, "description": "Una campa\xF1a se paus\xF3 sola, por ejemplo porque baj\xF3 la calidad del n\xFAmero.\n\n`reason` es `quality_degraded` (la calidad del n\xFAmero baj\xF3 respecto de la del inicio, o lleg\xF3 a RED) o `quality_unreadable` (no se pudo leer la calidad en tres controles seguidos). `quality` y `quality_baseline` son la calidad de Meta le\xEDda en el control y la del inicio del env\xEDo (`GREEN`, `YELLOW`, `RED`), o `null` si no se pudo leer. S\xF3lo las campa\xF1as con env\xEDo inteligente se pausan solas. Reanudar la campa\xF1a y que se pause otra vez emite otro evento." },
1443
+ { "type": "campaign.no_groups_available", "domains": ["communities"], "status": "live", "pending": true, "description": "A campaign has no groups to send the people who come through its link to." },
1444
+ { "type": "campaign.status_changed", "domains": ["communities"], "status": "live", "pending": false, "description": "Una campa\xF1a se activ\xF3 o se paus\xF3." },
1445
+ { "type": "contact.created", "domains": ["conversations"], "status": "live", "pending": false, "description": "Se cre\xF3 un contacto nuevo en la cuenta.\n\nSe env\xEDa un evento por cada contacto nuevo, sea cual sea el canal: un mensaje entrante, la creaci\xF3n manual, una importaci\xF3n o la API. No se env\xEDa cuando el contacto ya exist\xEDa y s\xF3lo se actualiza, ni para los contactos que la plataforma crea para sus propias notificaciones. En importaciones grandes el l\xEDmite de volumen de la cuenta puede descartar parte de los eventos." },
1446
+ { "type": "contact.stage_changed", "domains": ["conversations"], "status": "live", "pending": false, "description": "Un contacto cambi\xF3 de etapa en un tablero.\n\nSe env\xEDa un evento por cada contacto que cambia de etapa en un tablero por etapas. `kind` es entry (entra al tablero, `from_stage` es null), move (pasa de una etapa a otra), exit (sale del tablero, `to_stage` es null) o stage_deleted (se reubica porque su etapa se borr\xF3; el nombre de la etapa borrada es null). No se env\xEDa si el contacto se coloca en la etapa en la que ya estaba." },
1447
+ { "type": "contact.tag_added", "domains": ["conversations"], "status": "live", "pending": false, "description": "Un contacto recibi\xF3 una etiqueta.\n\nSe env\xEDa un evento por cada contacto y etiqueta agregados, tambi\xE9n en operaciones en bloque; no se env\xEDa si el contacto ya ten\xEDa la etiqueta. `source` indica el origen: manual, flow, bulk, import, sync, walink o ai. En operaciones en bloque grandes el l\xEDmite de volumen de la cuenta puede descartar parte de los eventos." },
1448
+ { "type": "contact.tag_removed", "domains": ["conversations"], "status": "live", "pending": false, "description": "Un contacto perdi\xF3 una etiqueta.\n\nSe env\xEDa un evento por cada contacto y etiqueta quitados, tambi\xE9n en operaciones en bloque; no se env\xEDa si el contacto no ten\xEDa la etiqueta. No se env\xEDa al borrar la etiqueta, al borrar o fusionar contactos ni al desvincular etiquetas de un tablero. `source` indica el origen: manual, flow o bulk. En operaciones en bloque grandes el l\xEDmite de volumen de la cuenta puede descartar parte de los eventos." },
1449
+ { "type": "conversation.assigned", "domains": ["conversations"], "status": "live", "pending": false, "description": "Una conversaci\xF3n qued\xF3 asignada a una persona del equipo o a un agente de IA.\n\nSe env\xEDa cuando una conversaci\xF3n queda asignada a una persona del equipo (`assignee.type` = `human`) o a un agente de IA (`assignee.type` = `ai`), por cualquier camino. `assignee` trae el tipo y el nombre, nunca un identificador. No se env\xEDa al desasignar, ni cuando se vuelve a enviar el mismo asignado." },
1450
+ { "type": "conversation.closed", "domains": ["conversations"], "status": "live", "pending": false, "description": "Una conversaci\xF3n pas\xF3 a cerrada.\n\nSe env\xEDa cuando una conversaci\xF3n pasa de abierta a cerrada. No se env\xEDa si la conversaci\xF3n ya estaba cerrada ni cuando se reabre." },
1451
+ { "type": "daily_report.generated", "domains": ["communities"], "status": "live", "pending": true, "description": "The daily report of the account was generated (clicks, joins and leaves)." },
1452
+ { "type": "device.connected", "domains": ["accounts"], "status": "live", "pending": true, "description": "One of the numbers of the account got connected: by scanning the QR code or by finishing the sign-up of a WhatsApp Cloud number." },
1453
+ { "type": "device.disconnected", "domains": ["accounts"], "status": "live", "pending": true, "description": "One of the numbers of the account disconnected and can no longer send or receive." },
1454
+ { "type": "flow.completed", "domains": ["conversations"], "status": "live", "pending": false, "description": "Un contacto termin\xF3 un flujo de automatizaci\xF3n.\n\nSe env\xEDa una sola vez por cada ejecuci\xF3n de un flujo, cuando termina con \xE9xito. No se env\xEDa si la ejecuci\xF3n se cort\xF3, se cancel\xF3, qued\xF3 bloqueada o fall\xF3. `contact` es `null` si la ejecuci\xF3n no tiene un contacto asociado. Un flujo muy usado puede generar muchos eventos seguidos." },
1455
+ { "type": "flow.failed", "domains": ["conversations"], "status": "live", "pending": false, "description": "La ejecuci\xF3n de un flujo para un contacto termin\xF3 por un error.\n\nSe env\xEDa una sola vez por cada ejecuci\xF3n de un flujo que termina por un error, no porque lleg\xF3 al final. `reason` indica el motivo y toma uno de estos valores: `node_missing` (el paso a ejecutar ya no existe), `unknown_node_type` (el paso es de un tipo no reconocido), `missing_subflow` (el flujo que deb\xEDa ejecutarse dentro de otro ya no existe), `malformed_node` (el paso tiene una configuraci\xF3n da\xF1ada), `loop_detected` (la ejecuci\xF3n dio vueltas sin esperar nada) y `execution_error` (un error inesperado detuvo la ejecuci\xF3n). No se env\xEDa si la ejecuci\xF3n se cancel\xF3, qued\xF3 bloqueada o se cort\xF3 porque se edit\xF3 el flujo o se unieron contactos. `contact` es `null` si la ejecuci\xF3n no tiene un contacto asociado." },
1456
+ { "type": "group_action.failed", "domains": ["communities"], "status": "live", "pending": true, "description": "An action on a group of a campaign failed." },
1457
+ { "type": "group_join_request.created", "domains": ["communities"], "status": "live", "pending": true, "description": "Someone asked to join a group that requires approval." },
1458
+ { "type": "group_join_request.revoked", "domains": ["communities"], "status": "live", "pending": true, "description": "Someone withdrew their request to join a group." },
1459
+ { "type": "group_participant.joined", "domains": ["communities"], "status": "live", "pending": false, "description": "Una persona entr\xF3 a un grupo de una campa\xF1a." },
1460
+ { "type": "group_participant.left", "domains": ["communities"], "status": "live", "pending": false, "description": "Una persona sali\xF3 de un grupo de una campa\xF1a." },
1461
+ { "type": "group.blocked", "domains": ["communities"], "status": "live", "pending": false, "description": "Un grupo qued\xF3 inoperable: sin administrador conectado, con el link de invitaci\xF3n inv\xE1lido o suspendido por WhatsApp." },
1462
+ { "type": "group.created", "domains": ["communities"], "status": "live", "pending": false, "description": "Se cre\xF3 o se import\xF3 un grupo dentro de una campa\xF1a de la cuenta." },
1463
+ { "type": "group.full", "domains": ["communities"], "status": "live", "pending": true, "description": "A group went from not full to full." },
1464
+ { "type": "group.invite_link_down", "domains": ["communities"], "status": "live", "pending": false, "description": "El enlace de invitaci\xF3n de un grupo qued\xF3 confirmado ca\xEDdo (WhatsApp respondi\xF3 que no existe y la renovaci\xF3n no consigui\xF3 otro); no se publica por errores transitorios ni por grupos prohibidos o suspendidos, y se publica una sola vez por episodio de bloqueo." },
1465
+ { "type": "group.invite_link_renewed", "domains": ["communities"], "status": "live", "pending": false, "description": "El enlace de invitaci\xF3n de un grupo cambi\xF3 por una renovaci\xF3n, autom\xE1tica al detectarse inv\xE1lido o pedida por el usuario; en v0 no se publican los cambios que detecta la sincronizaci\xF3n ni el enlace inicial al crear el grupo." },
1466
+ { "type": "group.message_received", "domains": ["communities"], "status": "live", "pending": true, "description": "Someone wrote in a group of one of your numbers. Not emitted for the messages of your own numbers." },
1467
+ { "type": "group.reaction_added", "domains": ["communities"], "status": "live", "pending": true, "description": "Someone reacted to a message of a group." },
1468
+ { "type": "group.recovered", "domains": ["communities"], "status": "live", "pending": false, "description": "Un grupo que estaba bloqueado volvi\xF3 a operar." },
1469
+ { "type": "mac.expiring", "domains": ["accounts"], "status": "live", "pending": true, "description": "Some monthly active contacts expire in the next 7 days." },
1470
+ { "type": "mac.usage_100", "domains": ["accounts"], "status": "live", "pending": true, "description": "The account used up its monthly active contacts of the cycle; a 24-hour grace period starts." },
1471
+ { "type": "mac.usage_80", "domains": ["accounts"], "status": "live", "pending": true, "description": "The account reached 80% of its monthly active contacts of the cycle." },
1472
+ { "type": "message.received", "domains": ["conversations"], "status": "live", "pending": false, "description": "Un contacto le escribi\xF3 a un n\xFAmero de WhatsApp de la cuenta." },
1473
+ { "type": "message.status_changed", "domains": ["conversations"], "status": "live", "pending": true, "description": "The delivery status of a message you sent with the API changed. One notice per transition, never one that goes back; `queued` is not reported. It must be asked for explicitly in `events`." },
1474
+ { "type": "pipeline.finished", "domains": ["communities"], "status": "live", "pending": false, "description": "Termin\xF3 un pipeline de operaciones sobre grupos, con su resultado agregado." },
1475
+ { "type": "poll.voted", "domains": ["communities"], "status": "live", "pending": false, "description": "Un participante vot\xF3 en una encuesta enviada a un grupo, o cambi\xF3 su voto." },
1476
+ { "type": "scheduled_message.finished", "domains": ["communities"], "status": "live", "pending": false, "description": "Un env\xEDo programado termin\xF3, con su resultado agregado." },
1477
+ { "type": "scheduled_message.group_delivery_failed", "domains": ["communities"], "status": "live", "pending": false, "description": "Un env\xEDo programado fall\xF3 en un grupo concreto, con el motivo desagregado." },
1478
+ { "type": "scheduled_message.response_received", "domains": ["communities"], "status": "live", "pending": false, "description": "Alguien respondi\xF3 a un env\xEDo programado votando una encuesta o respondiendo a un evento." },
1479
+ { "type": "subscription.cancellation_confirmed", "domains": ["accounts"], "status": "live", "pending": true, "description": "The cancellation of the subscription was confirmed (Stripe subscriptions only)." },
1480
+ { "type": "subscription.dormant_ending", "domains": ["accounts"], "status": "live", "pending": true, "description": "The dormant-account period is about to end." },
1481
+ { "type": "subscription.expiring", "domains": ["accounts"], "status": "planned", "pending": true, "description": "The subscription is about to expire. Planned: not emitted yet." },
1482
+ { "type": "subscription.reactivated", "domains": ["accounts"], "status": "live", "pending": true, "description": "The scheduled cancellation of the subscription was reverted (Stripe subscriptions only)." },
1483
+ { "type": "subscription.trial_ending", "domains": ["accounts"], "status": "live", "pending": true, "description": "The trial period is about to end." },
1484
+ { "type": "template.status_changed", "domains": ["conversations"], "status": "live", "pending": false, "description": "Una plantilla cambi\xF3 de estado de aprobaci\xF3n en Meta.\n\n`template` identifica la plantilla igual que `GET /templates` (`id`, `uid`, `name`, `category`). `previous_status` y `status` son los estados tal como los devuelve la API (por ejemplo `PENDING`, `APPROVED`, `REJECTED`, `PAUSED`); `previous_status` es `null` si la plantilla no ten\xEDa estado. `reason` trae la observaci\xF3n de rechazo (traducida como en la API y limitada a 500 caracteres) cuando el cambio es un rechazo informado al crear la plantilla, y es `null` en el resto. No se emite si Meta reenv\xEDa el estado que la plantilla ya ten\xEDa. Si una misma plantilla de Meta figura en varias cuentas, cada cuenta recibe su propio evento." },
1485
+ { "type": "webhook.test", "domains": ["conversations", "communities"], "status": "live", "pending": false, "description": "Evento de prueba enviado desde la ruta de prueba de una suscripci\xF3n." },
1486
+ { "type": "whatsapp_form.completed", "domains": ["conversations"], "status": "live", "pending": true, "description": "A contact completed a form you sent them." }
1487
+ ];
1488
+
1489
+ export { ACCESS_ERROR_CODES, APIConnectionError, APIConnectionTimeoutError, APIError, APIPromise, APIUserAbortError, AuthenticationError, BASE_URLS, BadRequestError, ConflictError, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT_MS, DEFAULT_TOLERANCE_SECONDS, Funnelchat, FunnelchatError, InternalServerError, NotFoundError, Page, PagePromise, PermissionDeniedError, REQUEST_ID_HEADER, RateLimitError, SEND_ERROR_CODES, SIGNATURE_HEADER, UnprocessableEntityError, UnsupportedMediaTypeError, VERSION, WebhookVerificationError, constructEvent, Funnelchat as default, isSendRuleError, routes, verify, webhookEvents };
1490
+ //# sourceMappingURL=index.js.map
1491
+ //# sourceMappingURL=index.js.map