ab-ecosystem-cpass 1.0.6 → 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 (44) hide show
  1. package/README.md +5 -2
  2. package/dist/client.d.ts +31 -6
  3. package/dist/client.js +36 -8
  4. package/dist/client.js.map +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.js +9 -5
  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 +153 -0
  15. package/dist/resources/campaign-messages.js +179 -0
  16. package/dist/resources/campaign-messages.js.map +1 -0
  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 +137 -41
  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 +282 -0
  36. package/docs/campaign/campaigns.md +8 -48
  37. package/docs/data-store.md +495 -0
  38. package/package.json +1 -1
  39. package/dist/resources/campaign-report-exports.d.ts +0 -14
  40. package/dist/resources/campaign-report-exports.js +0 -28
  41. package/dist/resources/campaign-report-exports.js.map +0 -1
  42. package/dist/resources/campaign-reports.d.ts +0 -15
  43. package/dist/resources/campaign-reports.js +0 -25
  44. package/dist/resources/campaign-reports.js.map +0 -1
@@ -0,0 +1,153 @@
1
+ import type { CampaignMessageRecord, CampaignMessageStats, CampaignMessageStatsQuery, CampaignRepliesQuery, CampaignReplyBreakdown, ListCampaignMessagesQuery } from '../types/campaign';
2
+ import type { Paginated } from '../types/common';
3
+ import type { HttpClient } from '../http';
4
+ /**
5
+ * Campaign reporting: what actually happened to each message.
6
+ *
7
+ * ```ts
8
+ * const stats = await crm.campaignMessages.stats(campaign);
9
+ * // { total: 12480, sent: 12410, delivered: 11980, read: 7420, replied: 310, failed: 70 }
10
+ *
11
+ * const { data } = await crm.campaignMessages.list(campaign, { status: 'failed' });
12
+ * data[0].error; // why that one did not go
13
+ * ```
14
+ *
15
+ * Every method takes the campaign id first. A report has no meaning without one,
16
+ * so it is addressed as the campaign's own sub-resource --
17
+ * `/v1/campaigns/:id/report` -- rather than as a collection of rows that a
18
+ * campaign filters. The options object holds only what *narrows* a report that
19
+ * already exists: a message index, a status, a page.
20
+ *
21
+ * ## Where these numbers come from
22
+ *
23
+ * One row per attempted send, written by the campaign manager as it hands each
24
+ * message to a channel, with its `status` moved forward by the channel's
25
+ * delivery webhooks -- which are processed outside the CRM entirely. The report
26
+ * is **counted off those rows when you ask for it**, not rolled up on a
27
+ * schedule, so there is no "generated at" to reason about and no staleness
28
+ * window: the numbers are as current as the webhooks that have arrived.
29
+ *
30
+ * The flip side is that a campaign still going out has moving numbers. Reading
31
+ * `stats` seconds after launching under-counts everything, and that is correct
32
+ * rather than a lag.
33
+ *
34
+ * ## The funnel
35
+ *
36
+ * `status` is a single field holding the furthest stage reached, so the counts
37
+ * are cumulative and nest: `replied ⊆ read ⊆ delivered ⊆ sent`. For a stage's
38
+ * exclusive count, subtract the next one -- `delivered - read` is "delivered but
39
+ * unread". `failed` is outside the funnel and never inside `sent`.
40
+ *
41
+ * ## There is no export endpoint
42
+ *
43
+ * An export is {@link list} written to a file, so {@link iterate} does the
44
+ * paging and the caller formats the rows. Nothing is queued, nothing has to be
45
+ * polled, and no generated file expires before someone downloads it.
46
+ */
47
+ export declare class CampaignMessages {
48
+ private readonly http;
49
+ constructor(http: HttpClient);
50
+ /**
51
+ * The campaign's funnel counts.
52
+ *
53
+ * `GET /v1/campaigns/:id/report` · `campaigns:read` · **200**
54
+ *
55
+ * ### Query
56
+ *
57
+ * | Field | Type | Notes |
58
+ * | --- | --- | --- |
59
+ * | `message` | number | One message of a multi-message campaign, zero-based. Omit for the whole campaign. |
60
+ *
61
+ * ```ts
62
+ * const overall = await crm.campaignMessages.stats(campaign);
63
+ * const second = await crm.campaignMessages.stats(campaign, { message: 1 });
64
+ * ```
65
+ *
66
+ * No date window: a campaign is one send, so its whole life is the only
67
+ * meaningful window. A repeating campaign's runs are separate campaigns with
68
+ * their own ids.
69
+ *
70
+ * `404 not_found` if the campaign is not the caller's -- the same answer as a
71
+ * campaign that does not exist, so the error cannot be used to probe ids.
72
+ */
73
+ stats(campaign: string, query?: CampaignMessageStatsQuery): Promise<CampaignMessageStats>;
74
+ /**
75
+ * The rows behind the funnel. This is the report table.
76
+ *
77
+ * `GET /v1/campaigns/:id/report/messages` · `campaigns:read` · **200**
78
+ *
79
+ * ### Query
80
+ *
81
+ * | Field | Type | Default | Notes |
82
+ * | --- | --- | --- | --- |
83
+ * | `message` | number | — | Zero-based index of one campaign message. |
84
+ * | `status` | `sent \| delivered \| read \| replied \| failed` | — | Cumulative; see below. Omit for every row. |
85
+ * | `exact` | boolean | `false` | Only rows sitting at `status` right now. |
86
+ * | `identifier` | string | — | Exact match on one recipient. Not a search. |
87
+ * | `reply_type` | string | — | Replied rows only; refused otherwise. |
88
+ * | `replied_within_hours` | number | — | Replied rows only; refused otherwise. |
89
+ * | `page` | number | `1` | 1-based. |
90
+ * | `rows` | number | `25` | 1–100. |
91
+ *
92
+ * Newest attempt first, ordered by when the row was written -- not by a stage
93
+ * timestamp, which would reorder the list as webhooks arrive and make paging
94
+ * skip and repeat rows.
95
+ *
96
+ * ```ts
97
+ * // the two queries a report screen actually makes
98
+ * const failures = await crm.campaignMessages.list(campaign, { status: 'failed', rows: 100 });
99
+ * const unread = await crm.campaignMessages.list(campaign, { status: 'delivered', exact: true });
100
+ * ```
101
+ *
102
+ * `reply_type` and `replied_within_hours` are **refused** with
103
+ * `422 validation_failed` unless `status` is `replied`, rather than quietly
104
+ * matching nothing.
105
+ */
106
+ list(campaign: string, query?: ListCampaignMessagesQuery): Promise<Paginated<CampaignMessageRecord>>;
107
+ /**
108
+ * Every matching row, paged through for you. What an export is.
109
+ *
110
+ * ```ts
111
+ * for await (const row of crm.campaignMessages.iterate(campaign)) {
112
+ * csv.write([row.identifier, row.name, row.status, row.error ?? '']);
113
+ * }
114
+ * ```
115
+ *
116
+ * Pages at 100 rows unless told otherwise -- the server's maximum, and the
117
+ * fewest round trips. A campaign of a million recipients is ten thousand
118
+ * requests, so for exports that size prefer running it where the latency is
119
+ * low rather than from a browser.
120
+ *
121
+ * The collection is live: rows are written while a campaign is going out, and
122
+ * statuses change under an export that takes minutes. Offset paging over
123
+ * moving data can repeat or skip a row -- for a stable snapshot, export after
124
+ * the campaign completes.
125
+ */
126
+ iterate(campaign: string, query?: ListCampaignMessagesQuery): AsyncGenerator<CampaignMessageRecord>;
127
+ /**
128
+ * What people replied, grouped by the reply itself.
129
+ *
130
+ * `GET /v1/campaigns/:id/report/replies` · `campaigns:read` · **200**
131
+ *
132
+ * ### Query
133
+ *
134
+ * | Field | Type | Default | Notes |
135
+ * | --- | --- | --- | --- |
136
+ * | `message` | number | — | Zero-based index of one campaign message. |
137
+ * | `limit` | number | `50` | Distinct replies to return, 1–200. |
138
+ *
139
+ * ```ts
140
+ * const { groups, replied, distinct } = await crm.campaignMessages.replies(campaign);
141
+ * groups; // [{ reply: 'Yes', replyType: 'button', count: 182 }, …]
142
+ * ```
143
+ *
144
+ * This is what a poll or a button message is sent to find out. Grouped on the
145
+ * reply **and** its type, so a tapped `Yes` and a typed `Yes` stay apart --
146
+ * they say different things about how engaged the recipient was.
147
+ *
148
+ * `distinct` against `groups.length` tells you whether the breakdown is
149
+ * complete. A campaign that asked an open question has as many groups as it
150
+ * has repliers, so a truncated answer there is normal rather than a problem.
151
+ */
152
+ replies(campaign: string, query?: CampaignRepliesQuery): Promise<CampaignReplyBreakdown>;
153
+ }
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CampaignMessages = void 0;
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`;
7
+ /**
8
+ * Campaign reporting: what actually happened to each message.
9
+ *
10
+ * ```ts
11
+ * const stats = await crm.campaignMessages.stats(campaign);
12
+ * // { total: 12480, sent: 12410, delivered: 11980, read: 7420, replied: 310, failed: 70 }
13
+ *
14
+ * const { data } = await crm.campaignMessages.list(campaign, { status: 'failed' });
15
+ * data[0].error; // why that one did not go
16
+ * ```
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
+ *
24
+ * ## Where these numbers come from
25
+ *
26
+ * One row per attempted send, written by the campaign manager as it hands each
27
+ * message to a channel, with its `status` moved forward by the channel's
28
+ * delivery webhooks -- which are processed outside the CRM entirely. The report
29
+ * is **counted off those rows when you ask for it**, not rolled up on a
30
+ * schedule, so there is no "generated at" to reason about and no staleness
31
+ * window: the numbers are as current as the webhooks that have arrived.
32
+ *
33
+ * The flip side is that a campaign still going out has moving numbers. Reading
34
+ * `stats` seconds after launching under-counts everything, and that is correct
35
+ * rather than a lag.
36
+ *
37
+ * ## The funnel
38
+ *
39
+ * `status` is a single field holding the furthest stage reached, so the counts
40
+ * are cumulative and nest: `replied ⊆ read ⊆ delivered ⊆ sent`. For a stage's
41
+ * exclusive count, subtract the next one -- `delivered - read` is "delivered but
42
+ * unread". `failed` is outside the funnel and never inside `sent`.
43
+ *
44
+ * ## There is no export endpoint
45
+ *
46
+ * An export is {@link list} written to a file, so {@link iterate} does the
47
+ * paging and the caller formats the rows. Nothing is queued, nothing has to be
48
+ * polled, and no generated file expires before someone downloads it.
49
+ */
50
+ class CampaignMessages {
51
+ http;
52
+ constructor(http) {
53
+ this.http = http;
54
+ }
55
+ /**
56
+ * The campaign's funnel counts.
57
+ *
58
+ * `GET /v1/campaigns/:id/report` · `campaigns:read` · **200**
59
+ *
60
+ * ### Query
61
+ *
62
+ * | Field | Type | Notes |
63
+ * | --- | --- | --- |
64
+ * | `message` | number | One message of a multi-message campaign, zero-based. Omit for the whole campaign. |
65
+ *
66
+ * ```ts
67
+ * const overall = await crm.campaignMessages.stats(campaign);
68
+ * const second = await crm.campaignMessages.stats(campaign, { message: 1 });
69
+ * ```
70
+ *
71
+ * No date window: a campaign is one send, so its whole life is the only
72
+ * meaningful window. A repeating campaign's runs are separate campaigns with
73
+ * their own ids.
74
+ *
75
+ * `404 not_found` if the campaign is not the caller's -- the same answer as a
76
+ * campaign that does not exist, so the error cannot be used to probe ids.
77
+ */
78
+ stats(campaign, query = {}) {
79
+ return this.http.request('GET', report(campaign), { query });
80
+ }
81
+ /**
82
+ * The rows behind the funnel. This is the report table.
83
+ *
84
+ * `GET /v1/campaigns/:id/report/messages` · `campaigns:read` · **200**
85
+ *
86
+ * ### Query
87
+ *
88
+ * | Field | Type | Default | Notes |
89
+ * | --- | --- | --- | --- |
90
+ * | `message` | number | — | Zero-based index of one campaign message. |
91
+ * | `status` | `sent \| delivered \| read \| replied \| failed` | — | Cumulative; see below. Omit for every row. |
92
+ * | `exact` | boolean | `false` | Only rows sitting at `status` right now. |
93
+ * | `identifier` | string | — | Exact match on one recipient. Not a search. |
94
+ * | `reply_type` | string | — | Replied rows only; refused otherwise. |
95
+ * | `replied_within_hours` | number | — | Replied rows only; refused otherwise. |
96
+ * | `page` | number | `1` | 1-based. |
97
+ * | `rows` | number | `25` | 1–100. |
98
+ *
99
+ * Newest attempt first, ordered by when the row was written -- not by a stage
100
+ * timestamp, which would reorder the list as webhooks arrive and make paging
101
+ * skip and repeat rows.
102
+ *
103
+ * ```ts
104
+ * // the two queries a report screen actually makes
105
+ * const failures = await crm.campaignMessages.list(campaign, { status: 'failed', rows: 100 });
106
+ * const unread = await crm.campaignMessages.list(campaign, { status: 'delivered', exact: true });
107
+ * ```
108
+ *
109
+ * `reply_type` and `replied_within_hours` are **refused** with
110
+ * `422 validation_failed` unless `status` is `replied`, rather than quietly
111
+ * matching nothing.
112
+ */
113
+ list(campaign, query = {}) {
114
+ return this.http.request('GET', `${report(campaign)}/messages`, { query });
115
+ }
116
+ /**
117
+ * Every matching row, paged through for you. What an export is.
118
+ *
119
+ * ```ts
120
+ * for await (const row of crm.campaignMessages.iterate(campaign)) {
121
+ * csv.write([row.identifier, row.name, row.status, row.error ?? '']);
122
+ * }
123
+ * ```
124
+ *
125
+ * Pages at 100 rows unless told otherwise -- the server's maximum, and the
126
+ * fewest round trips. A campaign of a million recipients is ten thousand
127
+ * requests, so for exports that size prefer running it where the latency is
128
+ * low rather than from a browser.
129
+ *
130
+ * The collection is live: rows are written while a campaign is going out, and
131
+ * statuses change under an export that takes minutes. Offset paging over
132
+ * moving data can repeat or skip a row -- for a stable snapshot, export after
133
+ * the campaign completes.
134
+ */
135
+ async *iterate(campaign, query = {}) {
136
+ const rows = query.rows ?? 100;
137
+ let page = query.page ?? 1;
138
+ for (;;) {
139
+ const result = await this.list(campaign, { ...query, page, rows });
140
+ for (const record of result.data)
141
+ yield record;
142
+ if (result.data.length === 0 || page >= result.pagination.pages)
143
+ return;
144
+ page += 1;
145
+ }
146
+ }
147
+ /**
148
+ * What people replied, grouped by the reply itself.
149
+ *
150
+ * `GET /v1/campaigns/:id/report/replies` · `campaigns:read` · **200**
151
+ *
152
+ * ### Query
153
+ *
154
+ * | Field | Type | Default | Notes |
155
+ * | --- | --- | --- | --- |
156
+ * | `message` | number | — | Zero-based index of one campaign message. |
157
+ * | `limit` | number | `50` | Distinct replies to return, 1–200. |
158
+ *
159
+ * ```ts
160
+ * const { groups, replied, distinct } = await crm.campaignMessages.replies(campaign);
161
+ * groups; // [{ reply: 'Yes', replyType: 'button', count: 182 }, …]
162
+ * ```
163
+ *
164
+ * This is what a poll or a button message is sent to find out. Grouped on the
165
+ * reply **and** its type, so a tapped `Yes` and a typed `Yes` stay apart --
166
+ * they say different things about how engaged the recipient was.
167
+ *
168
+ * `distinct` against `groups.length` tells you whether the breakdown is
169
+ * complete. A campaign that asked an open question has as many groups as it
170
+ * has repliers, so a truncated answer there is normal rather than a problem.
171
+ */
172
+ replies(campaign, query = {}) {
173
+ return this.http.request('GET', `${report(campaign)}/replies`, {
174
+ query,
175
+ });
176
+ }
177
+ }
178
+ exports.CampaignMessages = CampaignMessages;
179
+ //# sourceMappingURL=campaign-messages.js.map
@@ -0,0 +1 @@
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"}