@funnelchat-hq/sdk 0.2.0 → 0.2.1
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/README.md +9 -9
- package/dist/index.cjs +7 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -18
- package/dist/index.d.ts +27 -18
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npm install @funnelchat-hq/sdk
|
|
|
16
16
|
|
|
17
17
|
## First call
|
|
18
18
|
|
|
19
|
-
You need an API key (`<id>|fc_live_...`) created in the "Developers" section of the panel, with the permissions of what you are going to use. Keep it in an environment variable: the client reads `FUNNELCHAT_API_KEY` when you do not pass it.
|
|
19
|
+
You need an API key (`<id>|fc_live_...`) created in the "Developers" section of the panel, with the permissions of what you are going to use. The panel groups them by area, with one checkbox per level: this README names each one as the panel shows it, with its literal in parentheses, such as **WhatsApp numbers → View** (`accounts:read`). The literal is what the panel shows as a tooltip and what the API names in its errors. In the panel, **Send** includes **View** and **Edit**, and **Edit** includes **View**. Keep it in an environment variable: the client reads `FUNNELCHAT_API_KEY` when you do not pass it.
|
|
20
20
|
|
|
21
21
|
```ts
|
|
22
22
|
import { Funnelchat } from '@funnelchat-hq/sdk';
|
|
@@ -24,7 +24,7 @@ import { Funnelchat } from '@funnelchat-hq/sdk';
|
|
|
24
24
|
|
|
25
25
|
const funnelchat = new Funnelchat({ apiKey: process.env.FUNNELCHAT_API_KEY });
|
|
26
26
|
|
|
27
|
-
// Your WhatsApp numbers (
|
|
27
|
+
// Your WhatsApp numbers. Permission: WhatsApp numbers → View (accounts:read). The `id` of each one is the `device_id` you send with.
|
|
28
28
|
for await (const device of funnelchat.devices.list()) {
|
|
29
29
|
console.log(device.id, device.number, device.capabilities.messages.templates);
|
|
30
30
|
}
|
|
@@ -64,7 +64,7 @@ Parameters, bodies and responses are typed from the spec. For any other route, s
|
|
|
64
64
|
|
|
65
65
|
## Send a message
|
|
66
66
|
|
|
67
|
-
Sending with a key needs
|
|
67
|
+
Sending with a key needs **Contacts and conversations → Send** (`conversations:send`) and follows the WhatsApp rules. **Every send answers right away with the registered message, `status: 'queued'`:** accepted is not delivered. What happens next arrives by the `message.status_changed` webhook, or is read with `messages.get`.
|
|
68
68
|
|
|
69
69
|
**Open window: free text.** With an official Meta number you can only send free text if the contact wrote to you in the last 24 hours. Check the window first:
|
|
70
70
|
|
|
@@ -133,7 +133,7 @@ const { message } = await funnelchat.templates.send(3, '45', {
|
|
|
133
133
|
A WhatsApp form is built, published on one or more numbers and sent to a contact. There are two ids: `id` is one version; `family_id` is the form itself, the one you send it with and the one that comes back as `form.id` in `whatsapp_form.completed`.
|
|
134
134
|
|
|
135
135
|
```ts
|
|
136
|
-
const { data: draft } = await funnelchat.forms.create({ device_id: 3, template_key: 'lead' }); //
|
|
136
|
+
const { data: draft } = await funnelchat.forms.create({ device_id: 3, template_key: 'lead' }); // Contacts and conversations → Edit (conversations:write)
|
|
137
137
|
await funnelchat.forms.publish(draft.id);
|
|
138
138
|
|
|
139
139
|
const { message } = await funnelchat.messages.sendForm(2001, {
|
|
@@ -150,7 +150,7 @@ The answers arrive by the `whatsapp_form.completed` webhook (`answers`: `key`, `
|
|
|
150
150
|
|
|
151
151
|
> **In production, except the official sign-up by link.** Creating QR numbers and connect links by QR work in production. Connect links with `type: 'meta'` are not available in production yet: they answer `503 meta_connect_disabled` until the Funnelchat domain is registered with Meta. The published spec does not describe these routes yet; see [Pending of the spec](#pending-of-the-spec).
|
|
152
152
|
|
|
153
|
-
A number belongs to the account of the key and takes a slot of its plan, as in the panel. Creating numbers, showing their QR code and creating or revoking connect links needs
|
|
153
|
+
A number belongs to the account of the key and takes a slot of its plan, as in the panel. Creating numbers, showing their QR code and creating or revoking connect links needs **WhatsApp numbers → Create, connect and delete** (`accounts:operate`); reading links, **WhatsApp numbers → View** (`accounts:read`). Tag each number with your own `external_reference` (for example the id of your customer): it comes back in the number, in the links and in every `device.*` webhook.
|
|
154
154
|
|
|
155
155
|
**A connect link** is a single-use page on the Funnelchat domain. Send your customer there: the number is created when they finish.
|
|
156
156
|
|
|
@@ -205,7 +205,7 @@ for await (const number of funnelchat.devices.list({ external_reference: 'custom
|
|
|
205
205
|
- `country` (two letters, optional) is the country of the phone that will scan the QR: the WhatsApp session is opened from there.
|
|
206
206
|
- An account has at most one QR number waiting to be scanned: creating another answers `409 pending_number_exists`, with that number's `device_id` in `error.body`. Show its QR instead, or delete it.
|
|
207
207
|
- `external_reference` changes with `devices.update(id, { external_reference })`.
|
|
208
|
-
- `devices.delete(id)` deletes a number and frees its slot (`accounts:operate
|
|
208
|
+
- `devices.delete(id)` deletes a number and frees its slot. It needs **WhatsApp numbers → Create, connect and delete** (`accounts:operate`) and is irreversible. An official number stops working through Funnelchat but stays registered with Meta; a QR one logs out. It sends `device.deleted`.
|
|
209
209
|
- `devices.list(query, options)` takes the query first, like every list. The signature of 0.1.2, `devices.list(options)`, still works.
|
|
210
210
|
|
|
211
211
|
**`idempotencyKey`** (both creates, 1 to 255 characters) is sent as `Idempotency-Key` and kept 24 hours. The same key with the same body answers the same response without creating another number or link, so after a timeout or a `5xx` you can repeat the call with it. Only a success is replayed, with the `Idempotent-Replayed: true` header (see it with `.withResponse()`); a rejection is evaluated again. On their own, creates follow the rule of writes (see [Retries](#retries)): they are retried only on a `429` or `503`, which guarantee nothing was created.
|
|
@@ -306,7 +306,7 @@ const subscription = await funnelchat.webhooks.create({
|
|
|
306
306
|
// Store subscription.id and subscription.secret: the secret is shown only once.
|
|
307
307
|
```
|
|
308
308
|
|
|
309
|
-
- Permissions: `webhooks:read` to read and `webhooks:write` for the rest. Also, a key can only subscribe to events of the areas it can read (`conversations:read
|
|
309
|
+
- Permissions: **Notifications (webhooks) → View** (`webhooks:read`) to read and **Notifications (webhooks) → Manage** (`webhooks:write`) for the rest. Also, a key can only subscribe to events of the areas it can read: **Contacts and conversations → View** (`conversations:read`), **Groups and campaigns → View** (`communities:read`) and **WhatsApp numbers → View** (`accounts:read`). Asking for others is a `422` on `events`.
|
|
310
310
|
- Subscriptions belong to the account: `list()` also brings the ones of other integrations. To change the URL use `update(id, …)` (the secret stays). `test(id)` sends a signed `webhook.test` right now and `listDeliveries(id)` is the history.
|
|
311
311
|
- `rotateSecret(id)` is **not open to API keys** (`403 route_not_available`): rotate the secret from the panel.
|
|
312
312
|
|
|
@@ -367,7 +367,7 @@ The errors of the API entry and of access control apply to any route (`ACCESS_ER
|
|
|
367
367
|
| Error | What happened |
|
|
368
368
|
|---|---|
|
|
369
369
|
| `401 unauthenticated` | The key is missing or not valid. |
|
|
370
|
-
| `403 permission_denied` | The key lacks the permission of the route. |
|
|
370
|
+
| `403 permission_denied` | The key lacks the permission of the route. `message` names it by its literal (such as `accounts:operate`), which the panel shows as the tooltip of each checkbox. |
|
|
371
371
|
| `403 route_not_available` | The route is not open to API keys (or it is turned off). |
|
|
372
372
|
| `404 route_not_found` | The route does not exist in the public API. |
|
|
373
373
|
| `429 daily_cap_exceeded` | The daily call cap of the key was reached. It carries `Retry-After`. |
|
|
@@ -385,7 +385,7 @@ const { data, requestId, response } = await funnelchat.contacts.retrieve(2001).w
|
|
|
385
385
|
|
|
386
386
|
- Reads and writes that give the same result when repeated (`GET`, `PUT`, `DELETE`) are retried on a `429`, a `5xx` or a network error (also when `timeoutMs` ran out), up to `maxRetries` times (2 by default).
|
|
387
387
|
- Other writes (`POST`, `PATCH`) are retried **only when the API guarantees they were not applied**: `429` and `503`. A `500`, `502` or `504`, a timeout or a dropped connection can come after the write was applied, so the error is thrown right away: read the state before repeating it.
|
|
388
|
-
- **A send is never retried automatically**: the `POST`s
|
|
388
|
+
- **A send is never retried automatically**: the `POST`s that need **Contacts and conversations → Send** (`conversations:send`): messages, forms, cards, forwards and templates. If a `429 rate_limited` sent nothing and you want the SDK to retry for you, ask for it in that call with `{ retrySend: true }`: it then follows the rule of writes. `mac_lockout_24h` and `mac_limit_reached` are never retried.
|
|
389
389
|
- With `Retry-After`, the wait is exactly what it asks for. If it asks for more than 60 seconds, there is no wait: the error is thrown right away with its `retryAfter` and you decide. Without `Retry-After`, the wait is exponential: 0.5 s, 1 s, 2 s… up to 8 s, with some jitter.
|
|
390
390
|
- Per call you can change `maxRetries`, `timeoutMs` and `signal`:
|
|
391
391
|
|
package/dist/index.cjs
CHANGED
|
@@ -923,7 +923,8 @@ var Devices = class extends Resource {
|
|
|
923
923
|
/**
|
|
924
924
|
* `POST /api/v1/devices`: creates a number that connects by QR code (`connection: 'qr'`), with
|
|
925
925
|
* `connected: false`. Then show its QR with `qr`, or send your customer a connect link
|
|
926
|
-
* (`connectLinks.create`). Needs
|
|
926
|
+
* (`connectLinks.create`). Needs **WhatsApp numbers → Create, connect and delete**
|
|
927
|
+
* (`accounts:operate`). It takes a slot of the plan, with the panel's
|
|
927
928
|
* rejections: `403 numbers_not_included`, `403 number_limit_reached`, `422 qr_requires_coexistence`
|
|
928
929
|
* and `422 method_already_present` (`same_number_as` adds QR to a number in coexistence instead).
|
|
929
930
|
* An account has at most one QR number waiting to be scanned: another one is `409
|
|
@@ -944,7 +945,8 @@ var Devices = class extends Resource {
|
|
|
944
945
|
/**
|
|
945
946
|
* `DELETE /api/v1/devices/{device}`: deletes the number and frees its slot of the plan. An
|
|
946
947
|
* official number stops receiving through Funnelchat but stays registered with Meta; a QR one
|
|
947
|
-
* logs out. Irreversible; needs
|
|
948
|
+
* logs out. Irreversible; needs **WhatsApp numbers → Create, connect and delete**
|
|
949
|
+
* (`accounts:operate`). Sends `device.deleted`.
|
|
948
950
|
*/
|
|
949
951
|
delete(device, options) {
|
|
950
952
|
return this.client.request("DELETE", "/api/v1/devices/{device}", { ...options, path: { device } });
|
|
@@ -959,7 +961,8 @@ var Devices = class extends Resource {
|
|
|
959
961
|
* 0 once connected) and have it scanned right away: WhatsApp changes the code every few seconds and
|
|
960
962
|
* an old one no longer links, so show the latest and never store or forward a QR. Only for QR
|
|
961
963
|
* numbers (an official one is `422 provider_unsupported`); needs
|
|
962
|
-
* `accounts:operate
|
|
964
|
+
* **WhatsApp numbers → Create, connect and delete** (`accounts:operate`), since scanning it gives
|
|
965
|
+
* control of a WhatsApp. A number that cannot connect now
|
|
963
966
|
* answers `409` (`trial_number_blocked`, `number_suspended_by_plan`, `device_inactive`…).
|
|
964
967
|
*/
|
|
965
968
|
qr(device, options) {
|
|
@@ -1384,7 +1387,7 @@ var Webhooks = class extends Resource {
|
|
|
1384
1387
|
};
|
|
1385
1388
|
|
|
1386
1389
|
// src/version.ts
|
|
1387
|
-
var VERSION = "0.2.
|
|
1390
|
+
var VERSION = "0.2.1";
|
|
1388
1391
|
|
|
1389
1392
|
// src/client.ts
|
|
1390
1393
|
var BASE_URLS = {
|