@signalhousellc/sdk 1.0.67 → 1.0.68

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.
@@ -1,3 +1,9 @@
1
+ /** @typedef {import('../SignalHouseSDK.js').RequestOptions} RequestOptions */
2
+ /**
3
+ * @template T
4
+ * @typedef {import('../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
5
+ */
6
+
1
7
  export class Messages {
2
8
  constructor(client, multipartClient, enableAdmin) {
3
9
  this.client = client;
@@ -9,7 +15,7 @@ export class Messages {
9
15
  * Estimate Canadian SMS/MMS retail cost without sending or charging.
10
16
  * @async
11
17
  * @param {Object} params - Sender, up to 100 recipients, messageBody and request options.
12
- * @returns {Promise<Object>} - Per-recipient rates and total in microdollars; unknown carriers use the upper fallback.
18
+ * @returns {Promise<SdkResponse<any>>} - Per-recipient rates and total in microdollars; unknown carriers use the upper fallback.
13
19
  */
14
20
  async estimateMessage({ senderPhoneNumber, recipientPhoneNumbers, messageBody, messageType = "SMS", options = {} }) {
15
21
  return this.client("/message/estimate", { method: "POST", body: { senderPhoneNumber, recipientPhoneNumbers, messageBody, messageType }, ...options });
@@ -32,16 +38,18 @@ export class Messages {
32
38
  * @param {boolean} [params.matchAny] - When true, the provided campaignId/brandId/phoneNumber filters are OR'd (a message matches ANY of them) instead of the default most-specific-single behavior. subgroupId stays an AND scope. Opt-in/additive.
33
39
  * @param {string} [params.carrier] - Filter messages by the carrier used for sending
34
40
  * @param {string|string[]} [params.sentimentLabel] - Filter messages by sentiment bucket. Accepts a single value or an array (e.g. ["positive", "negative"]). Allowed values: positive, neutral, negative. Applied as the sentimentScore range the ±25 label threshold produces, so unscored messages match no bucket and outbound messages (never scored) are excluded.
41
+ * @param {string|string[]} [params.phoneNumber] - Filter messages by a business number on either side of the message. Accepts a single value or an array; OR'd with campaignId/brandId when matchAny is true
35
42
  * @param {string} [params.senderPhoneNumber] - Filter messages by the sender's 5-6 digit Short Code or 10+ digit phone number
36
43
  * @param {string} [params.recipientPhoneNumber] - Filter messages by the recipient's phone number
37
- * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
38
- * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
44
+ * @param {string} [params.startDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode
45
+ * @param {string} [params.endDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back
39
46
  * @param {string} [params.sortField] - The field to sort the messages by (createdAt, segmentCount, status, sentimentScore)
40
47
  * @param {string} [params.sortOrder] - The order to sort the messages (asc, desc)
41
48
  * @param {number} [params.page] - The page number for pagination
42
49
  * @param {number} [params.limit] - The number of messages per page
43
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
44
- * @returns {Promise<Object>} - A promise that resolves to the list of messages. Inbound messages also carry
50
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
51
+ * @param {RequestOptions} [params.options] - Additional options for the request
52
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the list of messages. Inbound messages also carry
45
53
  * sentimentScore (-100..100, null when unscored), sentimentLabel ("positive"|"neutral"|"negative", null when
46
54
  * sentimentScore is null) and sentimentScoredAt. Outbound messages are never scored.
47
55
  */
@@ -67,9 +75,10 @@ export class Messages {
67
75
  sortOrder,
68
76
  page,
69
77
  limit,
78
+ dateBounds,
70
79
  options = {},
71
80
  }) {
72
- const filters = { id, campaignId, brandId, subgroupId, groupId, phoneNumber, senderPhoneNumber, recipientPhoneNumber, status, direction, messageType, channel, matchAny, carrier, sentimentLabel, startDate, endDate, sortField, sortOrder, page, limit };
81
+ const filters = { id, campaignId, brandId, subgroupId, groupId, phoneNumber, senderPhoneNumber, recipientPhoneNumber, status, direction, messageType, channel, matchAny, carrier, sentimentLabel, startDate, endDate, sortField, sortOrder, page, limit, dateBounds };
73
82
  const queryString = this.client._getQueryString(filters);
74
83
  return this.client(`/message${queryString}`, { method: "GET", ...options });
75
84
  }
@@ -82,8 +91,8 @@ export class Messages {
82
91
  * @roles api, admin, developer, billing, user
83
92
  * @param {Object} params - The parameters for fetching delivery reports
84
93
  * @param {string[]} params.messageIds - The message IDs to fetch carrier delivery reports for (1-100)
85
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
86
- * @returns {Promise<Object>} - A promise that resolves to an array of carrier delivery reports
94
+ * @param {RequestOptions} [params.options] - Additional options for the request
95
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to an array of carrier delivery reports
87
96
  */
88
97
  async getDeliveryReports({ messageIds, options = {} }) {
89
98
  this.client._require({ messageIds });
@@ -106,13 +115,14 @@ export class Messages {
106
115
  * @param {string|string[]} [params.phoneNumber] - Filter analytics by the phone number involved in the messages. Accepts a single value, an array, or a comma-separated string.
107
116
  * @param {string|string[]} [params.carrier] - Filter analytics by the carrier used for sending. Accepts a single value, an array, or a comma-separated string.
108
117
  * @param {string|string[]} [params.carrierFamily] - Filter analytics by the recipient's resolved carrier family (Default, ATT, TMobile, Verizon, USCellular, GoogleVoice, ClearSky, Interop, RogueMobile, P2P). Accepts a single value, an array, or a comma-separated string. Only accepted where the environment serves hourly analytics; elsewhere any value is rejected with a 400 -- omit the parameter unless it is wanted.
109
- * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
110
- * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
118
+ * @param {string} [params.startDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode
119
+ * @param {string} [params.endDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back
111
120
  * @param {boolean} [params.matchAny] - When true, the provided campaignId/brandId/phoneNumber filters are OR'd (match ANY) instead of the default most-specific-single behavior. subgroupId stays an AND scope. Opt-in/additive.
112
121
  * @param {(string|string[])} [params.messageType] - Message type(s) the SENTIMENT figures are scoped to: SMS, MMS or P2P (single value, array, or comma-separated string). Omit for all scored types. Does NOT narrow the message counts, which are always returned split per type (smsInbound/mmsInbound/p2pInbound) and have no filterable type dimension.
113
122
  * @param {string|string[]} [params.channel] - Filter analytics by channel (tenDLC, virtualLongCode, tollFree, shortCode, p2p). Filters on the stored channel column; a tenDLC selection also includes older messages that have no channel, and p2p is a real channel here (matched by carrier). Accepts a single value or an array. Ignored when matchAny is true (matchAny takes precedence).
114
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
115
- * @returns {Promise<Object>} - A promise that resolves to the analytics data. Also includes inbound-message
123
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
124
+ * @param {RequestOptions} [params.options] - Additional options for the request
125
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the analytics data. Also includes inbound-message
116
126
  * sentiment for the requested scope: sentimentScore (volume-weighted average of every scored inbound message
117
127
  * in range, -100..100, null when nothing was scored), sentimentLabel ("positive"|"neutral"|"negative", null
118
128
  * when sentimentScore is null), sentimentScoredCount, sentimentPositive, sentimentNeutral, sentimentNegative.
@@ -121,8 +131,8 @@ export class Messages {
121
131
  * summed or re-averaged across types, rows or responses. To weight a subset of message types, pass
122
132
  * messageType and read sentimentScore.
123
133
  */
124
- async getAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, matchAny, options = {} }) {
125
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, matchAny };
134
+ async getAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, matchAny, dateBounds, options = {} }) {
135
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, matchAny, dateBounds };
126
136
  const queryString = this.client._getQueryString(filters);
127
137
  return this.client(`/message/analytics${queryString}`, { method: "GET", ...options });
128
138
  }
@@ -139,14 +149,15 @@ export class Messages {
139
149
  * @param {string|string[]} [params.phoneNumber] - Filter analytics by the phone number involved in the messages. Accepts a single value, an array, or a comma-separated string.
140
150
  * @param {string|string[]} [params.carrier] - Filter analytics by the carrier used for sending. Accepts a single value, an array, or a comma-separated string.
141
151
  * @param {string|string[]} [params.carrierFamily] - Filter analytics by the recipient's resolved carrier family (Default, ATT, TMobile, Verizon, USCellular, GoogleVoice, ClearSky, Interop, RogueMobile, P2P). Accepts a single value, an array, or a comma-separated string. Only accepted where the environment serves hourly analytics; elsewhere any value is rejected with a 400 -- omit the parameter unless it is wanted.
142
- * @param {string} [params.startDate] - ISO-8601 date or timestamp. At day granularity it is normalized to start-of-UTC-day (inclusive); at hour granularity it is floored to its hour.
143
- * @param {string} [params.endDate] - ISO-8601 date or timestamp. At day granularity it is normalized to end-of-UTC-day (inclusive); at hour granularity it is honoured as given.
152
+ * @param {string} [params.startDate] - ISO-8601 date or timestamp. It is bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode, then floored to the start of its hour at either granularity, because the hourly cube is keyed by hour and a mid-hour start would drop that hour's bucket.
153
+ * @param {string} [params.endDate] - ISO-8601 date or timestamp. At day granularity it is bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back; at hour granularity it is honoured as given.
144
154
  * @param {(string|string[])} [params.messageType] - Message type(s) the SENTIMENT figures are scoped to: SMS, MMS or P2P (single value, array, or comma-separated string). Omit for all scored types. Does NOT narrow the message counts, which are always returned split per type (smsInbound/mmsInbound/p2pInbound) and have no filterable type dimension.
145
155
  * @param {string|string[]} [params.channel] - Filter analytics by channel (tenDLC, virtualLongCode, tollFree, shortCode, p2p). Filters on the stored channel column; a tenDLC selection also includes older messages that have no channel, and p2p is a real channel here. Accepts a single value or an array.
146
156
  * @param {("day"|"hour")} [params.granularity] - Time-bucket grain of the byDate rows. Omit for day (each row's _id is a "YYYY-MM-DD" date). "hour" buckets by hour (each row's _id is a "YYYY-MM-DD HH:MM:SS" UTC hour) and is capped to a 7-day span. Only accepted where the environment serves hourly analytics; elsewhere any value, including "day", is rejected with a 400 -- omit the parameter unless hour grain is wanted.
147
157
  * @param {("carrier"|"carrierFamily")} [params.breakdown] - Adds a byBreakdown time × dimension series to the response ({ dimension, rows }: each row is one byDate bucket (_id) for one carrier or carrier family (key), over the top 50 keys by volume, 10DLC only) and caps the span at 31 days. Only accepted where the environment serves hourly analytics; elsewhere any value is rejected with a 400 -- omit the parameter unless it is wanted.
148
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
149
- * @returns {Promise<Array>} - A promise that resolves to an array of analytics snapshot records. cardTotals and
158
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
159
+ * @param {RequestOptions} [params.options] - Additional options for the request
160
+ * @returns {Promise<SdkResponse<Array>>} - A promise that resolves to an array of analytics snapshot records. cardTotals and
150
161
  * every byDate/byPhoneNumber/byCarrier row also carry that row's own inbound-message sentiment: sentimentScore
151
162
  * (volume-weighted average, -100..100, null when nothing in that row's scope was scored), sentimentLabel,
152
163
  * sentimentScoredCount, sentimentPositive, sentimentNeutral, sentimentNegative. The fields are always present;
@@ -154,12 +165,39 @@ export class Messages {
154
165
  * score is divided server-side and the raw sums are never returned: a score is an average and must never be
155
166
  * summed or re-averaged across types, rows or responses.
156
167
  */
157
- async getAnalyticsDetail({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, granularity, breakdown, options = {} }) {
158
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, granularity, breakdown };
168
+ async getAnalyticsDetail({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, granularity, breakdown, dateBounds, options = {} }) {
169
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, channel, messageType, granularity, breakdown, dateBounds };
159
170
  const queryString = this.client._getQueryString(filters);
160
171
  return this.client(`/message/analytics/detail${queryString}`, { method: "GET", ...options });
161
172
  }
162
173
 
174
+ /**
175
+ * Get per-minute send throughput (peak segments per minute, sending minutes, minutes at a ceiling) for a
176
+ * campaign, brand, or group, per time bucket and per carrier family. Measured from SENT transitions at
177
+ * campaign × carrier-family grain -- the grain carrier ceilings are enforced at -- so a peak here is the
178
+ * figure a ceiling is compared against. Only accepted where the environment serves hourly analytics;
179
+ * elsewhere the request is rejected with a 400.
180
+ * @async
181
+ * @roles api, admin, developer, billing, user
182
+ * @param {Object} params - The parameters for the throughput read
183
+ * @param {string} params.groupId - The group to scope the read to (required)
184
+ * @param {string|string[]} [params.brandId] - Brand scope. Accepts a single value, an array, or a comma-separated string.
185
+ * @param {string|string[]} [params.campaignId] - Campaign scope; wins over brandId. Accepts a single value, an array, or a comma-separated string.
186
+ * @param {string|string[]} [params.carrierFamily] - Carrier-family filter (ATT, TMobile, Verizon, USCellular, ...). Accepts a single value, an array, or a comma-separated string.
187
+ * @param {string} params.startDate - ISO-8601 date or timestamp. It is bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode, then floored to the start of its hour at hour granularity and of its minute at day granularity (the cube is keyed by minute).
188
+ * @param {string} params.endDate - ISO-8601 date or timestamp. At hour granularity it is honoured as given; at day granularity it is bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back. The span is capped at 35 days (the retention of the per-minute data).
189
+ * @param {("hour"|"day")} [params.granularity] - Bucket grain of the byDate and byBreakdown rows. Omit for hour (each row's _id is a "YYYY-MM-DD HH:MM:SS" UTC hour, span capped at 7 days); "day" buckets by calendar day on the end bound's offset.
190
+ * @param {number} [params.ceiling] - Segments-per-minute ceiling. When given, every row also reports minutesAtCeiling (sending minutes at or above it); otherwise minutesAtCeiling is null.
191
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
192
+ * @param {RequestOptions} [params.options] - Additional options for the request
193
+ * @returns {Promise<SdkResponse<any>>} - `{ totals, byDate, byCarrierFamily, byBreakdown: { dimension: "carrierFamily", rows } }`. Every row carries segments, messages, peakSegmentsPerMinute, peakMessagesPerMinute, peakMinute (UTC "YYYY-MM-DD HH:MM:SS", null when no traffic), sendingMinutes, averageSegmentsPerSendingMinute and minutesAtCeiling.
194
+ */
195
+ async getAnalyticsThroughput({ groupId, brandId, campaignId, carrierFamily, startDate, endDate, granularity, ceiling, dateBounds, options = {} }) {
196
+ const filters = { groupId, brandId, campaignId, carrierFamily, startDate, endDate, granularity, ceiling, dateBounds };
197
+ const queryString = this.client._getQueryString(filters);
198
+ return this.client(`/message/analytics/throughput${queryString}`, { method: "GET", ...options });
199
+ }
200
+
163
201
  /**
164
202
  * Get filter dropdown options (subgroups, brands, campaigns, phone numbers) for the Analytics page,
165
203
  * scoped to a single group. Sourced from ClickHouse — only items with at least one message are returned.
@@ -167,8 +205,8 @@ export class Messages {
167
205
  * @roles api, admin, developer, billing, user
168
206
  * @param {Object} params - The parameters for getting analytics filter options
169
207
  * @param {string} params.groupId - The ID of the group whose filter options to load
170
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
171
- * @returns {Promise<Object>} - A promise that resolves to { subgroups, brands, campaigns, phoneNumbers }
208
+ * @param {RequestOptions} [params.options] - Additional options for the request
209
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to { subgroups, brands, campaigns, phoneNumbers }
172
210
  */
173
211
  async getAnalyticsFilterOptions({ groupId, options = {} }) {
174
212
  const filters = { groupId };
@@ -195,12 +233,13 @@ export class Messages {
195
233
  * @param {number} [params.limit=50] - max 50 per page
196
234
  * @param {("tenDLC"|"virtualLongCode"|"tollFree"|"shortCode"|"p2p"|"both")|Array<"tenDLC"|"virtualLongCode"|"tollFree"|"shortCode"|"p2p">} [params.channel="both"] - Filter and rank rows by the stored channel column. "both" (default) means all activity. tenDLC/virtualLongCode/tollFree/shortCode/p2p (single value or array) limit to the selected channel(s).
197
235
  * @param {(string|string[])} [params.messageType] - Message type(s) the SENTIMENT figures are scoped to: SMS, MMS or P2P (single value, array, or comma-separated string). Omit for all scored types. Does NOT narrow the message counts, which are always returned split per type (smsInbound/mmsInbound/p2pInbound) and have no filterable type dimension.
198
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options]
199
- * @returns {Promise<Object>} - { rows, totalCount, page, limit }. Each row carries sms/mms/p2p metric columns plus `smsOptOuts` and `mmsOptOuts` from the DNC analytics MV (P2P has no opt-outs).
236
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
237
+ * @param {RequestOptions} [params.options]
238
+ * @returns {Promise<SdkResponse<any>>} - { rows, totalCount, page, limit }. Each row carries sms/mms/p2p metric columns plus `smsOptOuts` and `mmsOptOuts` from the DNC analytics MV (P2P has no opt-outs).
200
239
  * Each row also carries that subgroup's inbound-message sentiment: `sentimentScore` (volume-weighted average of every scored inbound message in that subgroup, -100..100, null when nothing was scored), `sentimentLabel`, `sentimentScoredCount`, `sentimentPositive`, `sentimentNeutral`, `sentimentNegative`, plus the same six per message type under `smsSentiment*`, `mmsSentiment*` and `p2pSentiment*` prefixes. The score is derived server-side and must never be re-averaged across rows — combine the counts, not the scores.
201
240
  */
202
- async getAnalyticsBySubgroup({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, messageType, options = {} }) {
203
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, messageType };
241
+ async getAnalyticsBySubgroup({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, messageType, dateBounds, options = {} }) {
242
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, messageType, dateBounds };
204
243
  const queryString = this.client._getQueryString(filters);
205
244
  return this.client(`/message/analytics/by-subgroup${queryString}`, { method: "GET", ...options });
206
245
  }
@@ -223,11 +262,12 @@ export class Messages {
223
262
  * @param {number} [params.page=1]
224
263
  * @param {number} [params.limit=50] - max 50 per page
225
264
  * @param {("tenDLC"|"virtualLongCode"|"tollFree"|"shortCode"|"p2p"|"both")|Array<"tenDLC"|"virtualLongCode"|"tollFree"|"shortCode"|"p2p">} [params.channel="both"] - Filter and rank rows by the stored channel column. "both" (default) means every code. tenDLC/virtualLongCode/tollFree/shortCode/p2p (single value or array) limit to the selected channel(s); when one channel is selected, totalErrors reflects only that channel.
226
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options]
227
- * @returns {Promise<Object>} - { rows, totalCount, totalErrors: { sms, mms, p2p, all }, page, limit }
265
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
266
+ * @param {RequestOptions} [params.options]
267
+ * @returns {Promise<SdkResponse<any>>} - { rows, totalCount, totalErrors: { sms, mms, p2p, all }, page, limit }
228
268
  */
229
- async getAnalyticsByErrorCode({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, options = {} }) {
230
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel };
269
+ async getAnalyticsByErrorCode({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, dateBounds, options = {} }) {
270
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, carrierFamily, startDate, endDate, page, limit, channel, dateBounds };
231
271
  const queryString = this.client._getQueryString(filters);
232
272
  return this.client(`/message/analytics/by-error-code${queryString}`, { method: "GET", ...options });
233
273
  }
@@ -243,14 +283,15 @@ export class Messages {
243
283
  * @param {string} [params.campaignId] - Filter by the ID of the associated campaign
244
284
  * @param {string} [params.phoneNumber] - Filter by the phone number
245
285
  * @param {string} [params.carrier] - Filter by the carrier
246
- * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
247
- * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
286
+ * @param {string} [params.startDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode
287
+ * @param {string} [params.endDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back
248
288
  * @param {string|string[]} [params.channel] - Filter by channel (tenDLC, virtualLongCode, tollFree, shortCode). Opt-outs are A2P-only, so p2p applies no filter. Filters on the stored channel column; a tenDLC selection also includes older opt-outs that have no channel.
249
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
250
- * @returns {Promise<Object>} - A promise that resolves to the DNC analytics data (totals, byDate, byPhoneNumber, byCarrier, byKeyword). byKeyword breaks opt-outs down by the normalized keyword that revoked consent ({ optOutKeyword, total, sms, mms }); opt-outs recorded before keyword capture shipped appear as a single optOutKeyword: null bucket ("not recorded"). The distinct keyword set is open-ended (campaigns register their own keywords beyond the mandatory floor) — render top-N plus Other, never a fixed list.
289
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
290
+ * @param {RequestOptions} [params.options] - Additional options for the request
291
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the DNC analytics data (totals, byDate, byPhoneNumber, byCarrier, byKeyword). byKeyword breaks opt-outs down by the normalized keyword that revoked consent ({ optOutKeyword, total, sms, mms }); opt-outs recorded before keyword capture shipped appear as a single optOutKeyword: null bucket ("not recorded"). The distinct keyword set is open-ended (campaigns register their own keywords beyond the mandatory floor) — render top-N plus Other, never a fixed list.
251
292
  */
252
- async getDncAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel, options = {} }) {
253
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel };
293
+ async getDncAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel, dateBounds, options = {} }) {
294
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel, dateBounds };
254
295
  const queryString = this.client._getQueryString(filters);
255
296
  return this.client(`/message/dnc/analytics${queryString}`, { method: "GET", ...options });
256
297
  }
@@ -266,18 +307,19 @@ export class Messages {
266
307
  * @param {string} [params.campaignId] - Filter by the ID of the associated campaign
267
308
  * @param {string} [params.phoneNumber] - Filter by the phone number
268
309
  * @param {string} [params.carrier] - Filter by the carrier
269
- * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
270
- * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
310
+ * @param {string} [params.startDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode
311
+ * @param {string} [params.endDate] - ISO-8601 date or timestamp; bound per dateBounds: a bare date is always that whole UTC day (inclusive); a timestamp is widened to its whole UTC day in "day" mode and bound as the instant it names, in whatever offset it was written, in "exact" mode. In "exact" mode day buckets align to the UTC offset this bound carries, so a client that sends its own midnights gets its own calendar dates back
271
312
  * @param {number} [params.page] - Page number for pagination
272
313
  * @param {number} [params.limit] - Number of records per page
273
314
  * @param {string} [params.sortField] - Field to sort by
274
315
  * @param {string} [params.sortOrder] - Sort direction (asc, desc)
275
316
  * @param {string|string[]} [params.channel] - Filter by channel (tenDLC, virtualLongCode, tollFree, shortCode). Opt-outs are A2P-only, so p2p applies no filter. Filters on the stored channel column; a tenDLC selection also includes older opt-outs that have no channel.
276
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
277
- * @returns {Promise<Object>} - A promise that resolves to the paginated DNC records. Each record carries optOutKeyword — the normalized keyword that revoked consent (e.g. "stop", "opt out"; a repeat opt-out records the most recent word), null for opt-outs recorded before keyword capture shipped — and carrierFamily, the resolved carrier family of the recipient number (Default, ATT, TMobile, Verizon, USCellular, GoogleVoice, ClearSky, Interop, RogueMobile), copied from the inbound message that triggered the opt-out.
317
+ * @param {("day"|"exact")} [params.dateBounds] - Which contract binds startDate/endDate. "day": each bound is widened to its whole UTC day. "exact": a timestamp is bound as the instant it names and a bare date is its whole UTC day. Omitted: "day".
318
+ * @param {RequestOptions} [params.options] - Additional options for the request
319
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the paginated DNC records. Each record carries optOutKeyword — the normalized keyword that revoked consent (e.g. "stop", "opt out"; a repeat opt-out records the most recent word), null for opt-outs recorded before keyword capture shipped — and carrierFamily, the resolved carrier family of the recipient number (Default, ATT, TMobile, Verizon, USCellular, GoogleVoice, ClearSky, Interop, RogueMobile), copied from the inbound message that triggered the opt-out.
278
320
  */
279
- async getDncRecords({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel, options = {} }) {
280
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel };
321
+ async getDncRecords({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel, dateBounds, options = {} }) {
322
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel, dateBounds };
281
323
  const queryString = this.client._getQueryString(filters);
282
324
  return this.client(`/message/dnc${queryString}`, { method: "GET", ...options });
283
325
  }
@@ -303,8 +345,8 @@ export class Messages {
303
345
  * @param {string} [params.statusCallbackUrl] - The URL to receive status callbacks
304
346
  * @param {boolean} [params.enableShortlink=false] - Whether to enable shortlink in the message
305
347
  * @param {boolean} [params.filterLandlinesAndInactiveNumbers=false] - Whether to filter out landline and inactive numbers before sending
306
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
307
- * @returns {Promise<Object>} - A promise that resolves to the response data from the server
348
+ * @param {RequestOptions} [params.options] - Additional options for the request
349
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the response data from the server
308
350
  */
309
351
  async sendSMS({ senderPhoneNumber, recipientPhoneNumbers, messageBody, statusCallbackUrl, enableShortlink = false, filterLandlinesAndInactiveNumbers = false, options = {} }) {
310
352
  this.client._require({ senderPhoneNumber, recipientPhoneNumbers, messageBody });
@@ -330,8 +372,8 @@ export class Messages {
330
372
  * @param {boolean} [params.useSignalHouseShortlinks] - When false, SignalHouse applies no link-shortening
331
373
  * or text-spin and your pre-spun links/content are sent verbatim (bring-your-own spinner). Defaults to
332
374
  * true server-side (the SignalHouse shortlink system, which mints unique per-recipient links).
333
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
334
- * @returns {Promise<Object>} - A promise that resolves to the response data from the server
375
+ * @param {RequestOptions} [params.options] - Additional options for the request
376
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the response data from the server
335
377
  */
336
378
  async sendP2P({ recipientPhoneNumbers, messageBody, statusCallbackUrl, groupId, subgroupId, useSignalHouseShortlinks, options = {} }) {
337
379
  this.client._require({ recipientPhoneNumbers, messageBody });
@@ -353,9 +395,9 @@ export class Messages {
353
395
  * @roles api, admin, developer, billing, user
354
396
  * @param {Object} params - The parameters for getting a P2P batch
355
397
  * @param {string} params.batchId - The ID of the P2P batch to retrieve
356
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
398
+ * @param {RequestOptions} [params.options] - Additional options for the request
357
399
  * @throws {Error} Throws an error if the batchId parameter is missing
358
- * @returns {Promise<Object>} - The P2P batch object returned from the server
400
+ * @returns {Promise<SdkResponse<any>>} - The P2P batch object returned from the server
359
401
  */
360
402
  async getP2PBatch({ batchId, options = {} }) {
361
403
  this.client._require({ batchId });
@@ -387,8 +429,8 @@ export class Messages {
387
429
  * @param {boolean} [params.enableCompression=true] - Whether to enable image compression for the image attachments
388
430
  * @param {boolean} [params.filterLandlinesAndInactiveNumbers=false] - Whether to filter out landline and inactive numbers before sending
389
431
  * @param {File[]} [params.images] - The images to attach to the MMS message
390
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
391
- * @returns {Promise<Object>} - A promise that resolves to the response data from the server
432
+ * @param {RequestOptions} [params.options] - Additional options for the request
433
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the response data from the server
392
434
  */
393
435
  async sendMMS({
394
436
  senderPhoneNumber,
@@ -425,9 +467,9 @@ export class Messages {
425
467
  formData.append("statusCallbackUrl", statusCallbackUrl);
426
468
  }
427
469
 
428
- formData.append("enableShortlink", enableShortlink);
429
- formData.append("enableCompression", enableCompression);
430
- if (filterLandlinesAndInactiveNumbers) formData.append("filterLandlinesAndInactiveNumbers", filterLandlinesAndInactiveNumbers);
470
+ formData.append("enableShortlink", String(enableShortlink));
471
+ formData.append("enableCompression", String(enableCompression));
472
+ if (filterLandlinesAndInactiveNumbers) formData.append("filterLandlinesAndInactiveNumbers", String(filterLandlinesAndInactiveNumbers));
431
473
 
432
474
  return this.multipartClient(`/message/mms`, { method: "POST", body: formData, ...options });
433
475
  }
@@ -456,8 +498,8 @@ export class Messages {
456
498
  * @param {boolean} [params.enableCompression=true] - Whether to enable image compression for the image attachments
457
499
  * @param {boolean} [params.filterLandlinesAndInactiveNumbers=false] - Whether to filter out landline and inactive numbers before sending
458
500
  * @param {File[]} [params.images] - The images to attach to the MMS message
459
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
460
- * @returns {Promise<Object>} - A promise that resolves to the response data from the server
501
+ * @param {RequestOptions} [params.options] - Additional options for the request
502
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to the response data from the server
461
503
  */
462
504
  async sendGroupMessage({
463
505
  senderPhoneNumber,
@@ -494,9 +536,9 @@ export class Messages {
494
536
  formData.append("statusCallbackUrl", statusCallbackUrl);
495
537
  }
496
538
 
497
- formData.append("enableShortlink", enableShortlink);
498
- formData.append("enableCompression", enableCompression);
499
- if (filterLandlinesAndInactiveNumbers) formData.append("filterLandlinesAndInactiveNumbers", filterLandlinesAndInactiveNumbers);
539
+ formData.append("enableShortlink", String(enableShortlink));
540
+ formData.append("enableCompression", String(enableCompression));
541
+ if (filterLandlinesAndInactiveNumbers) formData.append("filterLandlinesAndInactiveNumbers", String(filterLandlinesAndInactiveNumbers));
500
542
 
501
543
  return this.multipartClient(`/message/groupMessage`, { method: "POST", body: formData, ...options });
502
544
  }
@@ -507,8 +549,8 @@ export class Messages {
507
549
  * @roles api, admin, developer, billing, user
508
550
  * @param {Object} params - The parameters for looking up a carrier
509
551
  * @param {string} params.phoneNumber - The phone number to look up (10+ digits, no + prefix)
510
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
511
- * @returns {Promise<Object>} - A promise that resolves to carrier information
552
+ * @param {RequestOptions} [params.options] - Additional options for the request
553
+ * @returns {Promise<SdkResponse<any>>} - A promise that resolves to carrier information
512
554
  */
513
555
  async carrierIdLookup({ phoneNumber, options = {} }) {
514
556
  this.client._require({ phoneNumber });
@@ -1,3 +1,9 @@
1
+ /** @typedef {import('../SignalHouseSDK.js').RequestOptions} RequestOptions */
2
+ /**
3
+ * @template T
4
+ * @typedef {import('../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
5
+ */
6
+
1
7
  export class Notifications {
2
8
  constructor(client, enableAdmin) {
3
9
  this.client = client;
@@ -15,8 +21,8 @@ export class Notifications {
15
21
  * @param {number} [params.limit] - Results per page (min 1, max 100, default 10)
16
22
  * @param {string} [params.status] - Filter by status: "READ" or "UNREAD"
17
23
  * @param {string|string[]} [params.eventTypes] - Event types to filter by (comma-separated string or array)
18
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
19
- * @returns {Promise<Object>} The response from the server
24
+ * @param {RequestOptions} [params.options] - Additional options for the request
25
+ * @returns {Promise<SdkResponse<any>>} The response from the server
20
26
  */
21
27
  async getNotifications({ id, groupId, page, limit, status, eventTypes, options = {} }) {
22
28
  this.client._require({ "id or groupId": id ?? groupId });
@@ -31,8 +37,8 @@ export class Notifications {
31
37
  * @param {Object} params
32
38
  * @param {string|string[]} params.ids - Notification id or array of notification ids
33
39
  * @param {string} params.status - New status: "READ" or "UNREAD"
34
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
35
- * @returns {Promise<Object>} The response from the server
40
+ * @param {RequestOptions} [params.options] - Additional options for the request
41
+ * @returns {Promise<SdkResponse<any>>} The response from the server
36
42
  */
37
43
  async updateNotificationStatus({ ids, status, options = {} }) {
38
44
  this.client._require({ ids, status });
@@ -45,8 +51,8 @@ export class Notifications {
45
51
  * @roles signalhouse_api, signalhouse_admin, signalhouse_user, api, admin, developer, billing, user
46
52
  * @param {Object} params
47
53
  * @param {string} params.id - The notification id to delete
48
- * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
49
- * @returns {Promise<Object>} The response from the server
54
+ * @param {RequestOptions} [params.options] - Additional options for the request
55
+ * @returns {Promise<SdkResponse<any>>} The response from the server
50
56
  */
51
57
  async deleteNotification({ id, options = {} }) {
52
58
  this.client._require({ id });