@funnelchat-hq/sdk 0.3.1 → 0.3.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/README.md CHANGED
@@ -88,10 +88,10 @@ if (event.type === 'message.received') {
88
88
 
89
89
  ## Groups
90
90
 
91
- List the groups of your numbers and send a message to one by its `uid`:
91
+ Start from the groups your number is in, read live from WhatsApp, and write to one by its `uid`:
92
92
 
93
93
  ```ts
94
- for await (const group of funnelchat.groups.list()) {
94
+ for await (const group of funnelchat.groups.listByDevice(device.id)) {
95
95
  console.log(group.uid, group.name); // '120363000000000001@g.us', 'Customers'
96
96
  }
97
97
 
@@ -102,9 +102,18 @@ const { action_id } = await funnelchat.groups.sendMessage('120363000000000001@g.
102
102
  });
103
103
  ```
104
104
 
105
+ To learn who joins and leaves a group, follow it once:
106
+
107
+ ```ts
108
+ const { data: followed } = await funnelchat.groups.follow('120363000000000001@g.us', { device_id: device.id });
109
+ console.log(followed.imported, followed.is_admin); // true, true
110
+ ```
111
+
112
+ - The live list is reused for a minute per number, so a group just created in WhatsApp can take that long to show. A group already in your account brings `device_id`; `funnelchat.groups.list()` lists those.
113
+ - Writing to a group that is not in your account follows it first, with the same rules: a number that is not admin of the group answers `422 device_not_group_admin` and nothing is imported. Following needs **Groups and campaigns → Manage groups** (`403 import_permission_required`), takes up to 5 groups per number per minute (`429 import_rate_limited`, with `retryAfter`) and works with QR numbers only (`422 provider_unsupported` with an official one). `followed.imported` is `false` when the group was already in your account.
105
114
  - The message goes out in the background: `group_action.completed` (with `action.uuid` equal to `action_id`) or `funnelchat.groups.retrieveAction(action_id)` tell you how it went. A file goes as `type: 'image'` (or `video`, `audio`, `document`) with its `https` `media_url`.
106
- - Who joins or leaves arrives by `group_participant.joined` and `group_participant.left`: `group`, `participant` (`identity`, `identity_type`, `name`) and `campaign`, `null` for a group without a campaign.
107
- - Permissions: **Groups and campaigns → View** to read and **Groups and campaigns → Send** to send.
115
+ - Who joins or leaves a followed group arrives by `group_participant.joined` and `group_participant.left`: `group`, `participant` (`identity`, `identity_type`, `name`) and `campaign`, `null` for a group without a campaign.
116
+ - Permissions: **Groups and campaigns → View** to read, **Groups and campaigns → Send** to send and **Groups and campaigns → Manage groups** to follow.
108
117
 
109
118
  ## Workspaces
110
119
 
@@ -146,7 +155,7 @@ CommonJS: `const { Funnelchat } = require('@funnelchat-hq/sdk');`. Every request
146
155
  | `funnelchat.contacts` | `list`, `create`, `upsert`, `retrieve`, `update`, `delete`, the tags and fields of a contact, `metaWindow` (the 24-hour window), notes. |
147
156
  | `funnelchat.messages` | `send`, `sendDirect`, `sendForm`, `sendContactCard`, `forward`, `get` (one message by id), `list` (the history of a contact), `react`, `removeReaction`. |
148
157
  | `funnelchat.templates` | `list`, `create`, `retrieve`, `update`, `quickReplyButtons`, `send`, `delete`. |
149
- | `funnelchat.groups` | `list`, `retrieve`, `listByDevice`, participants and their joins and leaves, messages, `sendMessage` and `retrieveAction`. |
158
+ | `funnelchat.groups` | `listByDevice` (a number's live groups), `follow`, `list`, `retrieve`, participants and their joins and leaves, messages, `sendMessage` and `retrieveAction`. |
150
159
  | `funnelchat.webhooks` | Subscriptions (`list`, `create`, `retrieve`, `update`, `delete`, `test`, `listDeliveries`), the catalog (`events`), `verify` and `constructEvent`. |
151
160
  | `funnelchat.conversations` | `list`, `update`, counters, mark everything as read or unread. |
152
161
  | `funnelchat.forms` | WhatsApp forms: `list`, `templates`, `create`, `retrieve`, `update`, `delete`, `createVersion`, `sync`, `publish`, numbers, `versions`, answers and `metrics`. |
@@ -166,7 +175,8 @@ A key has the permissions ticked when it was created. The panel groups them by a
166
175
  | Create contacts | **Contacts and conversations → Edit** |
167
176
  | Send messages and templates | **Contacts and conversations → Send** |
168
177
  | Read groups | **Groups and campaigns → View** |
169
- | Send to a group | **Groups and campaigns → Send** |
178
+ | Send to a group | **Groups and campaigns → Send** (and **Groups and campaigns → Manage groups** for one not in the account yet) |
179
+ | Follow a group | **Groups and campaigns → Manage groups** |
170
180
  | Webhook subscriptions | **Notifications (webhooks) → View** to read, **Notifications (webhooks) → Manage** for the rest, plus **View** of the area of each event |
171
181
 
172
182
  ### Send a message
@@ -524,12 +534,11 @@ const funnelchat = new Funnelchat({ apiKey: '1|fc_live_sandbox', baseUrl: 'http:
524
534
 
525
535
  ### Pending of the spec
526
536
 
527
- The types come from the full unified spec of the developer portal (`spec/funnelchat.json`; where it came from, in `spec/procedencia.json`: developers-portal c90565e4 of 04-11-2026, with the areas at accounts v1.5.97, conversations v1.19.7 and communities v2.35.5). What production has and that spec does not describe, or describes otherwise than production answers, lives in a separate layer, `spec/pending.json`, which generation applies **only while the spec does not bring it**, or still says exactly what the entry corrects; each entry cites the production code it was read in:
537
+ The types come from the full unified spec of the developer portal (`spec/funnelchat.json`; where it came from, in `spec/procedencia.json`: developers-portal 99928866 of 05-11-2026, with the areas at accounts v1.5.97, conversations v1.19.21 and communities v2.35.7). What production has and that spec does not describe, or describes otherwise than production answers, lives in a separate layer, `spec/pending.json`, which generation applies **only while the spec does not bring it**, or still says exactly what the entry corrects; each entry cites the production code it was read in:
528
538
 
529
- - a message to a group and its action (`groups.sendMessage`, `groups.retrieveAction`, `group_action.completed`): the spec was built with the group actions off;
530
- - the events of the account (`device.*`, billing, subscriptions, monthly active contacts) and some of groups (`group.message_received`, `group.reaction_added`, `group.full`, `group_action.failed`, the join requests): the API publishes them in its catalog (`webhooks.events()`), not in the spec;
539
+ - the events of the account (`device.*`, billing, subscriptions, monthly active contacts) and some of groups (`group.message_received`, `group.reaction_added`, `group.full`, `group_action.completed` and `.failed`, the join requests): the API publishes them in its catalog (`webhooks.events()`), not in the spec;
531
540
  - WhatsApp forms (except sending one) and `webhooks.rotateSecret`;
532
- - where the spec contradicts production: what a send answers (`{ message }`, never `null`), the body of `devices.qr`, the items of `templates.list`, the answer of `webhooks.update`, and the events the spec only shows by one example (`message.received`, `message.status_changed`, `whatsapp_form.completed`, `group_participant.joined` and `.left`).
541
+ - where the spec contradicts production: what a send answers (`{ message }`, never `null`), the body of `devices.qr`, the items of `templates.list`, the answer of `webhooks.update` and of `groups.retrieveAction`, and the events the spec only shows by one example (`message.received`, `message.status_changed`, `whatsapp_form.completed`, `group_participant.joined` and `.left`).
533
542
 
534
543
  `src/pending/` has what is not OpenAPI (the `workspace_id` of every event) and `src/core/error-codes.ts` the error codes.
535
544
 
package/dist/index.cjs CHANGED
@@ -388,7 +388,13 @@ var GROUP_ACTION_ERROR_CODES = {
388
388
  /** 422: that `type` cannot be sent to this group by this number. */
389
389
  unsupported_type: 422,
390
390
  /** 429: the number has 50 group actions open. Wait for some to finish; not worth retrying. */
391
- too_many_pending_actions: 429
391
+ too_many_pending_actions: 429,
392
+ /** 403: following a group (also by writing to one not in the account) needs **Groups and campaigns → Manage groups**. */
393
+ import_permission_required: 403,
394
+ /** 429, with `Retry-After`: the number already followed 5 groups this minute. */
395
+ import_rate_limited: 429,
396
+ /** 409: another call is following the same group right now. */
397
+ import_in_progress: 409
392
398
  };
393
399
  var IDEMPOTENCY_ERROR_CODES = {
394
400
  /** 422: the key was already used with another body. Use a new key for a new resource. */
@@ -600,6 +606,7 @@ var routes = [
600
606
  { "method": "POST", "path": "/api/v1/groups/{group}/admins", "operationId": "apiGroupAction.promoteAdmins", "domain": "communities", "permission": "communities:operate", "tag": "Acciones de grupo", "summary": "Promueve tel\xE9fonos a administrador", "pending": false },
601
607
  { "method": "POST", "path": "/api/v1/groups/{group}/admins/remove", "operationId": "apiGroupAction.demoteAdmins", "domain": "communities", "permission": "communities:operate", "tag": "Acciones de grupo", "summary": "Quita el rol de administrador a tel\xE9fonos", "pending": false },
602
608
  { "method": "PUT", "path": "/api/v1/groups/{group}/description", "operationId": "apiGroupAction.updateDescription", "domain": "communities", "permission": "communities:operate", "tag": "Acciones de grupo", "summary": "Cambia la descripci\xF3n del grupo", "pending": false },
609
+ { "method": "POST", "path": "/api/v1/groups/{group}/import", "operationId": "v2.apiGroupImport", "domain": "communities", "permission": "communities:operate", "tag": "Grupos", "summary": "Importar un grupo de un n\xFAmero", "pending": false },
603
610
  { "method": "GET", "path": "/api/v1/groups/{group}/invite-link/live", "operationId": "apiGroupAction.inviteLink", "domain": "communities", "permission": "communities:read", "tag": "Acciones de grupo", "summary": "Link de invitaci\xF3n vigente del grupo", "pending": false },
604
611
  { "method": "POST", "path": "/api/v1/groups/{group}/invite-link/renew", "operationId": "apiGroupAction.renewInviteLink", "domain": "communities", "permission": "communities:operate", "tag": "Acciones de grupo", "summary": "Renueva el link de invitaci\xF3n del grupo", "pending": false },
605
612
  { "method": "GET", "path": "/api/v1/groups/{group}/join-requests", "operationId": "apiGroupAction.joinRequests", "domain": "communities", "permission": "communities:read", "tag": "Acciones de grupo", "summary": "Solicitudes pendientes para unirse al grupo", "pending": false },
@@ -1132,7 +1139,11 @@ var Groups = class extends Resource {
1132
1139
  retrieve(group, options) {
1133
1140
  return this.client.request("GET", "/api/v1/groups/{group}", { ...options, path: { group } });
1134
1141
  }
1135
- /** `GET /api/v1/devices/{device}/groups`, paginated (`page`, `pageSize`): the groups of one number. */
1142
+ /**
1143
+ * `GET /api/v1/devices/{device}/groups`, paginated (`page`, `pageSize`): the groups of one number,
1144
+ * read live from WhatsApp (for a key, reused for a minute per number). A group that is already in
1145
+ * the account brings `device_id`; any other can be followed with {@link Groups.follow}.
1146
+ */
1136
1147
  listByDevice(device, query, options) {
1137
1148
  return this.client.paginate("/api/v1/devices/{device}/groups", { ...options, path: { device }, query });
1138
1149
  }
@@ -1175,6 +1186,20 @@ var Groups = class extends Resource {
1175
1186
  sendMessage(group, body, options) {
1176
1187
  return this.client.request("POST", "/api/v1/groups/{group}/messages", { ...options, path: { group }, body });
1177
1188
  }
1189
+ /**
1190
+ * `POST /api/v1/groups/{group}/import`: follows a WhatsApp group of a QR number (`device_id`), as
1191
+ * importing it in the panel does: from then on its joins and leaves arrive by
1192
+ * `group_participant.joined` and `group_participant.left`. Resolves to `{ data: { uid, name,
1193
+ * imported, is_admin } }`: `imported` is `false` when the group was already in the account, and
1194
+ * `is_admin` says whether the number administers it (without that it cannot write to it). Needs
1195
+ * **Groups and campaigns → Manage groups** (`403 import_permission_required`); up to 5 groups
1196
+ * per number per minute (`429 import_rate_limited`, with `retryAfter`); an official number is
1197
+ * `422 provider_unsupported`. {@link Groups.sendMessage} follows the group itself when it is not in
1198
+ * the account yet.
1199
+ */
1200
+ follow(group, body, options) {
1201
+ return this.client.request("POST", "/api/v1/groups/{group}/import", { ...options, path: { group }, body });
1202
+ }
1178
1203
  /**
1179
1204
  * `GET /api/v1/groups/actions/{action}`: where a group action, such as a message sent with
1180
1205
  * {@link Groups.sendMessage}, is (`queued`, `running`, `completed` or `failed`) and its `result`.
@@ -1462,7 +1487,7 @@ var Webhooks = class extends Resource {
1462
1487
  };
1463
1488
 
1464
1489
  // src/version.ts
1465
- var VERSION = "0.3.1";
1490
+ var VERSION = "0.3.2";
1466
1491
 
1467
1492
  // src/client.ts
1468
1493
  var BASE_URLS = {