ab-ecosystem-cpass 1.0.10 → 1.0.12

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.
Files changed (40) hide show
  1. package/README.md +1 -0
  2. package/dist/client.d.ts +6 -0
  3. package/dist/client.js +7 -0
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +7 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/internal/embedded-signup.d.ts +29 -0
  9. package/dist/internal/embedded-signup.js +175 -0
  10. package/dist/internal/embedded-signup.js.map +1 -0
  11. package/dist/resources/channels/onboarding.d.ts +62 -0
  12. package/dist/resources/channels/onboarding.js +76 -0
  13. package/dist/resources/channels/onboarding.js.map +1 -0
  14. package/dist/resources/data-stores.d.ts +43 -0
  15. package/dist/resources/data-stores.js +43 -0
  16. package/dist/resources/data-stores.js.map +1 -1
  17. package/dist/types/channels.d.ts +79 -0
  18. package/docs/audience/canned-replies.md +3 -0
  19. package/docs/audience/contact-tags.md +4 -0
  20. package/docs/audience/contacts.md +23 -0
  21. package/docs/audience/crm-triggers.md +3 -0
  22. package/docs/audience/custom-fields.md +23 -0
  23. package/docs/audience/exports.md +6 -0
  24. package/docs/audience/imports.md +7 -0
  25. package/docs/audience/notifications.md +11 -0
  26. package/docs/audience/scheduled-messages.md +5 -0
  27. package/docs/audience/segments.md +4 -0
  28. package/docs/audience/webhooks.md +7 -0
  29. package/docs/campaign/campaigns.md +20 -0
  30. package/docs/channels/activity.md +3 -0
  31. package/docs/channels/billing.md +2 -0
  32. package/docs/channels/channel-props.md +19 -0
  33. package/docs/channels/channels.md +34 -2
  34. package/docs/channels/onboarding.md +324 -0
  35. package/docs/channels/templates.md +22 -0
  36. package/docs/channels/wallet.md +2 -0
  37. package/docs/channels/webhooks.md +6 -0
  38. package/docs/data-store.md +78 -0
  39. package/docs/inbox/connection.md +88 -0
  40. package/package.json +1 -1
@@ -381,3 +381,82 @@ export interface ListActivityLogsQuery extends ListQuery {
381
381
  /** Restrict to a period. */
382
382
  range?: UnixRange;
383
383
  }
384
+ /** Channel families the onboarding endpoints can connect today. */
385
+ export type OnboardingChannelType = 'whatsapp';
386
+ /** Every family the picker shows, implemented or not. */
387
+ export type OfferableChannelType = 'whatsapp' | 'facebook' | 'instagram';
388
+ /** Why a family cannot be connected right now. */
389
+ export type OnboardingUnavailableReason = 'not_included' | 'quota_exceeded' | 'not_supported' | 'provider_not_configured' | 'not_permitted';
390
+ /** One row of the "add a channel" picker. */
391
+ export interface ChannelOnboardingOption {
392
+ type: OfferableChannelType;
393
+ label: string;
394
+ available: boolean;
395
+ /** The inclusion a channel would be created against. Pass back to `url()`. */
396
+ slug?: string;
397
+ limit?: number;
398
+ used?: number;
399
+ remaining?: number;
400
+ /** Set only when `available` is false. */
401
+ reason?: OnboardingUnavailableReason;
402
+ }
403
+ export interface OnboardingUrlInput {
404
+ type?: OnboardingChannelType;
405
+ /** Only needed when the plan carries more than one channel inclusion. */
406
+ slug?: string;
407
+ /** Must be one of the service's configured redirects. Omit for the default. */
408
+ redirectUri?: string;
409
+ }
410
+ export interface OnboardingUrlResult {
411
+ /** Open in a popup. Meta refuses to render the dialog in an iframe. */
412
+ url: string;
413
+ /** Opaque; hand back to `exchange()` unchanged. */
414
+ state: string;
415
+ redirectUri: string;
416
+ expiresAt: IsoDate;
417
+ provider: string;
418
+ remaining?: number;
419
+ }
420
+ /** What Meta reports about the number, for the confirmation card. */
421
+ export interface OnboardingPhoneNumber {
422
+ id: string;
423
+ wabaId: string;
424
+ /** Formatted, e.g. `+91 93116 36081`. */
425
+ displayPhoneNumber: string;
426
+ verifiedName: string;
427
+ status?: string;
428
+ codeVerificationStatus?: string;
429
+ qualityRating?: string;
430
+ }
431
+ export interface OnboardingExchangeInput {
432
+ state: string;
433
+ code: string;
434
+ wabaId: string;
435
+ phoneNumberId: string;
436
+ }
437
+ export interface OnboardingExchangeResult {
438
+ /** The pending record's id. Pass to `complete()` as `token`. */
439
+ id: string;
440
+ expiresAt: IsoDate;
441
+ phoneNumber: OnboardingPhoneNumber;
442
+ /** Prefill for the final form. */
443
+ defaults: {
444
+ name: string;
445
+ color: string;
446
+ currency: ChannelCurrency;
447
+ };
448
+ }
449
+ export interface CompleteOnboardingInput {
450
+ /** The `id` from `exchange()`. */
451
+ token: string;
452
+ name: string;
453
+ color?: string;
454
+ currency?: ChannelCurrency;
455
+ }
456
+ /** What the Meta dialog hands back, once both halves of it have arrived. */
457
+ export interface SignupCredentials {
458
+ code: string;
459
+ state: string;
460
+ wabaId: string;
461
+ phoneNumberId: string;
462
+ }
@@ -22,6 +22,9 @@ Two kinds, and the difference is who gets the last word:
22
22
  | [`update`](#update) | `PATCH /v1/audience/canned-replies/:reply` | `canned_replies:write` |
23
23
  | [`delete`](#delete) | `DELETE /v1/audience/canned-replies/:reply` | `canned_replies:write` |
24
24
 
25
+ **Permissions** — `CRM.CHAT.CANNED_REPLY`: `LIST`, `READ`, `CREATE`, `UPDATE`,
26
+ `DELETE`. Account-level, so `data_scope` does not narrow it.
27
+
25
28
  ## Addressing a reply: `:reply` is an id **or** a name
26
29
 
27
30
  A 24-hex-character path segment is read as an **id**; anything else as a
@@ -14,6 +14,10 @@ tag **ids** on a write, and hands back the full tag documents resolved into
14
14
  | [`update`](#update) | `PATCH /v1/audience/contact-tags/:id` | `contact_tags:write` |
15
15
  | [`delete`](#delete) | `DELETE /v1/audience/contact-tags/:id` | `contact_tags:write` |
16
16
 
17
+ **Permissions** — `CRM.CONTACT.TAG`: `LIST` to list, `READ` to read one,
18
+ `CREATE`, `UPDATE`, `DELETE`. A tag is account-level, so a role's `data_scope`
19
+ does not narrow this list — a member who holds `LIST` sees the account's tags.
20
+
17
21
  ## The tag object
18
22
 
19
23
  ```ts
@@ -19,6 +19,29 @@ land on the caller's own.
19
19
  | [`delete`](#delete) | `DELETE /v1/audience/contacts/:id` | `contacts:write` |
20
20
  | [`iterate`](#iterate) | — (walks `list`) | `contacts:read` |
21
21
 
22
+ **Permissions** — each route also names a permission key, and a team member
23
+ needs it. An owner does not.
24
+
25
+ | Action | Key |
26
+ | --- | --- |
27
+ | `list` | `CRM.CONTACT.LIST` |
28
+ | `get` | `CRM.CONTACT.READ` |
29
+ | `create` | `CRM.CONTACT.CREATE` |
30
+ | `update` | `CRM.CONTACT.UPDATE` |
31
+ | `delete` | `CRM.CONTACT.DELETE` |
32
+
33
+ A refusal is `403` with code `A403` (the role does not hold the key) or `E403`
34
+ (the account's plan does not cover contacts — `details.feature` names it). An
35
+ owner can meet `E403` but never `A403`.
36
+
37
+ Separately from that, how many contacts a member can see is their role's
38
+ `data_scope`: `own` — the default — returns the contacts assigned to them as
39
+ `agent`, and `all` returns the account's. A contact assigned to nobody is the
40
+ account's and not visible to an `own`-scoped member. Reading, updating or
41
+ deleting a contact outside that scope is `404`, never `403`: a `403` would
42
+ confirm the contact exists, which is what the scope is hiding.
43
+
44
+
22
45
  ## The contact object
23
46
 
24
47
  ```ts
@@ -25,6 +25,9 @@ mean on the list.
25
25
  | [`delete`](#delete) | `DELETE /v1/audience/crm-triggers/:id` | `crm_triggers:write` |
26
26
  | [`logs`](#logs) | `GET /v1/audience/crm-triggers/logs` | `crm_triggers:read` |
27
27
 
28
+ **Permissions** — `CRM.TRIGGER`: `LIST`, `READ` (one trigger, and the log
29
+ endpoint), `CREATE`, `UPDATE`, `DELETE`.
30
+
28
31
  ## The trigger object
29
32
 
30
33
  ```ts
@@ -18,6 +18,29 @@ is looked up as one; anything else is read as a slug.
18
18
  | [`update`](#update) | `PATCH /v1/audience/custom-fields/:ref` | `custom_fields:write` |
19
19
  | [`delete`](#delete) | `DELETE /v1/audience/custom-fields/:ref` | `custom_fields:write` |
20
20
 
21
+ **Permissions** — the feature is `CRM.CONTACT.FIELD`, which is its own feature
22
+ and not a verb of `CRM.CONTACT`. A team member needs the key; an owner does not.
23
+
24
+ | Action | Key |
25
+ | --- | --- |
26
+ | `list`, `get` | `CRM.CONTACT.FIELD.LIST` |
27
+ | `create` | `CRM.CONTACT.FIELD.CREATE` |
28
+ | `update` | `CRM.CONTACT.FIELD.UPDATE` |
29
+ | `delete` | `CRM.CONTACT.FIELD.DELETE` |
30
+
31
+ Reading one field uses `LIST`: the feature publishes no `READ` verb.
32
+
33
+ The schema is account-level, so there is nothing for a role's `data_scope` to
34
+ narrow — a field definition is not assigned to anybody, and a member seeing half
35
+ a schema would see half of every contact. A member either reaches custom fields
36
+ or does not.
37
+
38
+ `teamAccess` on a field (`restricted` / `open_to_all`) is **stored and
39
+ filterable, not enforced**: passing `?teamAccess=restricted` filters the list,
40
+ and nothing restricts a member to `open_to_all` fields. If per-field visibility
41
+ is meant to be real, that is a separate change from the key above.
42
+
43
+
21
44
  ## The field object
22
45
 
23
46
  ```ts
@@ -23,11 +23,17 @@ The route is `/exports`, not `/contact-exports`: a row's `module` is `contact`
23
23
  | [`create`](#create) | `POST /v1/audience/exports` | `contacts:read` |
24
24
  | [`delete`](#delete) | `DELETE /v1/audience/exports/:id` | `contacts:write` |
25
25
 
26
+ **Permissions** — every route here needs `CRM.CONTACT.EXPORT`, reads included:
27
+ an export history row *is* the export, so a member who may not export has no
28
+ business reading what one produced. The catalogue notes `EXPORT` implies
29
+ `CRM.CONTACT.LIST`.
30
+
26
31
  There is no `update`: a row is moved by the worker, not by a caller.
27
32
 
28
33
  Note the scope on `create` — reading contacts out is a **read**, so an export
29
34
  can be started with read-only access.
30
35
 
36
+
31
37
  ## The export object
32
38
 
33
39
  ```ts
@@ -23,8 +23,15 @@ The route is `/imports`, not `/contact-imports`: a row's `module` is `contact`
23
23
  | [`create`](#create) | `POST /v1/audience/imports` | `contacts:write` |
24
24
  | [`delete`](#delete) | `DELETE /v1/audience/imports/:id` | `contacts:write` |
25
25
 
26
+ **Permissions** — the catalogue publishes `CRM.CONTACT.EXPORT` but **no
27
+ `IMPORT`**, so these map to what an import does: `create` and `delete` need
28
+ `CRM.CONTACT.CREATE` (an import is bulk contact creation) and the reads need
29
+ `CRM.CONTACT.LIST`. If importing should be grantable separately from creating
30
+ one contact at a time, the catalogue needs an `IMPORT` verb.
31
+
26
32
  There is no `update`: a row is moved by the worker, not by a caller.
27
33
 
34
+
28
35
  ## The import object
29
36
 
30
37
  ```ts
@@ -12,6 +12,10 @@ acting user, and the only party who can see or revoke it is that user.
12
12
  | [`unsubscribe`](#unsubscribe) | `POST /v1/audience/devices/unregister` | `devices:write` |
13
13
  | [`devices`](#devices) | `GET /v1/audience/devices` | `devices:read` |
14
14
 
15
+ **Permissions** — `CRM.DEVICE`: `LIST`, `CREATE` to register, `DELETE` to
16
+ unregister. Every device row belongs to the acting person rather than the
17
+ account, so a caller only ever sees and changes their own whatever the key says.
18
+
15
19
  ## What the SDK does, and what you do
16
20
 
17
21
  A push subscription has two halves and only one of them is an API call.
@@ -342,6 +346,13 @@ The practical consequence: a device registered before this existed is still
342
346
  reachable by `psuid` exactly as before, and the next `subscribe()` call — which
343
347
  the frontend makes on every app load — fills in the second id.
344
348
 
349
+ A **team member's** device records only their `psuid`. The legacy id on a member
350
+ token belongs to the account holder, not to the member, so filling it in would
351
+ make the member's phone answer notifications addressed to the owner — and the
352
+ owner's answer theirs. The consequence is narrow and worth knowing: a member is
353
+ reachable by `psuid`, and a chat assigned to them notifies them because the chat
354
+ row names them.
355
+
345
356
  ### Rendering it in a browser
346
357
 
347
358
  If the payload carries a `notification` block, FCM displays it and opens
@@ -17,6 +17,11 @@ goes out.
17
17
  | [`cancelSeries`](#cancelseries) | `DELETE /v1/audience/scheduled-messages/series/:series` | `messages:write` |
18
18
  | [`sendNow`](#sendnow) | `POST /v1/audience/scheduled-messages/:id/send-now` | `messages:write` |
19
19
 
20
+ **Permissions** — `CRM.CHAT.SCHEDULED_MESSAGE`: `LIST`, `READ`, `CREATE`
21
+ (a single schedule or a series), `UPDATE` to reschedule, `DELETE` to cancel
22
+ either, and **`SEND` for send-now**, which bypasses the schedule. The rows carry
23
+ the account and no per-member creator, so `data_scope` does not narrow them.
24
+
20
25
  ---
21
26
 
22
27
  ## Why conditions exist
@@ -17,6 +17,10 @@ campaign. Two runs a day apart can return different people, by design.
17
17
  | [`update`](#update) | `PATCH /v1/audience/segments/:id` | `segments:write` |
18
18
  | [`delete`](#delete) | `DELETE /v1/audience/segments/:id` | `segments:write` |
19
19
 
20
+ **Permissions** — `CRM.CONTACT.SEGMENT`: `LIST`, `READ`, `CREATE`, `UPDATE`,
21
+ `DELETE`. Reading a segment reveals its filter, not the contacts behind it;
22
+ those still pass `CRM.CONTACT.LIST`.
23
+
20
24
  ## The segment object
21
25
 
22
26
  ```ts
@@ -12,6 +12,12 @@ Three things in this SDK are called a webhook; they are not the same thing:
12
12
  | [`crm.crmTriggers`](crm-triggers.md) | One configured request per rule, when a contact matches | no |
13
13
  | [`crm.channelWebhooks`](../channels/webhooks.md) | A channel's message traffic | n/a |
14
14
 
15
+ **Permissions** — `CRM.WEBHOOK`: `LIST`, `READ`, `CREATE`, `UPDATE`, `DELETE`.
16
+ `READ` is a grant rather than a side effect of `LIST` because reading one
17
+ reveals the endpoint URL and its secret header.
18
+
19
+ Distinct from `CRM.CHANNEL.WEBHOOK`, which is where a *provider* delivers
20
+ inbound traffic for one channel.
15
21
  **Scoping** — unlike the rest of the audience module, these rows are scoped by
16
22
  **tenant** rather than by the calling user.
17
23
 
@@ -23,6 +29,7 @@ Three things in this SDK are called a webhook; they are not the same thing:
23
29
  | [`update`](#update) | `PATCH /v1/audience/webhooks/:id` | `webhooks:write` |
24
30
  | [`delete`](#delete) | `DELETE /v1/audience/webhooks/:id` | `webhooks:write` |
25
31
 
32
+
26
33
  ## The webhook object
27
34
 
28
35
  ```ts
@@ -18,6 +18,26 @@ One message sent to many people.
18
18
  | [`cancel`](#cancel) | `POST /v1/campaigns/:id/cancel` | |
19
19
  | [`delete`](#delete) | `DELETE /v1/campaigns/:id` | |
20
20
 
21
+ **Permissions** — the feature is `CRM.CAMPAIGN`. A team member needs the key; an
22
+ owner does not.
23
+
24
+ | Action | Key |
25
+ | --- | --- |
26
+ | `list` | `CRM.CAMPAIGN.LIST` |
27
+ | `get`, the report endpoints | `CRM.CAMPAIGN.READ` |
28
+ | `create`, `createBulk`, `createGroup`, `uploadReceivers` | `CRM.CAMPAIGN.CREATE` |
29
+ | `update` | `CRM.CAMPAIGN.UPDATE` |
30
+ | `trigger`, `schedule`, `cancel` | `CRM.CAMPAIGN.SEND` |
31
+ | `delete` | `CRM.CAMPAIGN.DELETE` |
32
+
33
+ `cancel` is under `SEND` rather than `UPDATE` on purpose: it is control of a
34
+ dispatch, and somebody who may not launch a campaign should not be able to stop
35
+ one that is going out.
36
+
37
+ A campaign is not assigned to anybody, so a role's `data_scope` does not narrow
38
+ this list — a member who holds `CRM.CAMPAIGN.LIST` sees the account's campaigns.
39
+
40
+
21
41
  ---
22
42
 
23
43
  ## Creating a campaign does not send it
@@ -14,6 +14,9 @@ the [wallet ledger](wallet.md) instead.
14
14
  | --- | --- | --- |
15
15
  | [`list`](#list) | `GET /v1/channels/:channelId/logs` | `channels:read` |
16
16
 
17
+ **Permissions** — `CRM.CHANNEL.LOG.LIST`. Its own feature rather than part of
18
+ reading the channel, because the log exposes message traffic.
19
+
17
20
  ## The activity log object
18
21
 
19
22
  ```ts
@@ -17,8 +17,10 @@ message category on a single one.
17
17
  | [`list`](#list) | `GET /v1/channels/:channelId/conversations` | `channels:read` |
18
18
  | [`stats`](#stats) | `GET /v1/channels/:channelId/conversations?stat=…` | `channels:read` |
19
19
 
20
+ **Permissions** — `CRM.CHANNEL.USAGE.READ`, shared with the channel wallet.
20
21
  Read-only: charges are written by the platform as conversations happen.
21
22
 
23
+
22
24
  ## The billing log object
23
25
 
24
26
  ```ts
@@ -1,5 +1,24 @@
1
1
  # Channel properties
2
2
 
3
+
4
+ **Permissions** — a prop is not one capability, so the key depends on which prop
5
+ is named. The path carries neither the feature (that is the channel's type) nor
6
+ the verb (that is the prop), so this is checked in the handler.
7
+
8
+ | Prop | Key |
9
+ | --- | --- |
10
+ | `token` | **`CREDENTIAL`** — it returns the provider access token |
11
+ | `wa_pin_update`, `wa_request_otp`, `wa_submit_otp`, `migrate_number`, `wa_deregister`, `whatsapp_business_encryption` | **`REGISTER`** |
12
+ | `message_analytics`, `conversation_analytics` | `ANALYTICS` |
13
+ | `test_message`, `upload_media`, `resumable_upload` | `SEND` |
14
+ | `webhook_status`, `webhook_listener_update` | `CRM.CHANNEL.WEBHOOK.READ` / `.UPDATE` |
15
+ | every other read | `READ` |
16
+ | every other write | `UPDATE` |
17
+
18
+ The first two rows are the reason the split exists. `token` behind `READ` would
19
+ let anybody who can view a channel take its credential, and the number
20
+ lifecycle — retiring a number, moving it to another account, re-keying Flows
21
+ encryption — is not a profile edit.
3
22
  `crm.channels.getProp` / `crm.channels.setProp` — `/v1/channels/:id/:prop`
4
23
 
5
24
  Everything about a channel that lives with the **provider** rather than on the
@@ -6,8 +6,10 @@ The connections an account sends and receives on: a WhatsApp number, an RCS
6
6
  agent, a webchat widget.
7
7
 
8
8
  A channel is **provisioned by onboarding, not by this API**. There is no
9
- `create` and no `delete`: `disable` retires one for good, keeping the row its
10
- billing, wallet and activity history hang off.
9
+ `create` and no `delete` here: `disable` retires one for good, keeping the row
10
+ its billing, wallet and activity history hang off. Connecting a new one is
11
+ `crm.channelOnboarding`, which walks the account holder through Meta's signup
12
+ dialog -- **[connecting a channel](onboarding.md)**.
11
13
 
12
14
  Anything that lives with the provider rather than on the channel document — the
13
15
  business profile, the QR code, media uploads, number registration — moves
@@ -24,6 +26,36 @@ through `getProp` and `setProp`, documented on their own page:
24
26
  | [`getProp`](channel-props.md) | `GET /v1/channels/:id/:prop` | `channels:read` |
25
27
  | [`setProp`](channel-props.md) | `POST /v1/channels/:id/:prop` | `channels:write` |
26
28
 
29
+ **Permissions** — there is no single `CRM.CHANNEL` key. The catalogue has one
30
+ feature per channel type, because each sits behind its own plan option:
31
+ `CRM.CHANNEL.WHATSAPP`, `CRM.CHANNEL.SMS`, `CRM.CHANNEL.RCS`. So the key for a
32
+ request depends on the **type of the channel being addressed**, which is a field
33
+ on the row — the check runs in the handler, not as route middleware.
34
+
35
+ | Action | Verb |
36
+ | --- | --- |
37
+ | `list` | `LIST` — see below |
38
+ | `get` | `READ` |
39
+ | `updateDetails` | `UPDATE` |
40
+ | `disable` | `DELETE` (a channel is never deleted; disabling retires it) |
41
+ | `rotateToken` | **`CREDENTIAL`** |
42
+ | `getProp` / `setProp` | depends on the prop — see [channel properties](channel-props.md) |
43
+
44
+ `rotateToken` is `CREDENTIAL` rather than `UPDATE` because it replaces the same
45
+ provider access token the `token` prop reads. Three verbs exist for that reason:
46
+ `CREDENTIAL` for the token, `REGISTER` for the number lifecycle, and `ANALYTICS`
47
+ for the analytics reads — none of which belong beside a business-profile edit.
48
+
49
+ `list` spans every type the account owns, so it cannot be one key: it is
50
+ **filtered** rather than refused. A member holding only
51
+ `CRM.CHANNEL.WHATSAPP.LIST` sees the WhatsApp channels and no others, and asking
52
+ for `?type=sms` without `CRM.CHANNEL.SMS.LIST` returns an empty page rather than
53
+ somebody else's channels.
54
+
55
+ Not every verb exists on every type: SMS reaches no properties at all, so it has
56
+ no `CREDENTIAL`, `REGISTER` or `ANALYTICS`, and asking for one is refused rather
57
+ than ignored.
58
+
27
59
  ## The channel object
28
60
 
29
61
  Credentials and the private auth block are stripped from every response; what