ab-ecosystem-cpass 1.0.7 → 1.0.8

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 (38) hide show
  1. package/README.md +5 -1
  2. package/dist/client.d.ts +24 -0
  3. package/dist/client.js +28 -0
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +7 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/resources/audience/canned-replies.d.ts +95 -0
  9. package/dist/resources/audience/canned-replies.js +112 -0
  10. package/dist/resources/audience/canned-replies.js.map +1 -0
  11. package/dist/resources/audience/notifications.d.ts +14 -0
  12. package/dist/resources/audience/notifications.js +14 -0
  13. package/dist/resources/audience/notifications.js.map +1 -1
  14. package/dist/resources/campaign-messages.d.ts +25 -22
  15. package/dist/resources/campaign-messages.js +34 -27
  16. package/dist/resources/campaign-messages.js.map +1 -1
  17. package/dist/resources/data-stores.d.ts +469 -0
  18. package/dist/resources/data-stores.js +530 -0
  19. package/dist/resources/data-stores.js.map +1 -0
  20. package/dist/resources/paths.d.ts +6 -0
  21. package/dist/resources/paths.js +7 -1
  22. package/dist/resources/paths.js.map +1 -1
  23. package/dist/types/campaign.d.ts +11 -5
  24. package/dist/types/canned-reply.d.ts +124 -0
  25. package/dist/types/canned-reply.js +6 -0
  26. package/dist/types/canned-reply.js.map +1 -0
  27. package/dist/types/datastore.d.ts +260 -0
  28. package/dist/types/datastore.js +21 -0
  29. package/dist/types/datastore.js.map +1 -0
  30. package/dist/types/index.d.ts +2 -0
  31. package/dist/types/index.js +3 -0
  32. package/dist/types/index.js.map +1 -1
  33. package/docs/audience/canned-replies.md +302 -0
  34. package/docs/audience/notifications.md +99 -0
  35. package/docs/campaign/campaign-messages.md +31 -22
  36. package/docs/campaign/campaigns.md +1 -1
  37. package/docs/data-store.md +495 -0
  38. package/package.json +1 -1
@@ -1,18 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CampaignMessages = void 0;
4
- const BASE = '/v1/campaign-messages';
4
+ const paths_1 = require("./paths");
5
+ /** `/v1/campaigns/:id/report` -- the report is the campaign's own sub-resource. */
6
+ const report = (campaign) => `/v1/campaigns/${(0, paths_1.seg)(campaign)}/report`;
5
7
  /**
6
8
  * Campaign reporting: what actually happened to each message.
7
9
  *
8
10
  * ```ts
9
- * const stats = await crm.campaignMessages.stats({ campaign });
11
+ * const stats = await crm.campaignMessages.stats(campaign);
10
12
  * // { total: 12480, sent: 12410, delivered: 11980, read: 7420, replied: 310, failed: 70 }
11
13
  *
12
- * const { data } = await crm.campaignMessages.list({ campaign, status: 'failed' });
14
+ * const { data } = await crm.campaignMessages.list(campaign, { status: 'failed' });
13
15
  * data[0].error; // why that one did not go
14
16
  * ```
15
17
  *
18
+ * Every method takes the campaign id first. A report has no meaning without one,
19
+ * so it is addressed as the campaign's own sub-resource --
20
+ * `/v1/campaigns/:id/report` -- rather than as a collection of rows that a
21
+ * campaign filters. The options object holds only what *narrows* a report that
22
+ * already exists: a message index, a status, a page.
23
+ *
16
24
  * ## Where these numbers come from
17
25
  *
18
26
  * One row per attempted send, written by the campaign manager as it hands each
@@ -47,18 +55,17 @@ class CampaignMessages {
47
55
  /**
48
56
  * The campaign's funnel counts.
49
57
  *
50
- * `GET /v1/campaign-messages/stats?campaign=<id>` · `campaigns:read` · **200**
58
+ * `GET /v1/campaigns/:id/report` · `campaigns:read` · **200**
51
59
  *
52
60
  * ### Query
53
61
  *
54
- * | Field | Type | Required | Notes |
55
- * | --- | --- | --- | --- |
56
- * | `campaign` | string | yes | The campaign id. |
57
- * | `message` | number | no | One message of a multi-message campaign, zero-based. |
62
+ * | Field | Type | Notes |
63
+ * | --- | --- | --- |
64
+ * | `message` | number | One message of a multi-message campaign, zero-based. Omit for the whole campaign. |
58
65
  *
59
66
  * ```ts
60
- * const overall = await crm.campaignMessages.stats({ campaign });
61
- * const second = await crm.campaignMessages.stats({ campaign, message: 1 });
67
+ * const overall = await crm.campaignMessages.stats(campaign);
68
+ * const second = await crm.campaignMessages.stats(campaign, { message: 1 });
62
69
  * ```
63
70
  *
64
71
  * No date window: a campaign is one send, so its whole life is the only
@@ -68,19 +75,18 @@ class CampaignMessages {
68
75
  * `404 not_found` if the campaign is not the caller's -- the same answer as a
69
76
  * campaign that does not exist, so the error cannot be used to probe ids.
70
77
  */
71
- stats(query) {
72
- return this.http.request('GET', `${BASE}/stats`, { query });
78
+ stats(campaign, query = {}) {
79
+ return this.http.request('GET', report(campaign), { query });
73
80
  }
74
81
  /**
75
82
  * The rows behind the funnel. This is the report table.
76
83
  *
77
- * `GET /v1/campaign-messages?campaign=<id>` · `campaigns:read` · **200**
84
+ * `GET /v1/campaigns/:id/report/messages` · `campaigns:read` · **200**
78
85
  *
79
86
  * ### Query
80
87
  *
81
88
  * | Field | Type | Default | Notes |
82
89
  * | --- | --- | --- | --- |
83
- * | `campaign` | string | — | Required. |
84
90
  * | `message` | number | — | Zero-based index of one campaign message. |
85
91
  * | `status` | `sent \| delivered \| read \| replied \| failed` | — | Cumulative; see below. Omit for every row. |
86
92
  * | `exact` | boolean | `false` | Only rows sitting at `status` right now. |
@@ -96,22 +102,22 @@ class CampaignMessages {
96
102
  *
97
103
  * ```ts
98
104
  * // the two queries a report screen actually makes
99
- * const failures = await crm.campaignMessages.list({ campaign, status: 'failed', rows: 100 });
100
- * const unread = await crm.campaignMessages.list({ campaign, status: 'delivered', exact: true });
105
+ * const failures = await crm.campaignMessages.list(campaign, { status: 'failed', rows: 100 });
106
+ * const unread = await crm.campaignMessages.list(campaign, { status: 'delivered', exact: true });
101
107
  * ```
102
108
  *
103
109
  * `reply_type` and `replied_within_hours` are **refused** with
104
- * `400 validation_failed` unless `status` is `replied`, rather than quietly
110
+ * `422 validation_failed` unless `status` is `replied`, rather than quietly
105
111
  * matching nothing.
106
112
  */
107
- list(query) {
108
- return this.http.request('GET', BASE, { query });
113
+ list(campaign, query = {}) {
114
+ return this.http.request('GET', `${report(campaign)}/messages`, { query });
109
115
  }
110
116
  /**
111
117
  * Every matching row, paged through for you. What an export is.
112
118
  *
113
119
  * ```ts
114
- * for await (const row of crm.campaignMessages.iterate({ campaign })) {
120
+ * for await (const row of crm.campaignMessages.iterate(campaign)) {
115
121
  * csv.write([row.identifier, row.name, row.status, row.error ?? '']);
116
122
  * }
117
123
  * ```
@@ -126,11 +132,11 @@ class CampaignMessages {
126
132
  * moving data can repeat or skip a row -- for a stable snapshot, export after
127
133
  * the campaign completes.
128
134
  */
129
- async *iterate(query) {
135
+ async *iterate(campaign, query = {}) {
130
136
  const rows = query.rows ?? 100;
131
137
  let page = query.page ?? 1;
132
138
  for (;;) {
133
- const result = await this.list({ ...query, page, rows });
139
+ const result = await this.list(campaign, { ...query, page, rows });
134
140
  for (const record of result.data)
135
141
  yield record;
136
142
  if (result.data.length === 0 || page >= result.pagination.pages)
@@ -141,18 +147,17 @@ class CampaignMessages {
141
147
  /**
142
148
  * What people replied, grouped by the reply itself.
143
149
  *
144
- * `GET /v1/campaign-messages/replies?campaign=<id>` · `campaigns:read` · **200**
150
+ * `GET /v1/campaigns/:id/report/replies` · `campaigns:read` · **200**
145
151
  *
146
152
  * ### Query
147
153
  *
148
154
  * | Field | Type | Default | Notes |
149
155
  * | --- | --- | --- | --- |
150
- * | `campaign` | string | — | Required. |
151
156
  * | `message` | number | — | Zero-based index of one campaign message. |
152
157
  * | `limit` | number | `50` | Distinct replies to return, 1–200. |
153
158
  *
154
159
  * ```ts
155
- * const { groups, replied, distinct } = await crm.campaignMessages.replies({ campaign });
160
+ * const { groups, replied, distinct } = await crm.campaignMessages.replies(campaign);
156
161
  * groups; // [{ reply: 'Yes', replyType: 'button', count: 182 }, …]
157
162
  * ```
158
163
  *
@@ -164,8 +169,10 @@ class CampaignMessages {
164
169
  * complete. A campaign that asked an open question has as many groups as it
165
170
  * has repliers, so a truncated answer there is normal rather than a problem.
166
171
  */
167
- replies(query) {
168
- return this.http.request('GET', `${BASE}/replies`, { query });
172
+ replies(campaign, query = {}) {
173
+ return this.http.request('GET', `${report(campaign)}/replies`, {
174
+ query,
175
+ });
169
176
  }
170
177
  }
171
178
  exports.CampaignMessages = CampaignMessages;
@@ -1 +1 @@
1
- {"version":3,"file":"campaign-messages.js","sourceRoot":"","sources":["../../src/resources/campaign-messages.ts"],"names":[],"mappings":";;;AAWA,MAAM,IAAI,GAAG,uBAAuB,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAa,gBAAgB;IACE;IAA7B,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,KAAgC;QACpC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAuB,KAAK,EAAE,GAAG,IAAI,QAAQ,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACpF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,IAAI,CAAC,KAAgC;QACnC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAmC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,CAAC,OAAO,CAAC,KAAgC;QAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC;QAC/B,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAE3B,SAAS,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM,MAAM,CAAC;YAE/C,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK;gBAAE,OAAO;YACxE,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,OAAO,CAAC,KAA2B;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAyB,KAAK,EAAE,GAAG,IAAI,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACxF,CAAC;CACF;AAjID,4CAiIC"}
1
+ {"version":3,"file":"campaign-messages.js","sourceRoot":"","sources":["../../src/resources/campaign-messages.ts"],"names":[],"mappings":";;;AAUA,mCAA8B;AAE9B,mFAAmF;AACnF,MAAM,MAAM,GAAG,CAAC,QAAgB,EAAU,EAAE,CAAC,iBAAiB,IAAA,WAAG,EAAC,QAAQ,CAAC,SAAS,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAa,gBAAgB;IACE;IAA7B,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,QAAgB,EAAE,QAAmC,EAAE;QAC3D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAuB,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,IAAI,CACF,QAAgB,EAChB,QAAmC,EAAE;QAErC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CACtB,KAAK,EACL,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAC9B,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACH,KAAK,CAAC,CAAC,OAAO,CACZ,QAAgB,EAChB,QAAmC,EAAE;QAErC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC;QAC/B,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC;QAE3B,SAAS,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM,MAAM,CAAC;YAE/C,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK;gBAAE,OAAO;YACxE,IAAI,IAAI,CAAC,CAAC;QACZ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,QAAgB,EAAE,QAA8B,EAAE;QACxD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAyB,KAAK,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE;YACrF,KAAK;SACN,CAAC,CAAC;IACL,CAAC;CACF;AA1ID,4CA0IC"}
@@ -0,0 +1,469 @@
1
+ import type { BulkDeleteDataStoreRecordsInput, BulkUpdateDataStoreRecordsInput, CreateDataStoreCollectionInput, CreateDataStoreInput, DataStore, DataStoreCollection, DataStoreDeleteResult, DataStoreRecord, DataStoreTestResult, DataStoreUpdateResult, DataStoreWriteResult, InsertDataStoreRecordsInput, ListDataStoreRecordsQuery, ListQuery, Paginated, SearchDataStoreRecordsInput, UpdateDataStoreCollectionInput, UpdateDataStoreInput, UpdateDataStoreRecordInput } from '../types';
2
+ import type { HttpClient } from '../http';
3
+ /**
4
+ * Data stores: a MongoDB **you** own, registered with the platform.
5
+ *
6
+ * ```ts
7
+ * const store = await crm.dataStores.create({
8
+ * title: 'Orders DB',
9
+ * credentials: {
10
+ * type: 'field',
11
+ * host: 'cluster0.ab12c.mongodb.net',
12
+ * username: 'crm_writer',
13
+ * password: process.env.ORDERS_DB_PASSWORD!,
14
+ * database: 'shop',
15
+ * },
16
+ * });
17
+ *
18
+ * const orders = await crm.dataStoreCollections.create(store.id, {
19
+ * title: 'Orders',
20
+ * fields: [
21
+ * { name: 'order_no', type: 'String' },
22
+ * { name: 'total', type: 'Number' },
23
+ * { name: 'paid', type: 'Boolean', default: false },
24
+ * ],
25
+ * });
26
+ *
27
+ * await crm.dataStoreRecords.insert(store.id, orders.id, {
28
+ * record: { order_no: 'A-1001', total: 49.9 },
29
+ * });
30
+ * ```
31
+ *
32
+ * ## What the platform holds, and what it does not
33
+ *
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.
38
+ *
39
+ * Which also means every record call costs a round trip to your database. The
40
+ * platform keeps the connection open between calls, so that is one query's
41
+ * latency rather than a fresh TLS handshake each time, but a loop of a thousand
42
+ * single inserts is a thousand round trips: send them as one `insert` with
43
+ * `records` instead.
44
+ *
45
+ * ## Deleting never deletes your data
46
+ *
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.
53
+ *
54
+ * ## The host must be reachable from the internet
55
+ *
56
+ * A credential pointing at a private or loopback address is refused with
57
+ * `422 invalid_datastore_host`: the platform would be connecting to its own
58
+ * network rather than to yours. Atlas users will usually also need to allow the
59
+ * platform's egress addresses in their project's IP access list -- the symptom
60
+ * otherwise is `502 datastore_unreachable` with a server-selection timeout.
61
+ */
62
+ export declare class DataStores {
63
+ private readonly http;
64
+ constructor(http: HttpClient);
65
+ /**
66
+ * `POST /v1/datastores` -- 201 with the store.
67
+ *
68
+ * ### Payload
69
+ *
70
+ * | Field | Type | Required | Notes |
71
+ * | --- | --- | --- | --- |
72
+ * | `title` | string | yes | What you call it. 1-120 characters. |
73
+ * | `credentials` | object | yes | Either form below. |
74
+ *
75
+ * **`credentials.type: 'field'`**
76
+ *
77
+ * | Field | Type | Notes |
78
+ * | --- | --- | --- |
79
+ * | `host` | string | Hostname only: `cluster0.ab12c.mongodb.net`. |
80
+ * | `username` | string | |
81
+ * | `password` | string | Encrypted at rest; never returned. |
82
+ * | `database` | string | |
83
+ *
84
+ * Assembled into `mongodb+srv://…?retryWrites=true&w=majority`, with both
85
+ * halves of the credential percent-encoded -- so a password containing `@`,
86
+ * `/` or `?` works, which it did not in the legacy panel.
87
+ *
88
+ * **`credentials.type: 'string'`**
89
+ *
90
+ * | Field | Type | Notes |
91
+ * | --- | --- | --- |
92
+ * | `uri` | string | `mongodb://` or `mongodb+srv://`, and it must name a database. |
93
+ *
94
+ * ### What happens before the row is written
95
+ *
96
+ * The credential is checked for shape, the host is resolved and refused if it
97
+ * is private, and a connection is opened and pinged. Only then is anything
98
+ * stored -- so a 201 means the database really did accept these credentials,
99
+ * and a store you can see is a store that worked at least once.
100
+ *
101
+ * | Status | Code | Meaning |
102
+ * | --- | --- | --- |
103
+ * | 422 | `validation_failed` | Malformed URI, a host with a scheme in it, or no database named. |
104
+ * | 422 | `invalid_datastore_host` | The host does not resolve, or resolves somewhere private. |
105
+ * | 502 | `datastore_auth_failed` | The database refused the username or password. |
106
+ * | 502 | `datastore_unreachable` | No answer in time -- usually an IP access list. |
107
+ *
108
+ * Scope: `datastores:write`.
109
+ */
110
+ create(input: CreateDataStoreInput): Promise<DataStore>;
111
+ /**
112
+ * `GET /v1/datastores` -- offset paginated, newest first.
113
+ *
114
+ * | Query | Type | Default | Notes |
115
+ * | --- | --- | --- | --- |
116
+ * | `page` | number | `1` | 1-based. |
117
+ * | `rows` | number | `25` | 1-100. |
118
+ * | `search` | string | — | Case-insensitive match on the title. |
119
+ *
120
+ * Scope: `datastores:read`.
121
+ */
122
+ list(query?: Partial<ListQuery>): Promise<Paginated<DataStore>>;
123
+ /** `GET /v1/datastores/:store`. Scope: `datastores:read`. */
124
+ get(store: string): Promise<DataStore>;
125
+ /**
126
+ * `PATCH /v1/datastores/:store` -- rename, re-point, or both.
127
+ *
128
+ * | Field | Type | Notes |
129
+ * | --- | --- | --- |
130
+ * | `title` | string | |
131
+ * | `credentials` | object | Replaced whole, and tested before it is stored. |
132
+ *
133
+ * A partial credential is not accepted: send all four fields, or the whole
134
+ * URI. New credentials go through the same checks as `create`, and the old
135
+ * connection is dropped -- so the next record call reads from the new cluster
136
+ * rather than from a connection that happens to still be open.
137
+ *
138
+ * Scope: `datastores:write`.
139
+ */
140
+ update(store: string, input: UpdateDataStoreInput): Promise<DataStore>;
141
+ /**
142
+ * `DELETE /v1/datastores/:store` -- 204.
143
+ *
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.
148
+ *
149
+ * Scope: `datastores:write`.
150
+ */
151
+ remove(store: string): Promise<void>;
152
+ /**
153
+ * `POST /v1/datastores/:store/test` -- connect, and report.
154
+ *
155
+ * ```ts
156
+ * const { ok, database, collections, latencyMs } = await crm.dataStores.test(store.id);
157
+ * ```
158
+ *
159
+ * The answer to "is it us or them". A credential that worked last month may
160
+ * have been rotated, an Atlas project may be paused, an IP access list may
161
+ * have been tightened -- each of which this distinguishes by status code
162
+ * rather than leaving to be inferred from a failing record call.
163
+ *
164
+ * `collections` is also what you need in order to point a new schema at a
165
+ * collection that already exists. It is empty when the database user has no
166
+ * `listCollections` privilege, which is a perfectly good credential -- not a
167
+ * sign the database is empty.
168
+ *
169
+ * A POST rather than a GET because it opens a connection and updates
170
+ * `verifiedAt`. Scope: `datastores:read`.
171
+ */
172
+ test(store: string): Promise<DataStoreTestResult>;
173
+ }
174
+ /**
175
+ * The collections you declare over a data store: a name, and the fields in it.
176
+ *
177
+ * ```ts
178
+ * const customers = await crm.dataStoreCollections.create(store.id, {
179
+ * title: 'Customers',
180
+ * fields: [
181
+ * { name: 'Email', type: 'String' }, // stored as `email`
182
+ * { name: 'Signed Up', type: 'Date' }, // stored as `signed_up`
183
+ * { name: 'Credits', type: 'Number', default: 0 },
184
+ * { name: 'Tags', type: 'Array' },
185
+ * ],
186
+ * });
187
+ *
188
+ * customers.fields.map((field) => field.name); // ['email','signed_up','credits','tags']
189
+ * ```
190
+ *
191
+ * ## Field names are slugged, once
192
+ *
193
+ * The server normalises every name -- punctuation dropped, spaces and hyphens to
194
+ * underscores, lowercased -- and the normalised name is both what comes back and
195
+ * what the document actually stores. **Use the returned names as your record
196
+ * keys.** The legacy panel slugged the name for the database but kept the
197
+ * original in the schema, so a field called `First Name` stored under
198
+ * `first_name` while the schema said otherwise, and records written against the
199
+ * schema's spelling silently went nowhere.
200
+ *
201
+ * ## Declaring a collection does not create one
202
+ *
203
+ * Mongo creates a collection on its first write, so a new schema addresses a
204
+ * collection that does not exist yet. Reads against it correctly return an empty
205
+ * page rather than failing.
206
+ *
207
+ * ## Editing fields rewrites nothing
208
+ *
209
+ * `update()` changes validation and coercion from the next call onward. It does
210
+ * not migrate documents: dropping a field hides the values under that key
211
+ * (re-declare it and they are back), and changing a field's type leaves old
212
+ * documents holding the old type. The collection a schema points at cannot be
213
+ * changed at all -- every record ever written through it lives there.
214
+ */
215
+ export declare class DataStoreCollections {
216
+ private readonly http;
217
+ constructor(http: HttpClient);
218
+ /**
219
+ * `POST /v1/datastores/:store/collections` -- 201.
220
+ *
221
+ * | Field | Type | Required | Notes |
222
+ * | --- | --- | --- | --- |
223
+ * | `title` | string | yes | 1-120 characters. |
224
+ * | `fields` | array | yes | 1-100 fields; see below. |
225
+ * | `collection` | string | no | An existing collection to adopt. Immutable afterwards. |
226
+ * | `show_in_chat` | boolean | no | Default `false`. Whether the chat panel offers it. |
227
+ * | `display.theme` | string | no | Presentation only. |
228
+ * | `display.fields` | string[] | no | Field names to show, in order. Presentation only. |
229
+ *
230
+ * **Each field**
231
+ *
232
+ * | Field | Type | Notes |
233
+ * | --- | --- | --- |
234
+ * | `name` | string | Slugged by the server; the slug is the document key. |
235
+ * | `type` | `String` `Number` `Boolean` `Date` `Array` | |
236
+ * | `label` | string | Display only. |
237
+ * | `default` | string \| number \| boolean | `String`, `Number` and `Boolean` only. |
238
+ *
239
+ * Omit `collection` and the server generates `<id>_<slugged title>`, which
240
+ * cannot collide with anything already in your database. Supply it to point at
241
+ * a collection you already have -- the way to use a database with data in it
242
+ * rather than duplicating it.
243
+ *
244
+ * | Status | Code | Meaning |
245
+ * | --- | --- | --- |
246
+ * | 404 | `not_found` | No such store in this account. |
247
+ * | 409 | `conflict` | This store already has a schema for that collection name. |
248
+ * | 422 | `validation_failed` | A field name that slugs to nothing, a duplicate after slugging, or a default of the wrong type. |
249
+ *
250
+ * Scope: `datastores:write`.
251
+ */
252
+ create(store: string, input: CreateDataStoreCollectionInput): Promise<DataStoreCollection>;
253
+ /**
254
+ * `GET /v1/datastores/:store/collections` -- offset paginated,
255
+ * newest first. `page`, `rows`, and `search` on the title.
256
+ *
257
+ * Scope: `datastores:read`.
258
+ */
259
+ list(store: string, query?: Partial<ListQuery>): Promise<Paginated<DataStoreCollection>>;
260
+ /**
261
+ * `GET /v1/datastores/:store/collections/:collection`.
262
+ *
263
+ * Both ids are checked: a collection id belonging to a different store -- even
264
+ * one of your own -- is a 404 rather than a match.
265
+ *
266
+ * Scope: `datastores:read`.
267
+ */
268
+ get(store: string, collection: string): Promise<DataStoreCollection>;
269
+ /**
270
+ * `PATCH /v1/datastores/:store/collections/:collection`.
271
+ *
272
+ * `title`, `fields`, `show_in_chat` and `display`. Not `collection`: a schema
273
+ * points at one collection for its whole life, and re-pointing it would orphan
274
+ * every record written through it. Point a new schema at the other collection
275
+ * instead.
276
+ *
277
+ * Scope: `datastores:write`.
278
+ */
279
+ update(store: string, collection: string, input: UpdateDataStoreCollectionInput): Promise<DataStoreCollection>;
280
+ /**
281
+ * `DELETE /v1/datastores/:store/collections/:collection` -- 204.
282
+ *
283
+ * Forgets the declaration. **Your collection and its documents remain** --
284
+ * declaring the same collection name again reads the same records back.
285
+ *
286
+ * Scope: `datastores:write`.
287
+ */
288
+ remove(store: string, collection: string): Promise<void>;
289
+ }
290
+ /**
291
+ * The records in a data store collection -- documents in **your** database.
292
+ *
293
+ * ```ts
294
+ * await crm.dataStoreRecords.insert(store.id, orders.id, {
295
+ * records: [{ order_no: 'A-1', total: 10 }, { order_no: 'A-2', total: 20 }],
296
+ * });
297
+ *
298
+ * const { data, pagination } = await crm.dataStoreRecords.search(store.id, orders.id, {
299
+ * filter: { paid: false, total: { $gte: 20 } },
300
+ * sort: 'total',
301
+ * order: 'desc',
302
+ * });
303
+ * ```
304
+ *
305
+ * ## Writes are validated against the declaration
306
+ *
307
+ * Keys must be declared field names, and values are coerced to the declared
308
+ * type -- `"42"` into a `Number` field is stored as `42`, an ISO string into a
309
+ * `Date` field as a date. An **undeclared key is a 422 naming it**, which is the
310
+ * one deliberate break from the legacy endpoint: there, an unknown key was
311
+ * dropped silently, so a typo meant data quietly went nowhere.
312
+ *
313
+ * `null` clears a field. `created_on` and `updated_on` are stamped by the server
314
+ * -- unix seconds, or real dates if the collection declared them as `Date` --
315
+ * and ignored if you send them. `id` cannot be written.
316
+ *
317
+ * ## Reads return everything that is stored
318
+ *
319
+ * Including keys the schema does not declare, because the documents are yours
320
+ * and may have been written by something else. `_id` reads back as `id`.
321
+ *
322
+ * ## There is no aggregation endpoint
323
+ *
324
+ * Deliberately: a pipeline can read other collections with `$lookup` and write
325
+ * them with `$merge`, which would make a schema's boundary meaningless, and
326
+ * `$function` runs JavaScript on your cluster. The same three operators are
327
+ * refused inside a filter. For anything that needs a pipeline, use your own
328
+ * credentials and your own driver -- it is your database.
329
+ */
330
+ export declare class DataStoreRecords {
331
+ private readonly http;
332
+ constructor(http: HttpClient);
333
+ /**
334
+ * `POST /v1/datastores/:store/collections/:collection/records` -- 201.
335
+ *
336
+ * | Field | Type | Notes |
337
+ * | --- | --- | --- |
338
+ * | `record` | object | One record. Exactly one of this or `records`. |
339
+ * | `records` | object[] | 1-1000 records. |
340
+ *
341
+ * ```ts
342
+ * const { inserted, ids } = await crm.dataStoreRecords.insert(store, collection, {
343
+ * records: rows,
344
+ * });
345
+ * ```
346
+ *
347
+ * A batch is **unordered**: one document rejected by a unique index of yours
348
+ * does not abandon the rest, so `inserted` may be lower than what you sent.
349
+ * `ids` are in the order the documents were sent.
350
+ *
351
+ * Prefer one call with `records` over a loop of single inserts -- each call is
352
+ * a round trip to your cluster.
353
+ *
354
+ * Scope: `datastore-records:write`.
355
+ */
356
+ insert(store: string, collection: string, input: InsertDataStoreRecordsInput): Promise<DataStoreWriteResult>;
357
+ /**
358
+ * `GET /v1/datastores/:store/collections/:collection/records`
359
+ *
360
+ * | Query | Type | Default | Notes |
361
+ * | --- | --- | --- | --- |
362
+ * | `page` | number | `1` | 1-based. |
363
+ * | `rows` | number | `25` | 1-100. |
364
+ * | `sort` | string | `created_on` | Any field name. |
365
+ * | `order` | `asc` \| `desc` | `desc` | |
366
+ *
367
+ * `_id` is appended to the sort as a tiebreaker, so paging is reproducible
368
+ * even when the sort field repeats -- or does not exist, which it will not on
369
+ * an adopted collection that something else wrote.
370
+ *
371
+ * Scope: `datastore-records:read`.
372
+ */
373
+ list(store: string, collection: string, query?: ListDataStoreRecordsQuery): Promise<Paginated<DataStoreRecord>>;
374
+ /**
375
+ * `POST /v1/datastores/:store/collections/:collection/records/search`
376
+ *
377
+ * | Field | Type | Notes |
378
+ * | --- | --- | --- |
379
+ * | `filter` | object | A Mongo query. Required; `{}` matches everything. |
380
+ * | `page` `rows` `sort` `order` | | As `list`. |
381
+ *
382
+ * ```ts
383
+ * await crm.dataStoreRecords.search(store, collection, {
384
+ * filter: {
385
+ * $or: [{ status: 'open' }, { total: { $gte: 100 } }],
386
+ * signed_up: { $gte: '2026-01-01' }, // coerced to a Date for a Date field
387
+ * tags: 'vip', // an Array field contains this
388
+ * },
389
+ * });
390
+ * ```
391
+ *
392
+ * A POST because a Mongo filter is a nested object and encoding one into a
393
+ * query string is a different argument in every HTTP client. It reads and
394
+ * changes nothing.
395
+ *
396
+ * Operand values are coerced per declared field, so dates and numbers written
397
+ * as JSON strings match. `$where`, `$function` and `$accumulator` are refused
398
+ * with `422 filter_operator_not_allowed`, and a filter nested deeper than ten
399
+ * levels or holding more than 500 keys with `422 filter_too_complex`.
400
+ *
401
+ * Scope: `datastore-records:read`.
402
+ */
403
+ search(store: string, collection: string, input: SearchDataStoreRecordsInput): Promise<Paginated<DataStoreRecord>>;
404
+ /**
405
+ * `GET /v1/datastores/:store/collections/:collection/records/:record`
406
+ *
407
+ * 404 when there is no such document. Scope: `datastore-records:read`.
408
+ */
409
+ get(store: string, collection: string, record: string): Promise<DataStoreRecord>;
410
+ /**
411
+ * `PATCH /v1/datastores/:store/collections/:collection/records/:record`
412
+ *
413
+ * | Field | Type | Notes |
414
+ * | --- | --- | --- |
415
+ * | `record` | object | The fields to change. Validated exactly as an insert. |
416
+ *
417
+ * A `$set` of those fields and nothing else -- keys you leave out are
418
+ * untouched, and `null` clears a value. `updated_on` is stamped; `created_on`
419
+ * cannot be moved. Returns the record as it is after the write.
420
+ *
421
+ * Scope: `datastore-records:write`.
422
+ */
423
+ update(store: string, collection: string, record: string, input: UpdateDataStoreRecordInput): Promise<DataStoreRecord>;
424
+ /**
425
+ * `POST /v1/datastores/:store/collections/:collection/records/bulk-update`
426
+ *
427
+ * | Field | Type | Notes |
428
+ * | --- | --- | --- |
429
+ * | `filter` | object | Required and **non-empty**. |
430
+ * | `update` | object | The fields to set on every match. |
431
+ *
432
+ * ```ts
433
+ * const { matched, modified } = await crm.dataStoreRecords.bulkUpdate(store, collection, {
434
+ * filter: { status: 'pending', created_on: { $lt: cutoff } },
435
+ * update: { status: 'expired' },
436
+ * });
437
+ * ```
438
+ *
439
+ * `{}` as the filter is refused: it matches the whole collection, and a
440
+ * one-character slip that rewrites every record is not something to make
441
+ * convenient. `modified` is lower than `matched` where a record already held
442
+ * the values.
443
+ *
444
+ * Scope: `datastore-records:write`.
445
+ */
446
+ bulkUpdate(store: string, collection: string, input: BulkUpdateDataStoreRecordsInput): Promise<DataStoreUpdateResult>;
447
+ /**
448
+ * `DELETE /v1/datastores/:store/collections/:collection/records/:record`
449
+ *
450
+ * 404 when there is no such document. This one really does delete your data.
451
+ *
452
+ * Scope: `datastore-records:write`.
453
+ */
454
+ remove(store: string, collection: string, record: string): Promise<DataStoreDeleteResult>;
455
+ /**
456
+ * `POST /v1/datastores/:store/collections/:collection/records/bulk-delete`
457
+ *
458
+ * | Field | Type | Notes |
459
+ * | --- | --- | --- |
460
+ * | `filter` | object | Required and **non-empty**. |
461
+ *
462
+ * Deletes every match, irreversibly, from your database. The empty filter is
463
+ * refused for the same reason as in `bulkUpdate` -- emptying a collection
464
+ * deliberately means paging through it.
465
+ *
466
+ * Scope: `datastore-records:write`.
467
+ */
468
+ bulkRemove(store: string, collection: string, input: BulkDeleteDataStoreRecordsInput): Promise<DataStoreDeleteResult>;
469
+ }