ab-ecosystem-cpass 1.0.8 → 1.0.10

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
@@ -159,6 +159,44 @@ try {
159
159
  Common codes: `422` for a body or query that fails validation (`details` lists
160
160
  the paths), `404 not_found`, `409 conflict` for a name or slug already taken.
161
161
 
162
+ ### Plan limits
163
+
164
+ Every call needs an active subscription, and the metered resources are capped at
165
+ what the plan includes. Both refusals are `402`, told apart by `code`:
166
+
167
+ | Code | When | `details` |
168
+ | --- | --- | --- |
169
+ | `subscription_required` | The account has no subscription, or it has lapsed. Any call. | — |
170
+ | `not_included` | The plan includes none of that resource. | `{ resource }` |
171
+ | `quota_exceeded` | The plan's allowance is used up. | `{ resource, limit, used }` |
172
+
173
+ What counts, and against which inclusion:
174
+
175
+ | `resource` | Counted as | Inclusion |
176
+ | --- | --- | --- |
177
+ | `contact` | one contact | `contact_18472` — **1 unit = 1000 contacts** |
178
+ | `canned_reply` | one reply | `canned_reply_14172` |
179
+ | `scheduled_series` | one series, whatever its length — a one-off `schedule` is free | `series_91922` |
180
+ | `data_store` | one registered store | `data_store_52360` |
181
+ | `campaign` | one campaign | `campaign_73018` |
182
+ | `super_field` | one custom field | `super_field_29689` |
183
+ | `whatsapp_channel` | one live WhatsApp channel | `whatsapp_channels_28335` |
184
+
185
+ Counts are of what exists now, so deleting a row gives the allowance back.
186
+ `limit` and `used` are returned on `quota_exceeded` so a panel can say how far
187
+ over the account is without a second call.
188
+
189
+ ```ts
190
+ try {
191
+ await crm.campaigns.create(input);
192
+ } catch (err) {
193
+ if (err instanceof CrmApiError && err.status === 402) {
194
+ // err.code: 'subscription_required' | 'not_included' | 'quota_exceeded'
195
+ console.error(err.message, err.details);
196
+ }
197
+ }
198
+ ```
199
+
162
200
  ## Modules
163
201
 
164
202
  One page per resource, every action with its payload, query and response. The
@@ -271,6 +309,13 @@ await crm.campaigns.createGroup({
271
309
  });
272
310
  ```
273
311
 
312
+ **Creating a campaign does not send it.** Every create returns a draft:
313
+ `pending`, with no `scheduledAt`. `crm.campaigns.trigger(id)` sends it now (with
314
+ about a minute in which `cancel` still works), and
315
+ `crm.campaigns.trigger(id, { scheduledAt })` sends it then. A send time is
316
+ refused on `create` and `update`, because arming a campaign is also what records
317
+ who authorised it.
318
+
274
319
  Note that `campaign.receivers` is `0` on a campaign you just created: the
275
320
  audience is resolved when the campaign sends, not when it is built. The
276
321
  `receivers` on the result is the count written into the file, and it is the one
@@ -278,7 +323,7 @@ to show a user.
278
323
 
279
324
  | Resource | Page | Actions |
280
325
  | --- | --- | --- |
281
- | `crm.campaigns` | [campaigns](https://github.com/udghatan-dev/crm-sdk/blob/main/packages/sdk-js/docs/campaign/campaigns.md) | `list` `get` `create` `createBulk` `createGroup` `uploadReceivers` `update` `schedule` `cancel` `delete` |
326
+ | `crm.campaigns` | [campaigns](https://github.com/udghatan-dev/crm-sdk/blob/main/packages/sdk-js/docs/campaign/campaigns.md) | `list` `get` `create` `createBulk` `createGroup` `uploadReceivers` `update` **`trigger`** `schedule` `cancel` `delete` — creating does not send; `trigger` is what does |
282
327
  | `crm.campaignMessages` | [campaign reporting](https://github.com/udghatan-dev/crm-sdk/blob/main/packages/sdk-js/docs/campaign/campaign-messages.md) | `stats` `list` `iterate` `replies` — `/v1/campaigns/:id/report`, counted off the per-recipient rows |
283
328
 
284
329
  The audience helpers are exported too, so a UI can show "12,480 recipients, 3
@@ -2,7 +2,7 @@ import type { HttpClient } from '../http';
2
2
  import { InboxMedia } from './audience/inbox-media';
3
3
  import { type CampaignReceiver, type ReceiverEncoding } from '../internal/receiver-file';
4
4
  import { type BulkAudienceInput, type ResolvedAudience } from './campaign-audience';
5
- import type { Campaign, CampaignNumbersFilter, CreateCampaignInput, ListCampaignsQuery, UpdateCampaignInput } from '../types/campaign';
5
+ import type { Campaign, CampaignNumbersFilter, CreateCampaignInput, ListCampaignsQuery, TriggerCampaignInput, UpdateCampaignInput } from '../types/campaign';
6
6
  import type { Page } from '../types/common';
7
7
  /** Everything `createBulk` needs: a normal campaign, with an audience instead of `filters`. */
8
8
  export interface CreateBulkCampaignInput extends Omit<CreateCampaignInput, 'filters' | 'type'> {
@@ -102,6 +102,17 @@ export declare class Campaigns {
102
102
  /**
103
103
  * Creates a campaign from a payload you have already assembled.
104
104
  *
105
+ * **Creating does not send.** The campaign comes back `pending` with no
106
+ * `scheduledAt`: a draft you can keep editing, invisible to the sender until
107
+ * {@link trigger} arms it.
108
+ *
109
+ * ```ts
110
+ * const campaign = await crm.campaigns.create({ name: 'Diwali offer', messages });
111
+ * // ...show it for review...
112
+ * await crm.campaigns.trigger(campaign.id); // send now
113
+ * await crm.campaigns.trigger(campaign.id, { scheduledAt }); // or then
114
+ * ```
115
+ *
105
116
  * For a bulk recipient list, prefer {@link createBulk} -- it does the
106
117
  * encrypting and uploading that a file-backed audience requires, which is not
107
118
  * something a caller should have to reproduce.
@@ -129,9 +140,10 @@ export declare class Campaigns {
129
140
  * name: 'Renewal reminder',
130
141
  * audience: { csv: await file.text(), recipientColumn: 'phone' },
131
142
  * messages: [{ channel: { id: channelId }, message: template }],
132
- * trigger: 'schedule',
133
- * scheduledAt: '2026-11-01T04:30:00.000Z',
134
143
  * });
144
+ *
145
+ * // Neither of the above has been sent yet. Arm it when you are ready:
146
+ * await crm.campaigns.trigger(campaign.id, { scheduledAt: '2026-11-01T04:30:00.000Z' });
135
147
  * ```
136
148
  *
137
149
  * ## What it does, in order
@@ -233,6 +245,24 @@ export declare class Campaigns {
233
245
  */
234
246
  encoding?: ReceiverEncoding): Promise<CampaignNumbersFilter>;
235
247
  update(id: string, input: UpdateCampaignInput): Promise<Campaign>;
248
+ /**
249
+ * Arms a campaign -- the only call that makes one actually go out.
250
+ *
251
+ * ```ts
252
+ * await crm.campaigns.trigger(id); // now
253
+ * await crm.campaigns.trigger(id, { scheduledAt: isoInstant }); // then
254
+ * ```
255
+ *
256
+ * "Now" means a minute from now by default, which is the window `cancel()`
257
+ * works in. The server stamps `triggerInfo` -- the caller, their address, the
258
+ * moment -- as part of this call, so every send has one authorising act behind
259
+ * it; nothing you pass can set it.
260
+ *
261
+ * Only a `pending` campaign can be armed; anything else answers `409`.
262
+ * Triggering one that is armed but not yet picked up simply re-arms it.
263
+ */
264
+ trigger(id: string, input?: TriggerCampaignInput): Promise<Campaign>;
265
+ /** {@link trigger} with a required time, under the name the panel uses. */
236
266
  schedule(id: string, scheduledAt: string): Promise<Campaign>;
237
267
  cancel(id: string): Promise<Campaign>;
238
268
  delete(id: string): Promise<void>;
@@ -35,6 +35,17 @@ class Campaigns {
35
35
  /**
36
36
  * Creates a campaign from a payload you have already assembled.
37
37
  *
38
+ * **Creating does not send.** The campaign comes back `pending` with no
39
+ * `scheduledAt`: a draft you can keep editing, invisible to the sender until
40
+ * {@link trigger} arms it.
41
+ *
42
+ * ```ts
43
+ * const campaign = await crm.campaigns.create({ name: 'Diwali offer', messages });
44
+ * // ...show it for review...
45
+ * await crm.campaigns.trigger(campaign.id); // send now
46
+ * await crm.campaigns.trigger(campaign.id, { scheduledAt }); // or then
47
+ * ```
48
+ *
38
49
  * For a bulk recipient list, prefer {@link createBulk} -- it does the
39
50
  * encrypting and uploading that a file-backed audience requires, which is not
40
51
  * something a caller should have to reproduce.
@@ -64,9 +75,10 @@ class Campaigns {
64
75
  * name: 'Renewal reminder',
65
76
  * audience: { csv: await file.text(), recipientColumn: 'phone' },
66
77
  * messages: [{ channel: { id: channelId }, message: template }],
67
- * trigger: 'schedule',
68
- * scheduledAt: '2026-11-01T04:30:00.000Z',
69
78
  * });
79
+ *
80
+ * // Neither of the above has been sent yet. Arm it when you are ready:
81
+ * await crm.campaigns.trigger(campaign.id, { scheduledAt: '2026-11-01T04:30:00.000Z' });
70
82
  * ```
71
83
  *
72
84
  * ## What it does, in order
@@ -260,6 +272,28 @@ class Campaigns {
260
272
  update(id, input) {
261
273
  return this.http.request('PATCH', `/v1/campaigns/${encodeURIComponent(id)}`, { body: input });
262
274
  }
275
+ /**
276
+ * Arms a campaign -- the only call that makes one actually go out.
277
+ *
278
+ * ```ts
279
+ * await crm.campaigns.trigger(id); // now
280
+ * await crm.campaigns.trigger(id, { scheduledAt: isoInstant }); // then
281
+ * ```
282
+ *
283
+ * "Now" means a minute from now by default, which is the window `cancel()`
284
+ * works in. The server stamps `triggerInfo` -- the caller, their address, the
285
+ * moment -- as part of this call, so every send has one authorising act behind
286
+ * it; nothing you pass can set it.
287
+ *
288
+ * Only a `pending` campaign can be armed; anything else answers `409`.
289
+ * Triggering one that is armed but not yet picked up simply re-arms it.
290
+ */
291
+ trigger(id, input = {}) {
292
+ return this.http.request('POST', `/v1/campaigns/${encodeURIComponent(id)}/trigger`, {
293
+ body: input,
294
+ });
295
+ }
296
+ /** {@link trigger} with a required time, under the name the panel uses. */
263
297
  schedule(id, scheduledAt) {
264
298
  return this.http.request('POST', `/v1/campaigns/${encodeURIComponent(id)}/schedule`, {
265
299
  body: { scheduledAt },
@@ -1 +1 @@
1
- {"version":3,"file":"campaigns.js","sourceRoot":"","sources":["../../src/resources/campaigns.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"campaigns.js","sourceRoot":"","sources":["../../src/resources/campaigns.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,10 @@
1
- import type { BulkDeleteDataStoreRecordsInput, BulkUpdateDataStoreRecordsInput, CreateDataStoreCollectionInput, CreateDataStoreInput, DataStore, DataStoreCollection, DataStoreDeleteResult, DataStoreRecord, DataStoreTestResult, DataStoreUpdateResult, DataStoreWriteResult, InsertDataStoreRecordsInput, ListDataStoreRecordsQuery, ListQuery, Paginated, SearchDataStoreRecordsInput, UpdateDataStoreCollectionInput, UpdateDataStoreInput, UpdateDataStoreRecordInput } from '../types';
1
+ import type { BulkDeleteDataStoreRecordsInput, CloudDataStoreRegion, BulkUpdateDataStoreRecordsInput, CreateDataStoreCollectionInput, CreateDataStoreInput, DataStore, DataStoreCollection, DataStoreDeleteResult, DataStoreRecord, DataStoreTestResult, DataStoreUpdateResult, DataStoreWriteResult, InsertDataStoreRecordsInput, ListDataStoreRecordsQuery, ListQuery, Paginated, SearchDataStoreRecordsInput, UpdateDataStoreCollectionInput, UpdateDataStoreInput, UpdateDataStoreRecordInput } from '../types';
2
2
  import type { HttpClient } from '../http';
3
3
  /**
4
- * Data stores: a MongoDB **you** own, registered with the platform.
4
+ * Data stores: a MongoDB you own, or one the platform runs for you.
5
5
  *
6
6
  * ```ts
7
+ * // your own cluster, registered with a credential
7
8
  * const store = await crm.dataStores.create({
8
9
  * title: 'Orders DB',
9
10
  * credentials: {
@@ -15,6 +16,9 @@ import type { HttpClient } from '../http';
15
16
  * },
16
17
  * });
17
18
  *
19
+ * // or one of the platform's -- no credential, no cluster to run
20
+ * const hosted = await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
21
+ *
18
22
  * const orders = await crm.dataStoreCollections.create(store.id, {
19
23
  * title: 'Orders',
20
24
  * fields: [
@@ -29,12 +33,30 @@ import type { HttpClient } from '../http';
29
33
  * });
30
34
  * ```
31
35
  *
36
+ * ## Which kind to pick
37
+ *
38
+ * A **cloud** store (`type: 'cloud'`) is a database the platform provisions on
39
+ * one of its clusters, in a location you choose from `regions()` -- Germany for
40
+ * now. There is no credential to manage, nothing to expose to the internet, and
41
+ * no IP access list to maintain. Use it for data that does not already live
42
+ * somewhere.
43
+ *
44
+ * A **self** store is your own cluster, and the default. Use it when the data is
45
+ * already there, or has to stay there.
46
+ *
47
+ * The difference that matters beyond setup is **who the system of record is**,
48
+ * and it shows up on delete -- see below.
49
+ *
32
50
  * ## What the platform holds, and what it does not
33
51
  *
34
- * It holds the credential -- encrypted -- and the schemas you declare. It holds
35
- * **none of your data**: records are read from and written to your cluster on
36
- * every call, so nothing here is a copy that can go stale, and nothing here
37
- * survives you revoking the credential.
52
+ * For a `self` store it holds the credential -- encrypted -- and the schemas you
53
+ * declare. It holds **none of your data**: records are read from and written to
54
+ * your cluster on every call, so nothing here is a copy that can go stale, and
55
+ * nothing here survives you revoking the credential.
56
+ *
57
+ * For a `cloud` store it holds the database itself. Nothing about that cluster
58
+ * is returned -- no host, no user, no connection string -- because there is no
59
+ * second way in: the API is the only door to those records.
38
60
  *
39
61
  * Which also means every record call costs a round trip to your database. The
40
62
  * platform keeps the connection open between calls, so that is one query's
@@ -42,14 +64,17 @@ import type { HttpClient } from '../http';
42
64
  * single inserts is a thousand round trips: send them as one `insert` with
43
65
  * `records` instead.
44
66
  *
45
- * ## Deleting never deletes your data
67
+ * ## Deleting a `self` store never deletes your data. Deleting a `cloud` store does
68
+ *
69
+ * On a **self** store, `remove()` forgets the credential and the schemas, and
70
+ * `remove()` on a collection forgets the declaration. **Neither touches your
71
+ * database** -- the collections and every document in them are exactly as they
72
+ * were, and re-registering the same credential with the same collection name
73
+ * reads it all straight back.
46
74
  *
47
- * `remove()` on a store forgets the credential and the schemas. `remove()` on a
48
- * collection forgets the declaration. **Neither touches your database** -- the
49
- * collections and every document in them are exactly as they were, and
50
- * re-registering the same credential with the same collection name reads it all
51
- * straight back. Deleting *records* is the only thing here that deletes
52
- * anything of yours.
75
+ * On a **cloud** store, `remove()` **destroys the database**. Every record in it
76
+ * is gone, there is no undo, and re-creating the store gets a fresh empty one.
77
+ * Ask before you call it.
53
78
  *
54
79
  * ## The host must be reachable from the internet
55
80
  *
@@ -62,6 +87,21 @@ import type { HttpClient } from '../http';
62
87
  export declare class DataStores {
63
88
  private readonly http;
64
89
  constructor(http: HttpClient);
90
+ /**
91
+ * `GET /v1/datastores/cloud-regions` -- `{ data: [{ code, location }] }`.
92
+ *
93
+ * The clusters a cloud store can be provisioned on. Read it to render the
94
+ * location choice rather than hardcoding one: locations are added over time,
95
+ * and the `code` is what a create takes.
96
+ *
97
+ * An **empty list** means this deployment provisions none -- an on-premise
98
+ * install, typically -- and a `type: 'cloud'` create would be refused.
99
+ *
100
+ * Scope: `datastores:read`.
101
+ */
102
+ regions(): Promise<{
103
+ data: CloudDataStoreRegion[];
104
+ }>;
65
105
  /**
66
106
  * `POST /v1/datastores` -- 201 with the store.
67
107
  *
@@ -70,7 +110,25 @@ export declare class DataStores {
70
110
  * | Field | Type | Required | Notes |
71
111
  * | --- | --- | --- | --- |
72
112
  * | `title` | string | yes | What you call it. 1-120 characters. |
73
- * | `credentials` | object | yes | Either form below. |
113
+ * | `type` | `'self' \| 'cloud'` | no | Default `self`: your own database. |
114
+ * | `credentials` | object | for `self` | Either form below. Refused for `cloud`. |
115
+ * | `code` | string | no | `cloud` only: a cluster from `regions()`. Defaults to the platform's. |
116
+ *
117
+ * ### A cloud store
118
+ *
119
+ * ```ts
120
+ * await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
121
+ * ```
122
+ *
123
+ * That is the whole payload: the platform provisions a database of its own,
124
+ * in the default location, and the response carries the `code` and `location`
125
+ * it ended up in. Nothing below about credentials, hosts or IP access lists
126
+ * applies -- and neither does the 502 pair, because there is no cluster of
127
+ * yours to be unreachable.
128
+ *
129
+ * | Status | Code | Meaning |
130
+ * | --- | --- | --- |
131
+ * | 422 | `validation_failed` | Credentials sent with `type: 'cloud'`, or a `code` that is not on offer -- including when this deployment offers none. |
74
132
  *
75
133
  * **`credentials.type: 'field'`**
76
134
  *
@@ -128,7 +186,13 @@ export declare class DataStores {
128
186
  * | Field | Type | Notes |
129
187
  * | --- | --- | --- |
130
188
  * | `title` | string | |
131
- * | `credentials` | object | Replaced whole, and tested before it is stored. |
189
+ * | `credentials` | object | Replaced whole, and tested before it is stored. `self` stores only. |
190
+ *
191
+ * `type` and `code` are not editable: they say where the records are, not how
192
+ * the store is configured. Sending `credentials` for a **cloud** store is a
193
+ * `422` -- there is nothing to replace, and re-pointing it would leave its
194
+ * records in a database nothing addresses. Register your own database as a
195
+ * second store instead.
132
196
  *
133
197
  * A partial credential is not accepted: send all four fields, or the whole
134
198
  * URI. New credentials go through the same checks as `create`, and the old
@@ -141,10 +205,19 @@ export declare class DataStores {
141
205
  /**
142
206
  * `DELETE /v1/datastores/:store` -- 204.
143
207
  *
144
- * Forgets the credential and every schema declared on it. **Your database is
145
- * not touched**: the collections and all their documents remain, and
146
- * registering the same credential again with the same collection names reads
147
- * them straight back.
208
+ * **A `self` store:** forgets the credential and every schema declared on it.
209
+ * Your database is not touched -- the collections and all their documents
210
+ * remain, and registering the same credential again with the same collection
211
+ * names reads them straight back.
212
+ *
213
+ * **A `cloud` store:** drops every collection in its database, which destroys
214
+ * the database with them. Every record is gone, there is no undo, and a new
215
+ * store of the same name is a new empty database. Confirm with the user before
216
+ * calling this on one.
217
+ *
218
+ * | Status | Code | Meaning |
219
+ * | --- | --- | --- |
220
+ * | 502 | `datastore_purge_failed` | A collection could not be deleted, named in `details`. Nothing was removed -- the store is intact and the call can be retried. |
148
221
  *
149
222
  * Scope: `datastores:write`.
150
223
  */
@@ -8,9 +8,10 @@ const collectionsPath = (store) => `${storePath(store)}/collections`;
8
8
  const collectionPath = (store, collection) => `${collectionsPath(store)}/${(0, paths_1.seg)(collection)}`;
9
9
  const recordsPath = (store, collection) => `${collectionPath(store, collection)}/records`;
10
10
  /**
11
- * Data stores: a MongoDB **you** own, registered with the platform.
11
+ * Data stores: a MongoDB you own, or one the platform runs for you.
12
12
  *
13
13
  * ```ts
14
+ * // your own cluster, registered with a credential
14
15
  * const store = await crm.dataStores.create({
15
16
  * title: 'Orders DB',
16
17
  * credentials: {
@@ -22,6 +23,9 @@ const recordsPath = (store, collection) => `${collectionPath(store, collection)}
22
23
  * },
23
24
  * });
24
25
  *
26
+ * // or one of the platform's -- no credential, no cluster to run
27
+ * const hosted = await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
28
+ *
25
29
  * const orders = await crm.dataStoreCollections.create(store.id, {
26
30
  * title: 'Orders',
27
31
  * fields: [
@@ -36,12 +40,30 @@ const recordsPath = (store, collection) => `${collectionPath(store, collection)}
36
40
  * });
37
41
  * ```
38
42
  *
43
+ * ## Which kind to pick
44
+ *
45
+ * A **cloud** store (`type: 'cloud'`) is a database the platform provisions on
46
+ * one of its clusters, in a location you choose from `regions()` -- Germany for
47
+ * now. There is no credential to manage, nothing to expose to the internet, and
48
+ * no IP access list to maintain. Use it for data that does not already live
49
+ * somewhere.
50
+ *
51
+ * A **self** store is your own cluster, and the default. Use it when the data is
52
+ * already there, or has to stay there.
53
+ *
54
+ * The difference that matters beyond setup is **who the system of record is**,
55
+ * and it shows up on delete -- see below.
56
+ *
39
57
  * ## What the platform holds, and what it does not
40
58
  *
41
- * It holds the credential -- encrypted -- and the schemas you declare. It holds
42
- * **none of your data**: records are read from and written to your cluster on
43
- * every call, so nothing here is a copy that can go stale, and nothing here
44
- * survives you revoking the credential.
59
+ * For a `self` store it holds the credential -- encrypted -- and the schemas you
60
+ * declare. It holds **none of your data**: records are read from and written to
61
+ * your cluster on every call, so nothing here is a copy that can go stale, and
62
+ * nothing here survives you revoking the credential.
63
+ *
64
+ * For a `cloud` store it holds the database itself. Nothing about that cluster
65
+ * is returned -- no host, no user, no connection string -- because there is no
66
+ * second way in: the API is the only door to those records.
45
67
  *
46
68
  * Which also means every record call costs a round trip to your database. The
47
69
  * platform keeps the connection open between calls, so that is one query's
@@ -49,14 +71,17 @@ const recordsPath = (store, collection) => `${collectionPath(store, collection)}
49
71
  * single inserts is a thousand round trips: send them as one `insert` with
50
72
  * `records` instead.
51
73
  *
52
- * ## Deleting never deletes your data
74
+ * ## Deleting a `self` store never deletes your data. Deleting a `cloud` store does
75
+ *
76
+ * On a **self** store, `remove()` forgets the credential and the schemas, and
77
+ * `remove()` on a collection forgets the declaration. **Neither touches your
78
+ * database** -- the collections and every document in them are exactly as they
79
+ * were, and re-registering the same credential with the same collection name
80
+ * reads it all straight back.
53
81
  *
54
- * `remove()` on a store forgets the credential and the schemas. `remove()` on a
55
- * collection forgets the declaration. **Neither touches your database** -- the
56
- * collections and every document in them are exactly as they were, and
57
- * re-registering the same credential with the same collection name reads it all
58
- * straight back. Deleting *records* is the only thing here that deletes
59
- * anything of yours.
82
+ * On a **cloud** store, `remove()` **destroys the database**. Every record in it
83
+ * is gone, there is no undo, and re-creating the store gets a fresh empty one.
84
+ * Ask before you call it.
60
85
  *
61
86
  * ## The host must be reachable from the internet
62
87
  *
@@ -71,6 +96,21 @@ class DataStores {
71
96
  constructor(http) {
72
97
  this.http = http;
73
98
  }
99
+ /**
100
+ * `GET /v1/datastores/cloud-regions` -- `{ data: [{ code, location }] }`.
101
+ *
102
+ * The clusters a cloud store can be provisioned on. Read it to render the
103
+ * location choice rather than hardcoding one: locations are added over time,
104
+ * and the `code` is what a create takes.
105
+ *
106
+ * An **empty list** means this deployment provisions none -- an on-premise
107
+ * install, typically -- and a `type: 'cloud'` create would be refused.
108
+ *
109
+ * Scope: `datastores:read`.
110
+ */
111
+ regions() {
112
+ return this.http.request('GET', `${BASE}/cloud-regions`);
113
+ }
74
114
  /**
75
115
  * `POST /v1/datastores` -- 201 with the store.
76
116
  *
@@ -79,7 +119,25 @@ class DataStores {
79
119
  * | Field | Type | Required | Notes |
80
120
  * | --- | --- | --- | --- |
81
121
  * | `title` | string | yes | What you call it. 1-120 characters. |
82
- * | `credentials` | object | yes | Either form below. |
122
+ * | `type` | `'self' \| 'cloud'` | no | Default `self`: your own database. |
123
+ * | `credentials` | object | for `self` | Either form below. Refused for `cloud`. |
124
+ * | `code` | string | no | `cloud` only: a cluster from `regions()`. Defaults to the platform's. |
125
+ *
126
+ * ### A cloud store
127
+ *
128
+ * ```ts
129
+ * await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
130
+ * ```
131
+ *
132
+ * That is the whole payload: the platform provisions a database of its own,
133
+ * in the default location, and the response carries the `code` and `location`
134
+ * it ended up in. Nothing below about credentials, hosts or IP access lists
135
+ * applies -- and neither does the 502 pair, because there is no cluster of
136
+ * yours to be unreachable.
137
+ *
138
+ * | Status | Code | Meaning |
139
+ * | --- | --- | --- |
140
+ * | 422 | `validation_failed` | Credentials sent with `type: 'cloud'`, or a `code` that is not on offer -- including when this deployment offers none. |
83
141
  *
84
142
  * **`credentials.type: 'field'`**
85
143
  *
@@ -143,7 +201,13 @@ class DataStores {
143
201
  * | Field | Type | Notes |
144
202
  * | --- | --- | --- |
145
203
  * | `title` | string | |
146
- * | `credentials` | object | Replaced whole, and tested before it is stored. |
204
+ * | `credentials` | object | Replaced whole, and tested before it is stored. `self` stores only. |
205
+ *
206
+ * `type` and `code` are not editable: they say where the records are, not how
207
+ * the store is configured. Sending `credentials` for a **cloud** store is a
208
+ * `422` -- there is nothing to replace, and re-pointing it would leave its
209
+ * records in a database nothing addresses. Register your own database as a
210
+ * second store instead.
147
211
  *
148
212
  * A partial credential is not accepted: send all four fields, or the whole
149
213
  * URI. New credentials go through the same checks as `create`, and the old
@@ -158,10 +222,19 @@ class DataStores {
158
222
  /**
159
223
  * `DELETE /v1/datastores/:store` -- 204.
160
224
  *
161
- * Forgets the credential and every schema declared on it. **Your database is
162
- * not touched**: the collections and all their documents remain, and
163
- * registering the same credential again with the same collection names reads
164
- * them straight back.
225
+ * **A `self` store:** forgets the credential and every schema declared on it.
226
+ * Your database is not touched -- the collections and all their documents
227
+ * remain, and registering the same credential again with the same collection
228
+ * names reads them straight back.
229
+ *
230
+ * **A `cloud` store:** drops every collection in its database, which destroys
231
+ * the database with them. Every record is gone, there is no undo, and a new
232
+ * store of the same name is a new empty database. Confirm with the user before
233
+ * calling this on one.
234
+ *
235
+ * | Status | Code | Meaning |
236
+ * | --- | --- | --- |
237
+ * | 502 | `datastore_purge_failed` | A collection could not be deleted, named in `details`. Nothing was removed -- the store is intact and the call can be retried. |
165
238
  *
166
239
  * Scope: `datastores:write`.
167
240
  */
@@ -1 +1 @@
1
- {"version":3,"file":"data-stores.js","sourceRoot":"","sources":["../../src/resources/data-stores.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-stores.js","sourceRoot":"","sources":["../../src/resources/data-stores.ts"],"names":[],"mappings":"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"}
@@ -130,6 +130,13 @@ export interface Campaign {
130
130
  parentBroadcastId?: string;
131
131
  scheduleId?: string;
132
132
  hidden: boolean;
133
+ /**
134
+ * When the campaign is armed to go out -- and whether it is armed at all.
135
+ *
136
+ * Absent on a campaign nobody has triggered yet, which is the state every
137
+ * campaign starts in. Set by `trigger()` or `schedule()`, never by `create()`
138
+ * or `update()`.
139
+ */
133
140
  scheduledAt?: IsoDate;
134
141
  processedAt?: IsoDate;
135
142
  completedAt?: IsoDate;
@@ -147,11 +154,22 @@ export interface Campaign {
147
154
  createdAt: IsoDate;
148
155
  updatedAt: IsoDate;
149
156
  }
157
+ /**
158
+ * A campaign to write. Writing one does not send it: see `campaigns.trigger()`.
159
+ *
160
+ * There is no `scheduledAt` here on purpose, and the API rejects one rather than
161
+ * ignoring it -- a send time belongs to the act of arming the campaign, which is
162
+ * also what records who armed it.
163
+ */
150
164
  export interface CreateCampaignInput {
151
165
  name: string;
152
166
  /** Defaults to `general`. `createBulk()` sets `bulk_campaign` itself. */
153
167
  type?: string;
154
168
  campaignType?: CampaignType;
169
+ /**
170
+ * How you expect it to go out. Advisory: `trigger()` rewrites it from what it
171
+ * is actually given, so this only matters to your own reading of the record.
172
+ */
155
173
  trigger?: CampaignTrigger;
156
174
  channelType?: string;
157
175
  messagingPattern?: string;
@@ -163,10 +181,18 @@ export interface CreateCampaignInput {
163
181
  repeat?: CampaignRepeat;
164
182
  parentBroadcastId?: string;
165
183
  hidden?: boolean;
166
- /** Required when `trigger` is `schedule`. */
167
- scheduledAt?: IsoDate;
168
184
  }
169
185
  export type UpdateCampaignInput = Partial<CreateCampaignInput>;
186
+ /**
187
+ * When to send. An empty object -- or no argument at all -- means now.
188
+ *
189
+ * "Now" is not instant: the campaign is armed a short way out (a minute by
190
+ * default) so there is a window in which `cancel()` still stops it.
191
+ */
192
+ export interface TriggerCampaignInput {
193
+ /** ISO-8601 with an offset, e.g. `2026-11-01T04:30:00.000Z`. */
194
+ scheduledAt?: IsoDate;
195
+ }
170
196
  /**
171
197
  * Declared as a type alias rather than an interface, and that is load-bearing:
172
198
  * only an alias of an object literal gets TypeScript's implicit index signature,
@@ -1,10 +1,25 @@
1
1
  /**
2
2
  * Data Store: a customer's own MongoDB, registered with the platform.
3
3
  *
4
+ * A store is one of two things, and `type` says which:
5
+ *
6
+ * - **`self`** -- a database **you** own, registered with a credential. The
7
+ * platform holds that credential and nothing else: no copy, no mirror, and
8
+ * deleting the store never touches your data.
9
+ * - **`cloud`** -- a database the **platform** provisions for you, on one of its
10
+ * clusters, in a location you pick. No credential to manage, nothing to open
11
+ * to the internet -- and the platform is then the system of record, so
12
+ * deleting the store deletes the records with it.
13
+ *
14
+ * Everything else is identical: the same collections, the same records, the same
15
+ * calls. Pick `cloud` to store data you do not already have somewhere, and
16
+ * `self` to work with a database you do.
17
+ *
4
18
  * Three nested things:
5
19
  *
6
- * 1. **A data store** -- a connection to a database *you* own. The platform
7
- * holds the credential and nothing else: no copy of the data, no mirror.
20
+ * 1. **A data store** -- the database, yours or ours. For a `self` store the
21
+ * platform holds the credential and nothing else: no copy of the data, no
22
+ * mirror.
8
23
  * 2. **A collection** -- a schema you declare, naming a collection inside that
9
24
  * database and the fields in it.
10
25
  * 3. **Records** -- the documents. They live in your database, and every record
@@ -16,6 +31,21 @@
16
31
  * guarantee -- a document may carry fields the schema never mentioned, or lack
17
32
  * ones it does.
18
33
  */
34
+ /** Whose database the records are in. */
35
+ export type DataStoreType = 'self' | 'cloud';
36
+ /**
37
+ * One cluster the platform can provision a cloud store on.
38
+ *
39
+ * `code` identifies the cluster and is what you send on a create; `location` is
40
+ * the country it is in, which is the half worth showing a user. New locations
41
+ * appear in this list rather than in a release note -- read it, do not hardcode
42
+ * it -- and an existing store never moves when one is added, which is why its
43
+ * `code` is returned on the store itself.
44
+ */
45
+ export interface CloudDataStoreRegion {
46
+ code: string;
47
+ location: string;
48
+ }
19
49
  /** The five storage types a field can declare. */
20
50
  export type DataStoreFieldType = 'String' | 'Number' | 'Boolean' | 'Date' | 'Array';
21
51
  /**
@@ -55,9 +85,32 @@ export type DataStoreCredentials = {
55
85
  */
56
86
  uri: string;
57
87
  };
88
+ /**
89
+ * Register your own database, or ask for one of the platform's.
90
+ *
91
+ * ```ts
92
+ * // yours
93
+ * await crm.dataStores.create({ title: 'Orders DB', credentials: { … } });
94
+ *
95
+ * // theirs, in Germany
96
+ * await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
97
+ * ```
98
+ *
99
+ * `type` defaults to `self`, so existing code is unaffected. The two halves do
100
+ * not mix: a `self` store needs `credentials` and takes no `code`, a `cloud`
101
+ * store takes a `code` and no credentials, and anything else is a `422`.
102
+ */
58
103
  export interface CreateDataStoreInput {
59
104
  title: string;
60
- credentials: DataStoreCredentials;
105
+ /** `self` -- your database. `cloud` -- one of the platform's. Default `self`. */
106
+ type?: DataStoreType;
107
+ /** Required for `self`, and refused for `cloud`. */
108
+ credentials?: DataStoreCredentials;
109
+ /**
110
+ * `cloud` only: which cluster to put it on, from
111
+ * {@link DataStores.regions}. Omitted, the platform picks its default.
112
+ */
113
+ code?: string;
61
114
  }
62
115
  /**
63
116
  * Rename, re-point, or both.
@@ -81,12 +134,25 @@ export interface UpdateDataStoreInput {
81
134
  export interface DataStore {
82
135
  id: string;
83
136
  title: string;
84
- credentialType: 'field' | 'string';
137
+ /** Whose database it is. Stores created before cloud stores existed are `self`. */
138
+ type: DataStoreType;
139
+ /** `cloud` only: the cluster it is on. Quote it in a support request. */
140
+ code?: string;
141
+ /** `cloud` only: where that cluster is -- `Germany`. */
142
+ location?: string;
143
+ /** How the credential was given. Absent on a `cloud` store, which has none. */
144
+ credentialType?: 'field' | 'string';
85
145
  engine: 'mongodb';
146
+ /** Your host. Empty on a `cloud` store: the platform's own is not disclosed. */
86
147
  host: string;
87
148
  database: string;
149
+ /** Your database user. Empty on a `cloud` store. */
88
150
  username: string;
89
- /** The connection string with the password removed, for display. */
151
+ /**
152
+ * For display. The connection string with the password removed for a `self`
153
+ * store, and `cloud://<location>/<database>` for a cloud one -- a label, not
154
+ * something to connect with.
155
+ */
90
156
  summary: string;
91
157
  /** When the connection was last proven to work: a create, an update, or a test. */
92
158
  verifiedAt?: string;
@@ -2,10 +2,25 @@
2
2
  /**
3
3
  * Data Store: a customer's own MongoDB, registered with the platform.
4
4
  *
5
+ * A store is one of two things, and `type` says which:
6
+ *
7
+ * - **`self`** -- a database **you** own, registered with a credential. The
8
+ * platform holds that credential and nothing else: no copy, no mirror, and
9
+ * deleting the store never touches your data.
10
+ * - **`cloud`** -- a database the **platform** provisions for you, on one of its
11
+ * clusters, in a location you pick. No credential to manage, nothing to open
12
+ * to the internet -- and the platform is then the system of record, so
13
+ * deleting the store deletes the records with it.
14
+ *
15
+ * Everything else is identical: the same collections, the same records, the same
16
+ * calls. Pick `cloud` to store data you do not already have somewhere, and
17
+ * `self` to work with a database you do.
18
+ *
5
19
  * Three nested things:
6
20
  *
7
- * 1. **A data store** -- a connection to a database *you* own. The platform
8
- * holds the credential and nothing else: no copy of the data, no mirror.
21
+ * 1. **A data store** -- the database, yours or ours. For a `self` store the
22
+ * platform holds the credential and nothing else: no copy of the data, no
23
+ * mirror.
9
24
  * 2. **A collection** -- a schema you declare, naming a collection inside that
10
25
  * database and the fields in it.
11
26
  * 3. **Records** -- the documents. They live in your database, and every record
@@ -1 +1 @@
1
- {"version":3,"file":"datastore.js","sourceRoot":"","sources":["../../src/types/datastore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG"}
1
+ {"version":3,"file":"datastore.js","sourceRoot":"","sources":["../../src/types/datastore.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG"}
@@ -331,6 +331,17 @@ There is no presence check: an agent with the conversation open on screen still
331
331
  gets the push on their phone. That is the behaviour the panel has always had, and
332
332
  changing it is a product decision rather than a detail of delivery.
333
333
 
334
+ A person is matched under **either of their two ids**. A request carries the
335
+ ecosystem's `psuid`, which is what `subscribe()` files a device under; the inbox
336
+ trigger reads its recipient off a chat row the legacy stack wrote, so the id it
337
+ holds is a legacy one. Registration records both, and the lookup matches the one
338
+ id it is given against either field — so it resolves one person under two
339
+ names, and never returns a colleague's devices.
340
+
341
+ The practical consequence: a device registered before this existed is still
342
+ reachable by `psuid` exactly as before, and the next `subscribe()` call — which
343
+ the frontend makes on every app load — fills in the second id.
344
+
334
345
  ### Rendering it in a browser
335
346
 
336
347
  If the payload carries a `notification` block, FCM displays it and opens
@@ -13,12 +13,50 @@ One message sent to many people.
13
13
  | [`createGroup`](#creategroup) | uploads + `POST /v1/campaigns` | **one bucket per channel or template** |
14
14
  | [`uploadReceivers`](#uploadreceivers) | upload only | the file half of `createBulk` |
15
15
  | [`update`](#update) | `PATCH /v1/campaigns/:id` | before it runs |
16
- | [`schedule`](#schedule) | `POST /v1/campaigns/:id/schedule` | |
16
+ | [`trigger`](#trigger) | `POST /v1/campaigns/:id/trigger` | **the only call that sends** |
17
+ | [`schedule`](#schedule) | `POST /v1/campaigns/:id/schedule` | `trigger` with a required time |
17
18
  | [`cancel`](#cancel) | `POST /v1/campaigns/:id/cancel` | |
18
19
  | [`delete`](#delete) | `DELETE /v1/campaigns/:id` | |
19
20
 
20
21
  ---
21
22
 
23
+ ## Creating a campaign does not send it
24
+
25
+ Two steps, always:
26
+
27
+ ```ts
28
+ const campaign = await crm.campaigns.create({ name: 'Diwali offer', messages });
29
+ // nothing has gone out; `campaign.scheduledAt` is undefined
30
+
31
+ await crm.campaigns.trigger(campaign.id); // send now
32
+ await crm.campaigns.trigger(campaign.id, { scheduledAt: isoInstant }); // or at a time
33
+ ```
34
+
35
+ A created campaign is a **draft**: `pending`, with no `scheduledAt` and an empty
36
+ `triggerInfo`. You can `update` it, show it for review, and nothing will send it.
37
+ The sender cannot even see it — a campaign with no `scheduledAt` is not a
38
+ campaign with a moment.
39
+
40
+ `trigger` is what arms it, and is also what records *who* armed it: the server
41
+ stamps `triggerInfo` (the caller, their ip, the moment) as part of that call.
42
+ That is why a send time is **rejected** on `create` and `update` rather than
43
+ accepted — a campaign that could be armed by a write could go out with nobody
44
+ named against it.
45
+
46
+ | | `create` / `update` | `trigger` / `schedule` |
47
+ | --- | --- | --- |
48
+ | `scheduledAt` in the body | **`422`** | that is the point |
49
+ | writes `triggerInfo` | no | yes |
50
+ | campaign becomes sendable | no | yes |
51
+
52
+ **"Now" is not instant.** `trigger` with no time arms the campaign about a minute
53
+ out (`CAMPAIGN_IMMEDIATE_DELAY_SECONDS`), and [`cancel`](#cancel) still stops it
54
+ in that window. A second `trigger` on a campaign that is armed but not yet picked
55
+ up just re-arms it, so a double-click costs a fresh window rather than a second
56
+ send.
57
+
58
+ ---
59
+
22
60
  ## The audience is one field with five modes
23
61
 
24
62
  A campaign's recipients live in `filters`, and **the mode is decided by the shape
@@ -182,16 +220,18 @@ calls, so what you preview is what gets sent.
182
220
  ### Scheduling one
183
221
 
184
222
  ```ts
185
- await crm.campaigns.createBulk({
223
+ const { campaign } = await crm.campaigns.createBulk({
186
224
  name: 'Renewal reminder',
187
225
  audience: { csv, recipientColumn: 'phone' },
188
226
  messages: [{ channel: { id: channelId }, message: template }],
189
- trigger: 'schedule',
190
- scheduledAt: '2026-11-01T04:30:00.000Z',
191
227
  });
228
+
229
+ await crm.campaigns.trigger(campaign.id, { scheduledAt: '2026-11-01T04:30:00.000Z' });
192
230
  ```
193
231
 
194
- The file is uploaded immediately; only the send waits.
232
+ The file is uploaded during `createBulk`; the campaign itself does not go out
233
+ until you trigger it. Passing `scheduledAt` to `createBulk` is refused — see
234
+ [creating does not send](#creating-a-campaign-does-not-send-it).
195
235
 
196
236
  ### Two things to be careful about
197
237
 
@@ -385,19 +425,33 @@ const { data, nextCursor } = await crm.campaigns.list({ limit: 50, status: 'comp
385
425
  const campaign = await crm.campaigns.get(id);
386
426
  ```
387
427
 
428
+ ### What a campaign's `id` is
429
+
430
+ The `id` on a campaign is its own identity and the only one this API accepts —
431
+ the same value the campaign manager is handed as `broadcast.id` and stamps on
432
+ every report row as `cid`. So it is what you pass to `get`, `trigger`, `report`,
433
+ and what a re-target filter's `cid` must name.
434
+
435
+ It is a 24-character ObjectId string, but it is not the document's `_id`: that
436
+ is Mongo's key and never appears in a request or a response. The `nextCursor`
437
+ from [`list`](#list) is a paging position rather than an id — pass it back as a
438
+ cursor, never as a campaign id.
439
+
388
440
  ## `create`
389
441
 
390
442
  For an audience you have already expressed as `filters` — every contact,
391
443
  a segment, or a re-target. For a bulk list use [`createBulk`](#createbulk).
392
444
 
445
+ Returns a draft. Call [`trigger`](#trigger) to send it.
446
+
393
447
  | Field | Type | Default |
394
448
  | --- | --- | --- |
395
449
  | `name` | `string` | required, 1–160 |
396
450
  | `messages` | `CampaignMessage[]` | required, at least one |
397
- | `type` | `string` | `general` |
451
+ | `type` | `string` | `general` — also decides the dispatch pacing, see [below](#trigger) |
398
452
  | `campaignType` | `general` `payment` | `general` |
399
- | `trigger` | `immediate` `schedule` | `immediate` |
400
- | `scheduledAt` | ISO datetime | required when `trigger` is `schedule` |
453
+ | `trigger` | `immediate` `schedule` | `immediate` — advisory; `trigger()` rewrites it |
454
+ | `scheduledAt` | — | **not accepted**; `422`. Pass it to [`trigger`](#trigger) |
401
455
  | `channelType` | `string` | `whatsapp` |
402
456
  | `messagingPattern` | `string` | `A2O` |
403
457
  | `filters` | `CampaignFilter[]` | `[]` — every contact |
@@ -412,7 +466,49 @@ a segment, or a re-target. For a bulk list use [`createBulk`](#createbulk).
412
466
  await crm.campaigns.update(id, { name: 'Diwali offer (revised)' });
413
467
  ```
414
468
 
415
- Every `create` field, all optional. Only meaningful before the campaign runs.
469
+ Every `create` field, all optional — `scheduledAt` included, which means it is
470
+ refused here too. Only meaningful while the campaign is `pending`.
471
+
472
+ ## `trigger`
473
+
474
+ ```ts
475
+ await crm.campaigns.trigger(id); // now
476
+ await crm.campaigns.trigger(id, { scheduledAt: '2026-11-01T04:30:00.000Z' });
477
+ ```
478
+
479
+ | | |
480
+ | --- | --- |
481
+ | Route | `POST /v1/campaigns/:id/trigger` |
482
+ | Body | `{}` or `{ scheduledAt }` — ISO-8601 with an offset |
483
+ | Scope | `campaigns:write` |
484
+ | `404` | no such campaign, or not this account's |
485
+ | `409` | the campaign is not `pending` — already sending, sent, failed or cancelled |
486
+ | `409` | `channel_webhook_unset`: a channel cannot report delivery back (see below) |
487
+
488
+ Writes `trigger`, `scheduledAt` and `triggerInfo`, and nothing else. A past
489
+ `scheduledAt` is accepted and means "now".
490
+
491
+ **The webhook preflight runs here.** Before the campaign is armed, each v2
492
+ WhatsApp channel's Meta subscription is checked and repaired if it has drifted. A
493
+ channel that still cannot deliver webhooks fails the call with `409
494
+ channel_webhook_unset` and the campaign stays a draft — because that failure is
495
+ otherwise invisible: an unsubscribed channel *sends perfectly* and reports
496
+ nothing, leaving every recipient stuck at `sent` forever.
497
+
498
+ ### How the campaign reaches the sender
499
+
500
+ Once armed, a poller picks the campaign up when its moment arrives and hands it
501
+ to the sending service. Which route it takes depends on `type`:
502
+
503
+ | `type` | route |
504
+ | --- | --- |
505
+ | `general` | straight to the sender |
506
+ | anything else (`bulk_campaign`, `group_campaign`, `retarget`, …) | through the account's pacing lane — `<user_id>_user_campaign`, shared with the legacy middleware — which releases one campaign per minute per account |
507
+
508
+ So an account that arms five bulk campaigns at once has them delivered to the
509
+ sender a minute apart, in the order they were armed, while a general campaign
510
+ goes immediately. This is invisible in the API — the only trace is when the
511
+ report starts filling.
416
512
 
417
513
  ## `schedule`
418
514
 
@@ -420,6 +516,9 @@ Every `create` field, all optional. Only meaningful before the campaign runs.
420
516
  await crm.campaigns.schedule(id, '2026-11-01T04:30:00.000Z');
421
517
  ```
422
518
 
519
+ The same call as `trigger` with a required time. Kept because it is the name the
520
+ panel uses; new code can use either.
521
+
423
522
  ## `cancel`
424
523
 
425
524
  ```ts
@@ -2,9 +2,18 @@
2
2
 
3
3
  `crm.dataStores`, `crm.dataStoreCollections`, `crm.dataStoreRecords` — `/v1/datastores`
4
4
 
5
- A MongoDB **you** own, registered with the platform. You give it a connection,
6
- declare the collections and fields you want, and read and write records through
7
- the API — from a chatbot flow, an automation, or your own code.
5
+ A MongoDB — **yours**, registered with a credential, or **ours**, provisioned
6
+ for you in a location you pick. Either way you declare the collections and
7
+ fields you want and read and write records through the API: from a chatbot flow,
8
+ an automation, or your own code.
9
+
10
+ ```ts
11
+ // your own cluster
12
+ await crm.dataStores.create({ title: 'Orders DB', credentials: { … } });
13
+
14
+ // or one of ours, no credential to manage
15
+ await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
16
+ ```
8
17
 
9
18
  This runs as **its own service**, with its own database and its own deployment.
10
19
  Nothing else in the platform depends on it, and it depends on nothing else: a
@@ -22,12 +31,84 @@ connections it holds to customer clusters cannot crowd out anything but itself.
22
31
  Three things, nested:
23
32
 
24
33
  ```
25
- data store a connection to a database you own
34
+ data store a database — yours by credential, or ours by request
26
35
  └── collection a schema: one collection name, and its fields
27
- └── records the documents, in your database
36
+ └── records the documents, in that database
28
37
  ```
29
38
 
30
- The platform holds **the credential and the schemas, and nothing else**. There
39
+ ## Two kinds of store
40
+
41
+ `type` on a store says whose database the records are actually in, and it is the
42
+ one decision here with consequences:
43
+
44
+ | | `self` *(default)* | `cloud` |
45
+ | --- | --- | --- |
46
+ | The database | Yours | Ours, provisioned per store |
47
+ | To create one | A credential, tested before anything is stored | Nothing. Optionally a `code` for the location |
48
+ | Reachability | Your cluster must be reachable from the internet, and usually needs our egress in its IP access list | Nothing to open, nothing to allow |
49
+ | System of record | You. Anything else with the credential writes the same documents | Us. The API is the only door |
50
+ | `remove()` on the store | Forgets the credential. **Your data stays** | **Drops the database.** The records are gone |
51
+
52
+ Pick `cloud` for data that does not already live somewhere — a form's
53
+ submissions, a chatbot's answers, a lookup table someone maintains in the panel.
54
+ Pick `self` when the data is already in your cluster, or has to stay there.
55
+
56
+ Everything past this point — collections, records, filters, paging — is
57
+ identical for both.
58
+
59
+ ### Where a cloud store lives
60
+
61
+ ```ts
62
+ const { data: regions } = await crm.dataStores.regions();
63
+ // [{ code: '6869Z798672Z296Z152Z286Z307', location: 'Germany' }]
64
+
65
+ const store = await crm.dataStores.create({
66
+ title: 'Leads',
67
+ type: 'cloud',
68
+ code: regions[0].code, // omit it and the platform picks its default
69
+ });
70
+
71
+ store.location; // 'Germany'
72
+ store.code; // the cluster it is on — quote it in a support request
73
+ ```
74
+
75
+ Read `regions()` rather than hardcoding a code: locations are added over time,
76
+ and an existing store **never moves** when one is added. That is why the code
77
+ comes back on the store itself, and it is the answer to a data-residency
78
+ question in writing.
79
+
80
+ An empty `regions()` means this deployment provisions none — an on-premise
81
+ install, typically — and a `type: 'cloud'` create is then a `422`.
82
+
83
+ Nothing about our cluster is returned on a cloud store: `host` and `username`
84
+ are empty, there is no `credentialType`, and `summary` is a label
85
+ (`cloud://Germany/ds_…`) rather than a connection string. There is no second way
86
+ into those records, by design.
87
+
88
+ ## What `remove()` deletes
89
+
90
+ Worth stating before anything else, because the opposite assumption is the
91
+ expensive one in both directions:
92
+
93
+ | Call | What it removes |
94
+ | --- | --- |
95
+ | `dataStores.remove()` on a **`self`** store | The stored credential, and every schema on it. **Your database is untouched.** |
96
+ | `dataStores.remove()` on a **`cloud`** store | The schemas, and **every collection in its database — which destroys the database, irreversibly.** |
97
+ | `dataStoreCollections.remove()` | The schema. The collection and its documents remain, in either kind of store. |
98
+ | `dataStoreRecords.remove()` / `bulkRemove()` | Documents, irreversibly. |
99
+
100
+ For a `self` store: register the same credential again, declare the same
101
+ collection name, and all of it reads straight back. For a `cloud` store there is
102
+ nothing to re-register — the database was named after that store and a new one
103
+ is empty — so confirm with the user before calling it.
104
+
105
+ If a collection cannot be deleted, the call is a `502 datastore_purge_failed`
106
+ naming it, and **nothing** has been removed: the store and its schemas are
107
+ intact and the call can be retried once the cause is fixed.
108
+
109
+ ## What the platform holds
110
+
111
+ For a `self` store, **the credential and the schemas, and nothing else**. There
31
112
  is no copy of your data here, no mirror and no sync: a record read is a live
32
113
  query against your cluster, and a record write lands in it before the response
33
114
  comes back.
@@ -43,19 +124,9 @@ Two consequences worth holding on to:
43
124
  TLS handshake each time — but a loop of a thousand single inserts is a
44
125
  thousand round trips. Send one `insert` with `records`.
45
126
 
46
- ## Deleting never deletes your data
47
-
48
- Worth stating before anything else, because the opposite assumption is the
49
- expensive one:
50
-
51
- | Call | What it removes |
52
- | --- | --- |
53
- | `dataStores.remove()` | The stored credential, and every schema on it. **Your database is untouched.** |
54
- | `dataStoreCollections.remove()` | The schema. **Your collection and its documents remain.** |
55
- | `dataStoreRecords.remove()` / `bulkRemove()` | Documents, from your database, irreversibly. |
56
-
57
- Register the same credential again, declare the same collection name, and all of
58
- it reads straight back. Only the record calls delete anything of yours.
127
+ For a `cloud` store the platform holds the database too, and the first
128
+ consequence does not apply: nothing else can write those documents. The second
129
+ still does — it is a different cluster, not a local one.
59
130
 
60
131
  ---
61
132
 
@@ -140,6 +211,15 @@ order to show which database it is looking at. Host, user and database come back
140
211
  in the clear because an edit form has to render them, and none of them opens a
141
212
  connection on its own.
142
213
 
214
+ A cloud store takes no credentials at all:
215
+
216
+ ```ts
217
+ await crm.dataStores.create({ title: 'Leads', type: 'cloud' });
218
+ ```
219
+
220
+ Sending both is a `422`, as is a `code` on a `self` store: the two halves of the
221
+ body do not mix.
222
+
143
223
  ### Re-pointing one
144
224
 
145
225
  ```ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ab-ecosystem-cpass",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "JavaScript/TypeScript SDK for the CPaaS CRM gateway: audience and channels",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",