@signalhousellc/sdk 1.0.55 → 1.0.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalhousellc/sdk",
3
- "version": "1.0.55",
3
+ "version": "1.0.61",
4
4
  "description": "Signal House SDK for use with the Signal House platform",
5
5
  "type": "module",
6
6
  "main": "src/SignalHouseSDK.js",
@@ -19,6 +19,7 @@ import { Onboarding } from "./domains/Onboarding.js";
19
19
  import { Shortlinks } from "./domains/Shortlinks.js";
20
20
  import { Subgroups } from "./domains/Subgroups.js";
21
21
  import { Subscriptions } from "./domains/Subscriptions.js";
22
+ import { Tickets } from "./domains/Tickets.js";
22
23
  import { Users } from "./domains/Users.js";
23
24
  import { Notifications } from "./domains/Notifications.js";
24
25
  import { Webhooks } from "./domains/Webhooks.js";
@@ -56,7 +57,7 @@ export class SignalHouseSDK {
56
57
  this.auth = new Auth(client, this.enableAdmin);
57
58
  this.billing = new Billing(client, this.enableAdmin);
58
59
  this.brands = new Brands(client, multipartClient, this.enableAdmin);
59
- this.campaigns = new Campaigns(client, this.enableAdmin);
60
+ this.campaigns = new Campaigns(client, multipartClient, this.enableAdmin);
60
61
  this.groups = new Groups(client, this.enableAdmin);
61
62
  this.landings = new Landings(client, multipartClient, this.enableAdmin);
62
63
  this.messages = new Messages(client, multipartClient, this.enableAdmin);
@@ -65,6 +66,7 @@ export class SignalHouseSDK {
65
66
  this.shortlinks = new Shortlinks(client, this.enableAdmin);
66
67
  this.subgroups = new Subgroups(client, this.enableAdmin);
67
68
  this.subscriptions = new Subscriptions(client, this.enableAdmin);
69
+ this.tickets = new Tickets(client, this.enableAdmin);
68
70
  this.users = new Users(client, this.enableAdmin);
69
71
  this.notifications = new Notifications(client, this.enableAdmin);
70
72
  this.webhooks = new Webhooks(client, this.enableAdmin);
@@ -33,13 +33,15 @@ export class Auth {
33
33
  * @param {string} [params.volume] - Estimated monthly message volume
34
34
  * @param {string[]} [params.products] - Products the account intends to integrate
35
35
  * @param {boolean} [params.acceptedTermsOfService] - Whether the user accepted the Terms of Service
36
+ * @param {string} [params.psXid] - PartnerStack click id (ps_xid) captured from a referral link
37
+ * @param {string} [params.psPartnerKey] - PartnerStack partner key (ps_partner_key) captured from a referral link
36
38
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
37
39
  * @returns {Promise<Object>} The response from the server (contains the JWT token)
38
40
  */
39
- async register({ businessName, fullName, email, password, phone, dialCode, country, volume, products, acceptedTermsOfService, options = {} }) {
41
+ async register({ businessName, fullName, email, password, phone, dialCode, country, volume, products, acceptedTermsOfService, psXid, psPartnerKey, options = {} }) {
40
42
  return this.client(`/auth/register`, {
41
43
  method: "POST",
42
- body: { businessName, fullName, email, password, phone, dialCode, country, volume, products, acceptedTermsOfService },
44
+ body: { businessName, fullName, email, password, phone, dialCode, country, volume, products, acceptedTermsOfService, psXid, psPartnerKey },
43
45
  ...options,
44
46
  });
45
47
  }
@@ -147,6 +149,19 @@ export class Auth {
147
149
  return this.client(`/auth/logout-all`, { method: "POST", ...options });
148
150
  }
149
151
 
152
+ /**
153
+ * Get the Group ID associated with the caller's JWT (the caller's active group). Useful for
154
+ * discovering your own groupId, which is required on most other API requests.
155
+ * @async
156
+ * @roles admin, developer, billing, user, api
157
+ * @param {Object} [params]
158
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
159
+ * @returns {Promise<Object>} The response containing { groupId }
160
+ */
161
+ async getGroupId({ options = {} } = {}) {
162
+ return this.client(`/auth/group-id`, { method: "GET", ...options });
163
+ }
164
+
150
165
  /**
151
166
  * Mint a single-use, short-lived external-link token for the authenticated caller. The token is
152
167
  * handed to the GHL/Shopify backend so it can link the caller's existing V2 group to its tenant.
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @typedef {string} BrandLookupId - Carrier Brand ID (B… or TFNB…), Mongo `_id`, or internal reference UUID.
3
+ * Use on update, delete, revet, vetting, appeal, and transfer routes. Pending brands return `brandId: null`
4
+ * in read/create responses — poll `GET /brand?id=<Mongo _id>` until a carrier Brand ID is assigned.
5
+ */
6
+
1
7
  /**
2
8
  * @typedef {Object} CreateBrandData
3
9
  * @property {string} subgroupId - The ID of the subgroup to create the brand under
@@ -53,7 +59,7 @@
53
59
  * @property {string} [altBusinessIdType] - The type of the alternative business identifier ("NONE", "DUNS", "LEI", "GIIN")
54
60
  * @property {string} [brandRelationship] - Not in use - reserved for future use
55
61
  * @property {string} [businessContactEmail] - The email address of the business contact for the brand (required if entityType is PUBLIC_PROFIT or NON_PROFIT)
56
- * @property {Object} [tollFree] - Toll-Free editable fields, used when updating a Toll-Free brand. Optional sub-fields: legalEntityType, businessRegistrationType, taxId, countryCode, supportPhone, taxIdIssuingCountry, businessDBA. (Toll-Free brands sync to Infobip; only DCA_PENDING/DCA_ACTIVE brands are editable and subgroupId is immutable.)
62
+ * @property {Object} [tollFree] - Toll-Free editable fields, used when updating a Toll-Free brand. Optional sub-fields: legalEntityType, businessRegistrationType, taxId, countryCode, supportPhone, taxIdIssuingCountry, businessDBA, businessWebsite. (Toll-Free brands sync to Infobip; only DCA_PENDING/DCA_ACTIVE brands are editable and subgroupId is immutable.)
57
63
  */
58
64
 
59
65
  /**
@@ -65,6 +71,7 @@
65
71
  * @property {string} supportPhone - The customer-support phone number (required, at most 20 characters)
66
72
  * @property {string} [taxIdIssuingCountry] - The country that issued the tax ID (optional, 2-letter ISO code)
67
73
  * @property {string} [businessDBA] - The business "doing business as" name (optional, at most 255 characters)
74
+ * @property {string} [businessWebsite] - The customer's real website, sent to Infobip for brand verification (optional, valid URL, at most 255 characters; distinct from the top-level website / landing URL)
68
75
  */
69
76
 
70
77
  /**
@@ -103,7 +110,7 @@ export class Brands {
103
110
  * @async
104
111
  * @roles api, admin, developer, user
105
112
  * @param {Object} params - The parameters for filtering the brands
106
- * @param {string} [params.id] - The ID of the brand to filter by
113
+ * @param {string} [params.id] - Brand lookup id (carrier Brand ID, Mongo _id, or internal reference)
107
114
  * @param {string} [params.subgroupId] - The ID of the subgroup to filter by
108
115
  * @param {string} [params.groupId] - The ID of the group to filter by
109
116
  * @param {number} [params.page] - The page number for pagination
@@ -111,7 +118,7 @@ export class Brands {
111
118
  * @param {string} [params.status] - The status of the brand to filter by (PENDING_CREATION, PENDING_APPROVAL, UNVERIFIED, VERIFIED, VETTED_VERIFIED, PENDING_DELETE, DELETED)
112
119
  * @param {string} [params.registrationType] - Filter by registration type ("TEN_DLC" or "TOLL_FREE")
113
120
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
114
- * @returns {Promise<Array>} The response from the server
121
+ * @returns {Promise<Array<{ _id: string, brandId: string|null, status: string, registrationType: string, [key: string]: any }>>} Brand records. For 10DLC brands in PENDING_CREATION (or rejected before carrier assignment), brandId is null — poll by _id until status advances and brandId is assigned.
115
122
  */
116
123
  async getBrands({ id, subgroupId, groupId, page, limit, status, registrationType, options = {} }) {
117
124
  const filters = { id, subgroupId, groupId, page, limit, status, registrationType };
@@ -124,7 +131,7 @@ export class Brands {
124
131
  * @async
125
132
  * @roles api, admin, developer, user
126
133
  * @param {Object} params - The parameters for getting the external vetting information
127
- * @param {string} params.brandId - The ID of the brand to get the external vetting information for
134
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
128
135
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
129
136
  * @throws {Error} Throws an error if the brandId parameter is missing
130
137
  * @returns {Promise<Object>} The response from the server
@@ -143,7 +150,7 @@ export class Brands {
143
150
  * @param {CreateBrandData} params.brandData - The data for the brand to be created
144
151
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
145
152
  * @throws {Error} Throws an error if the brandData parameter is missing or if required fields in brandData are missing
146
- * @returns {Promise<Object>} The response from the server
153
+ * @returns {Promise<Object>} Brand entity. `brandId` is `null` until the carrier assigns a Brand ID — use `_id` to poll status.
147
154
  */
148
155
  async createBrand({ brandData, options = {} }) {
149
156
  this.client._require({ brandData: brandData });
@@ -158,7 +165,7 @@ export class Brands {
158
165
  * @param {CreateTollFreeBrandData} params.brandData - The data for the toll-free brand to be created (see CreateTollFreeBrandData typedef for details; TFN-specific fields live under brandData.tollFree, and registrationType is forced server-side by the route)
159
166
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
160
167
  * @throws {Error} Throws an error if the brandData parameter is missing
161
- * @returns {Promise<Object>} The response from the server
168
+ * @returns {Promise<Object>} Brand entity. For Toll-Free, `brandId` is a TFNB-prefixed id when assigned; otherwise `null` — use `_id` to poll.
162
169
  */
163
170
  async createTollFreeBrand({ brandData, options = {} }) {
164
171
  this.client._require({ brandData });
@@ -171,7 +178,7 @@ export class Brands {
171
178
  * @roles api, admin, developer, billing, user
172
179
  * @param {Object} params - The parameters for transferring the brands
173
180
  * @param {string} params.subgroupId - The ID of the subgroup to transfer the brands to
174
- * @param {Array<string>} params.brandIds - The IDs of the brands to be transferred
181
+ * @param {BrandLookupId[]} params.brandIds - Brand lookup ids to transfer (carrier id, Mongo `_id`, or internal reference).
175
182
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
176
183
  * @throws {Error} Throws an error if the subgroupId or brandIds parameters are missing
177
184
  * @returns {Promise<Object>} The response from the server
@@ -187,7 +194,7 @@ export class Brands {
187
194
  * @async
188
195
  * @roles api, admin, developer, billing, user
189
196
  * @param {Object} params - The parameters for creating external vetting
190
- * @param {string} params.brandId - The ID of the brand to create external vetting for
197
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
191
198
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
192
199
  * @throws {Error} Throws an error if the brandId parameter is missing
193
200
  * @returns {Promise<Object>} The response from the server
@@ -207,7 +214,7 @@ export class Brands {
207
214
  * @async
208
215
  * @roles api, admin, developer, billing, user
209
216
  * @param {Object} params - The parameters for importing external vetting
210
- * @param {string} params.brandId - The ID of the brand to import external vetting for
217
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
211
218
  * @param {string} params.vettingProviderId - The external vetting provider (AEGIS, WMC, CV)
212
219
  * @param {string} params.vettingId - The provider-issued vetting / transaction ID to import
213
220
  * @param {string} [params.vettingToken] - The provider-issued vetting token (required by some providers, e.g. AEGIS)
@@ -226,7 +233,7 @@ export class Brands {
226
233
  * @async
227
234
  * @roles api, admin, developer, billing, user
228
235
  * @param {Object} params - The parameters for updating the brand (see UpdateBrandData typedef for details)
229
- * @param {string} params.brandId - The ID of the brand to update
236
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
230
237
  * @param {UpdateBrandData} params.brandData - The data for the brand to be updated
231
238
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
232
239
  * @throws {Error} Throws an error if the brandId parameter is missing
@@ -243,7 +250,7 @@ export class Brands {
243
250
  * @async
244
251
  * @roles api, admin, developer, billing, user
245
252
  * @param {Object} params - The parameters for reverting the brand
246
- * @param {string} params.brandId - The ID of the brand to revert
253
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
247
254
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
248
255
  * @throws {Error} Throws an error if the brandId parameter is missing
249
256
  * @returns {Promise<Object>} The response from the server
@@ -259,7 +266,7 @@ export class Brands {
259
266
  * @async
260
267
  * @roles api, admin, developer, billing, user
261
268
  * @param {Object} params - The parameters for deleting the brand
262
- * @param {string} params.brandId - The ID of the brand to delete
269
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
263
270
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
264
271
  * @throws {Error} Throws an error if the brandId parameter is missing
265
272
  * @returns {Promise<Object>} The response from the server
@@ -275,7 +282,7 @@ export class Brands {
275
282
  * @async
276
283
  * @roles api, admin, developer, billing, user
277
284
  * @param {Object} params - The parameters for getting the appeal history
278
- * @param {string} params.brandId - The ID of the brand to get appeal history for
285
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
279
286
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
280
287
  * @throws {Error} Throws an error if the brandId parameter is missing
281
288
  * @returns {Promise<Array>} Array of BrandAppeal objects
@@ -291,7 +298,7 @@ export class Brands {
291
298
  * @async
292
299
  * @roles api, admin, developer, billing, user
293
300
  * @param {Object} params - The parameters for submitting the appeal
294
- * @param {string} params.brandId - The ID of the brand to appeal
301
+ * @param {BrandLookupId} params.brandId - Brand lookup id (carrier Brand ID, Mongo `_id`, or internal reference).
295
302
  * @param {Array<string>} params.appealCategories - Array of appeal categories (VERIFY_TAX_ID, VERIFY_NON_PROFIT, VERIFY_GOVERNMENT)
296
303
  * @param {string} params.explanation - Required explanation justifying the appeal
297
304
  * @param {File|Blob} params.file - The supporting document file
@@ -64,12 +64,12 @@
64
64
  * @property {string} [privacyPolicyLink] - A URL linking to the privacy policy for the campaign (optional, must be at most 2048 characters if provided)
65
65
  * @property {string} [termsAndConditionsLink] - A URL linking to the terms and conditions for the campaign (optional, must be at most 2048 characters if provided)
66
66
  * @property {string} [embeddedLinkSample] - A sample of the embedded link used in the campaign (optional, must be at most 255 characters if provided)
67
- * @property {Object} [tollFree] - Toll-Free editable fields, used when updating a Toll-Free campaign. Mirrors the CreateTollFreeCampaignData `tollFree` object with every field optional (useCase, messageVolume, programSummary (≤500), exampleMessage, customerCareEmail, optInImageURLs, optIns, multiNumberReason (≤500)). Phone numbers cannot be changed via update — Toll-Free numbers are locked to their campaign.
67
+ * @property {Object} [tollFree] - Toll-Free editable fields, used when updating a Toll-Free campaign. Mirrors the CreateTollFreeCampaignData `tollFree` object with every field optional (useCase, messageVolume, programSummary (40-500), exampleMessage, customerCareEmail, optInImageURLs, optIns, multiNumberReason (≤500)). Phone numbers cannot be changed via update — Toll-Free numbers are locked to their campaign.
68
68
  */
69
69
 
70
70
  /**
71
71
  * @typedef {Object} TollFreeOptIn
72
- * @property {string} [callToAction] - The opt-in call-to-action text for this channel (optional, at most 2048 characters)
72
+ * @property {string} [callToAction] - The opt-in call-to-action text for this channel (optional, 20-1024 characters when provided)
73
73
  * @property {string} [url] - The opt-in web URL (web channel only, optional, at most 2048 characters)
74
74
  * @property {Array<string>} [keywords] - The opt-in keywords (keyword channel only, optional)
75
75
  */
@@ -78,7 +78,7 @@
78
78
  * @typedef {Object} TollFreeCampaignDetails
79
79
  * @property {string} useCase - The Toll-Free use case (required, e.g. TWO_FA, GENERAL_MARKETING, HEALTHCARE, MIXED — one of the Toll-Free use-case values)
80
80
  * @property {string} messageVolume - The estimated monthly message volume tier (required, one of TEN, HUNDRED, THOUSAND, TEN_THOUSAND, HUNDRED_THOUSAND, TWO_HUNDRED_FIFTY_THOUSAND, FIVE_HUNDRED_THOUSAND, SEVEN_HUNDRED_FIFTY_THOUSAND, ONE_MILLION, FIVE_MILLION, TEN_MILLION_PLUS)
81
- * @property {string} programSummary - A summary of the messaging program (required, at most 500 characters)
81
+ * @property {string} programSummary - A summary of the messaging program (required, 40-500 characters)
82
82
  * @property {string} exampleMessage - An example message (required, at most 4096 characters; stored verbatim, no STOP/HELP appended by the server)
83
83
  * @property {string} customerCareEmail - A customer-care contact email (required, valid email)
84
84
  * @property {Array<string>} optInImageURLs - URLs to opt-in proof images (required, at least one, each at most 2048 characters)
@@ -102,8 +102,9 @@
102
102
  */
103
103
 
104
104
  export class Campaigns {
105
- constructor(client, enableAdmin) {
105
+ constructor(client, multipartClient, enableAdmin) {
106
106
  this.client = client;
107
+ this.multipartClient = multipartClient;
107
108
  this.enableAdmin = enableAdmin;
108
109
 
109
110
  if (enableAdmin) {
@@ -178,6 +179,22 @@ export class Campaigns {
178
179
  return this.client(`/campaign${queryString}`, { method: "GET", ...options });
179
180
  }
180
181
 
182
+ /**
183
+ * Get aggregated campaign health (7-day and 30-day windows) for a campaign.
184
+ * @async
185
+ * @roles api, admin, developer, billing, user
186
+ * @param {Object} params - Parameters for reading campaign health
187
+ * @param {string} params.campaignId - Campaign identifier
188
+ * @param {boolean} [params.includeNumbers=false] - When true, include per-number health entries
189
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional request options
190
+ * @returns {Promise<Object>} A promise that resolves to `{ campaignId, sevenDay, thirtyDay, numbers? }`
191
+ */
192
+ async getCampaignHealth({ campaignId, includeNumbers = false, options = {} }) {
193
+ this.client._require({ campaignId });
194
+ const queryString = this.client._getQueryString({ campaignId, includeNumbers });
195
+ return this.client(`/campaign/health${queryString}`, { method: "GET", ...options });
196
+ }
197
+
181
198
  /**
182
199
  * Create a new campaign
183
200
  * @async
@@ -208,6 +225,40 @@ export class Campaigns {
208
225
  return this.client(`/campaign/toll-free`, { method: "POST", body: campaignData, ...options });
209
226
  }
210
227
 
228
+ /**
229
+ * Upload an opt-in proof image to be hosted on-platform, returning a publicly-fetchable URL suitable for a
230
+ * Toll-Free campaign's optInImageURLs
231
+ * @async
232
+ * @roles api, admin, developer, user
233
+ * @param {Object} params - The parameters for uploading the opt-in image
234
+ * @param {Buffer|Blob|File} params.file - The image file to upload, provided as a Buffer/Blob/File
235
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
236
+ * @throws {Error} Throws an error if the file parameter is missing
237
+ * @returns {Promise<Object>} The response from the server containing the hosted image's id and URL
238
+ */
239
+ async uploadOptInImage({ file, options = {} }) {
240
+ this.client._require({ file });
241
+ const formData = new FormData();
242
+ formData.append("file", file);
243
+ return this.multipartClient(`/campaign/opt-in-image`, { method: "POST", body: formData, ...options });
244
+ }
245
+
246
+ /**
247
+ * Auto-capture an opt-in proof image from a brand's generated landing page and host it on-platform,
248
+ * returning a publicly-fetchable URL suitable for a Toll-Free campaign's optInImageURLs
249
+ * @async
250
+ * @roles api, admin, developer, user
251
+ * @param {Object} params - The parameters for capturing the opt-in image
252
+ * @param {string} params.brandId - The ID of the brand whose generated landing page should be captured
253
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
254
+ * @throws {Error} Throws an error if the brandId parameter is missing
255
+ * @returns {Promise<Object>} The response from the server containing the hosted image's id and URL
256
+ */
257
+ async captureOptInImageFromLanding({ brandId, options = {} }) {
258
+ this.client._require({ brandId });
259
+ return this.client(`/campaign/opt-in-image/from-landing`, { method: "POST", body: { brandId }, ...options });
260
+ }
261
+
211
262
  /**
212
263
  * Update an existing campaign
213
264
  * @async
@@ -18,7 +18,7 @@ export class Messages {
18
18
  * @param {string} [params.status] - Filter messages by their status (ENQUEUED, DEQUEUED, SENT, FAILED, DELIVERED)
19
19
  * @param {string} [params.direction] - Filter messages by their direction (INBOUND, OUTBOUND)
20
20
  * @param {string|string[]} [params.messageType] - Filter messages by their type. Accepts a single value or an array (e.g. ["SMS", "MMS"]). Allowed values: SMS, MMS, RCS, WHATSAPP, VIBER, P2P. When omitted, defaults to all non-P2P types.
21
- * @param {string|string[]} [params.channel] - Filter messages by messaging channel. Accepts a single value or an array (cumulative). Allowed values: TEN_DLC, TOLL_FREE, P2P. Channel is derived on read from the associated campaign/brand registrationType (or messageType "P2P"); it is not stored on the message. Ignored when matchAny is true (matchAny takes precedence).
21
+ * @param {string|string[]} [params.channel] - Filter messages by channel. Accepts a single value or an array. Allowed values: tenDLC, tollFree, p2p. Each message records its channel when it is saved, and this filters on that stored value; older messages from before this feature have no channel and count as tenDLC. Ignored when matchAny is true (matchAny takes precedence).
22
22
  * @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.
23
23
  * @param {string} [params.carrier] - Filter messages by the carrier used for sending
24
24
  * @param {string} [params.senderPhoneNumber] - Filter messages by the sender's phone number
@@ -94,7 +94,7 @@ export class Messages {
94
94
  * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
95
95
  * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
96
96
  * @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.
97
- * @param {string|string[]} [params.channel] - Filter analytics by messaging channel (TEN_DLC, TOLL_FREE). Derived on read from the campaign/brand registrationType; resolves to the channel's campaigns so the analytics count matches the channel-filtered message list. Accepts a single value or array (cumulative). Ignored when matchAny is true (matchAny takes precedence).
97
+ * @param {string|string[]} [params.channel] - Filter analytics by channel (tenDLC, tollFree, 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).
98
98
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
99
99
  * @returns {Promise<Object>} - A promise that resolves to the analytics data
100
100
  */
@@ -117,11 +117,12 @@ export class Messages {
117
117
  * @param {string} [params.carrier] - Filter analytics by the carrier used for sending
118
118
  * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
119
119
  * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
120
+ * @param {string|string[]} [params.channel] - Filter analytics by channel (tenDLC, tollFree, 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.
120
121
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
121
122
  * @returns {Promise<Array>} - A promise that resolves to an array of analytics snapshot records
122
123
  */
123
- async getAnalyticsDetail({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, options = {} }) {
124
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate };
124
+ async getAnalyticsDetail({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel, options = {} }) {
125
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel };
125
126
  const queryString = this.client._getQueryString(filters);
126
127
  return this.client(`/message/analytics/detail${queryString}`, { method: "GET", ...options });
127
128
  }
@@ -158,7 +159,7 @@ export class Messages {
158
159
  * @param {string} [params.endDate]
159
160
  * @param {number} [params.page=1]
160
161
  * @param {number} [params.limit=50] - max 50 per page
161
- * @param {"tenDLC"|"p2p"|"both"} [params.channel="both"] - Scope the ORDER BY + row inclusion. "tenDLC" excludes rows with no SMS/MMS activity; "p2p" excludes rows with no P2P activity; "both" returns rows with any activity.
162
+ * @param {("tenDLC"|"tollFree"|"p2p"|"both")|Array<"tenDLC"|"tollFree"|"p2p">} [params.channel="both"] - Filter and rank rows by the stored channel column. "both" (default) means all activity. tenDLC/tollFree/p2p (single value or array) limit to the selected channel(s); toll-free is kept separate from 10DLC using the stored channel column.
162
163
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options]
163
164
  * @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).
164
165
  */
@@ -184,7 +185,7 @@ export class Messages {
184
185
  * @param {string} [params.endDate]
185
186
  * @param {number} [params.page=1]
186
187
  * @param {number} [params.limit=50] - max 50 per page
187
- * @param {"tenDLC"|"p2p"|"both"} [params.channel="both"] - Scope the ORDER BY + totalCount. "tenDLC" excludes rows with no SMS/MMS errors; "p2p" excludes rows with no P2P errors; "both" returns rows with any error.
188
+ * @param {("tenDLC"|"tollFree"|"p2p"|"both")|Array<"tenDLC"|"tollFree"|"p2p">} [params.channel="both"] - Filter and rank rows by the stored channel column. "both" (default) means every code. tenDLC/tollFree/p2p (single value or array) limit to the selected channel(s); when one channel is selected, totalErrors reflects only that channel.
188
189
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options]
189
190
  * @returns {Promise<Object>} - { rows, totalCount, totalErrors: { sms, mms, p2p, all }, page, limit }
190
191
  */
@@ -207,11 +208,12 @@ export class Messages {
207
208
  * @param {string} [params.carrier] - Filter by the carrier
208
209
  * @param {string} [params.startDate] - ISO-8601 date or timestamp; normalized to start-of-UTC-day (inclusive)
209
210
  * @param {string} [params.endDate] - ISO-8601 date or timestamp; normalized to end-of-UTC-day (inclusive). Hourly resolution is not supported
211
+ * @param {string|string[]} [params.channel] - Filter by channel (tenDLC, tollFree). 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.
210
212
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
211
213
  * @returns {Promise<Object>} - A promise that resolves to the DNC analytics data (totals, byDate, byPhoneNumber, byCarrier)
212
214
  */
213
- async getDncAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, options = {} }) {
214
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate };
215
+ async getDncAnalytics({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel, options = {} }) {
216
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, channel };
215
217
  const queryString = this.client._getQueryString(filters);
216
218
  return this.client(`/message/dnc/analytics${queryString}`, { method: "GET", ...options });
217
219
  }
@@ -233,11 +235,12 @@ export class Messages {
233
235
  * @param {number} [params.limit] - Number of records per page
234
236
  * @param {string} [params.sortField] - Field to sort by
235
237
  * @param {string} [params.sortOrder] - Sort direction (asc, desc)
238
+ * @param {string|string[]} [params.channel] - Filter by channel (tenDLC, tollFree). 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.
236
239
  * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
237
240
  * @returns {Promise<Object>} - A promise that resolves to the paginated DNC records
238
241
  */
239
- async getDncRecords({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, options = {} }) {
240
- const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder };
242
+ async getDncRecords({ groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel, options = {} }) {
243
+ const filters = { groupId, subgroupId, brandId, campaignId, phoneNumber, carrier, startDate, endDate, page, limit, sortField, sortOrder, channel };
241
244
  const queryString = this.client._getQueryString(filters);
242
245
  return this.client(`/message/dnc${queryString}`, { method: "GET", ...options });
243
246
  }
@@ -68,6 +68,22 @@ export class Numbers {
68
68
  return this.client(`/number${queryString}`, { method: "GET", ...options });
69
69
  }
70
70
 
71
+ /**
72
+ * Get the portal-parity health score (1–10) for an owned phone number over 7-day and 30-day windows
73
+ * @async
74
+ * @roles api, admin, developer, billing, user
75
+ * @param {Object} params - The parameters for reading number health
76
+ * @param {string} params.phoneNumber - Owned phone number to score (11 digits, no + prefix)
77
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
78
+ * @throws {Error} Throws an error if the phoneNumber parameter is missing
79
+ * @returns {Promise<Object>} A promise that resolves to `{ phoneNumber, groupId, sevenDay, thirtyDay }`
80
+ */
81
+ async getNumberHealth({ phoneNumber, options = {} }) {
82
+ this.client._require({ phoneNumber });
83
+ const queryString = this.client._getQueryString({ phoneNumber });
84
+ return this.client(`/number/health${queryString}`, { method: "GET", ...options });
85
+ }
86
+
71
87
  /**
72
88
  * Get a list of available phone numbers for purchase with optional filters
73
89
  * @async
@@ -0,0 +1,80 @@
1
+ export class Tickets {
2
+ constructor(client, enableAdmin) {
3
+ this.client = client;
4
+ this.enableAdmin = enableAdmin;
5
+
6
+ // Hidden Admin namespace INSIDE the domain (staff-only customer ticket → Jira)
7
+ if (enableAdmin) {
8
+ this.admin = {
9
+ /**
10
+ * Load Jira form metadata (issue types, priorities, active sprint, field IDs). Staff-only.
11
+ * @async
12
+ * @roles signalhouse
13
+ * @param {Object} [params] - Optional parameters
14
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
15
+ * @returns {Promise<Object>} - Jira form metadata
16
+ */
17
+ getJiraMetadata: async ({ options = {} } = {}) => {
18
+ return this.client(`/admin/ticket/jira/metadata`, { method: "GET", ...options });
19
+ },
20
+
21
+ /**
22
+ * Search open SHGHL epics for the parent picker. Staff-only.
23
+ * @async
24
+ * @roles signalhouse
25
+ * @param {Object} [params]
26
+ * @param {string} [params.query] - Optional epic summary/key filter
27
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
28
+ * @returns {Promise<Object>} - `{ epics: Array<{ id, key, summary }> }`
29
+ */
30
+ searchJiraEpics: async ({ query, options = {} } = {}) => {
31
+ const queryString = this.client._getQueryString({ query });
32
+ return this.client(`/admin/ticket/jira/epics${queryString}`, { method: "GET", ...options });
33
+ },
34
+
35
+ /**
36
+ * Search assignable Jira users for the assignee picker. Staff-only.
37
+ * @async
38
+ * @roles signalhouse
39
+ * @param {Object} [params]
40
+ * @param {string} [params.query] - Partial name or email
41
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
42
+ * @returns {Promise<Object>} - `{ users: Array<{ accountId, displayName, emailAddress }> }`
43
+ */
44
+ searchJiraAssignees: async ({ query, options = {} } = {}) => {
45
+ const queryString = this.client._getQueryString({ query });
46
+ return this.client(`/admin/ticket/jira/assignees${queryString}`, { method: "GET", ...options });
47
+ },
48
+
49
+ /**
50
+ * Suggest Jira labels matching a query. Staff-only.
51
+ * @async
52
+ * @roles signalhouse
53
+ * @param {Object} [params]
54
+ * @param {string} [params.query] - Partial label text
55
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
56
+ * @returns {Promise<Object>} - `{ labels: string[] }`
57
+ */
58
+ searchJiraLabels: async ({ query, options = {} } = {}) => {
59
+ const queryString = this.client._getQueryString({ query });
60
+ return this.client(`/admin/ticket/jira/labels${queryString}`, { method: "GET", ...options });
61
+ },
62
+
63
+ /**
64
+ * Create a Jira ticket from the admin customer ticket form. Staff-only.
65
+ * @async
66
+ * @roles signalhouse
67
+ * @param {Object} params
68
+ * @param {Object} params.data - Create payload (groupId, issueTypeId, priorityId, summary required)
69
+ * @param {import('../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request
70
+ * @throws {Error} Throws an error if the data parameter is missing
71
+ * @returns {Promise<Object>} - Created ticket summary (key, browseUrl, reporter, etc.)
72
+ */
73
+ createJiraTicket: async ({ data, options = {} }) => {
74
+ this.client._require({ data });
75
+ return this.client(`/admin/ticket/jira`, { method: "POST", body: data, ...options });
76
+ },
77
+ };
78
+ }
79
+ }
80
+ }
@@ -1,6 +1,9 @@
1
1
  import { SipTrunks } from "./voice/SipTrunks.js";
2
2
  import { SipProfiles } from "./voice/SipProfiles.js";
3
+ import { ProgrammableVoiceProfiles } from "./voice/ProgrammableVoiceProfiles.js";
3
4
  import { Calls } from "./voice/Calls.js";
5
+ import { CallLogs } from "./voice/CallLogs.js";
6
+ import { GlobalVoiceSettings } from "./voice/GlobalVoiceSettings.js";
4
7
  import { Tokens } from "./voice/Tokens.js";
5
8
 
6
9
  /**
@@ -9,10 +12,13 @@ import { Tokens } from "./voice/Tokens.js";
9
12
  * namespace, accessed via `sdk.voice.*`.
10
13
  *
11
14
  * Sub-resources:
12
- * - `sdk.voice.sipTrunks` — SIP trunk (peer-to-peer connections to PBX/carrier).
13
- * - `sdk.voice.sipProfiles` — SIP profile / endpoint (single registerable UA).
14
- * - `sdk.voice.calls` — Outbound call origination + call log queries.
15
- * - `sdk.voice.tokens` — Mint ephemeral SIP credentials for the browser voice SDK.
15
+ * - `sdk.voice.sipTrunks` — SIP trunk (peer-to-peer connections to PBX/carrier).
16
+ * - `sdk.voice.sipProfiles` — SIP profile / endpoint (single registerable UA).
17
+ * - `sdk.voice.programmableVoiceProfiles` — Programmable Voice Profile (route a set of numbers through Signal House).
18
+ * - `sdk.voice.calls` — Twilio-compat programmatic call control (create/hangup/etc.).
19
+ * - `sdk.voice.callLogs` — Account-scoped call history: list/get + presigned recording.
20
+ * - `sdk.voice.globalVoiceSettings` — Account-wide voice defaults (accepted regions, max spend, E911).
21
+ * - `sdk.voice.tokens` — Mint ephemeral SIP credentials for the browser voice SDK.
16
22
  */
17
23
  export class Voice {
18
24
  constructor(client, enableAdmin) {
@@ -20,7 +26,10 @@ export class Voice {
20
26
  this.enableAdmin = enableAdmin;
21
27
  this.sipTrunks = new SipTrunks(client, enableAdmin);
22
28
  this.sipProfiles = new SipProfiles(client, enableAdmin);
29
+ this.programmableVoiceProfiles = new ProgrammableVoiceProfiles(client, enableAdmin);
23
30
  this.calls = new Calls(client, enableAdmin);
31
+ this.callLogs = new CallLogs(client, enableAdmin);
32
+ this.globalVoiceSettings = new GlobalVoiceSettings(client, enableAdmin);
24
33
  this.tokens = new Tokens(client, enableAdmin);
25
34
  }
26
35
  }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * @typedef {Object} ListCallLogsFilters
3
+ * @property {number} [page] - 1-indexed page (default 1).
4
+ * @property {number} [limit] - Page size (default 20, max 1000).
5
+ * @property {"INBOUND"|"OUTBOUND"|"INTERNAL"} [direction] - Filter by direction.
6
+ * @property {"QUEUED"|"RINGING"|"IN_PROGRESS"|"COMPLETED"|"FAILED"|"BUSY"|"NO_ANSWER"|"CANCELED"} [status] - Filter by status.
7
+ * @property {"SIP_TRUNK"|"PROGRAMMABLE_VOICE"|"AI_VOICE"|"WEBRTC"|"VOICEMAIL"|"CONFERENCE"|"PSTN"} [callSource] - Filter by how the call was handled.
8
+ * @property {string} [subgroupId] - Filter by owning subgroup.
9
+ * @property {string} [sipTrunkId] - Filter by SIP trunk id.
10
+ * @property {string} [programmableVoiceProfileId] - Filter by Programmable Voice Profile id.
11
+ * @property {string} [from] - Filter by caller number (contains).
12
+ * @property {string} [to] - Filter by called number (contains).
13
+ * @property {string} [q] - Free-text search over callId / from / to.
14
+ * @property {"positive"|"negative"|"neutral"} [sentiment] - Filter by sentiment.
15
+ * @property {boolean} [voicemail] - When true, return only voicemails (metadata.voicemail=true).
16
+ * @property {boolean} [hasRecording] - When true, only calls that have a recording (callRecordingUrl set).
17
+ * @property {boolean} [excludeVoicemail] - When true, exclude voicemails (used by the Call Recordings view).
18
+ * @property {string} [dateFrom] - ISO-8601 lower bound on start time.
19
+ * @property {string} [dateTo] - ISO-8601 upper bound on start time.
20
+ * @property {string} [sort] - Sort field (default startTime).
21
+ * @property {"asc"|"desc"} [order] - Sort order (default desc).
22
+ */
23
+
24
+ /**
25
+ * Call Logs — the account-scoped voice call history (voice-backend
26
+ * `/voice/api/v1/call-logs`). Distinct from `voice.calls`, which is the
27
+ * Twilio-compatible programmatic call-control surface; this is the richer log
28
+ * read surface with cost, recording, and voice-config attribution used by the
29
+ * portal Call Logs view. Accessed via `sdk.voice.callLogs`.
30
+ */
31
+ export class CallLogs {
32
+ constructor(client, enableAdmin) {
33
+ this.client = client;
34
+ this.enableAdmin = enableAdmin;
35
+ }
36
+
37
+ /**
38
+ * List call logs for the current account, paginated and filterable.
39
+ * @async
40
+ * @roles api, admin, developer, billing, user
41
+ * @param {Object} [params] - Request parameters.
42
+ * @param {ListCallLogsFilters} [params.filters] - Optional filters.
43
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
44
+ * @returns {Promise<Object>} `{ success, data: [...call logs], meta: { total, page, limit, totalPages } }`.
45
+ */
46
+ async list({ filters = {}, options = {} } = {}) {
47
+ const query = new URLSearchParams();
48
+ for (const [k, v] of Object.entries(filters)) {
49
+ if (v !== undefined && v !== null && v !== "") query.set(k, String(v));
50
+ }
51
+ const qs = query.toString();
52
+ const path = qs ? `/voice/api/v1/call-logs?${qs}` : `/voice/api/v1/call-logs`;
53
+ return this.client(path, { method: "GET", ...options });
54
+ }
55
+
56
+ /**
57
+ * Get a single call log by its UUID or callId.
58
+ * @async
59
+ * @roles api, admin, developer, billing, user
60
+ * @param {Object} params - Request parameters.
61
+ * @param {string} params.id - The call log UUID or callId.
62
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
63
+ * @throws {Error} If id is missing.
64
+ * @returns {Promise<Object>} `{ success, data: { ...call log } }`.
65
+ */
66
+ async get({ id, options = {} }) {
67
+ this.client._require({ id });
68
+ const safeId = encodeURIComponent(id);
69
+ return this.client(`/voice/api/v1/call-logs/${safeId}`, { method: "GET", ...options });
70
+ }
71
+
72
+ /**
73
+ * Get a short-TTL presigned playback URL for a call's recording. Account-scoped
74
+ * — only the owning account can presign. 404 if the call has no recording.
75
+ * @async
76
+ * @roles api, admin, developer, billing, user
77
+ * @param {Object} params - Request parameters.
78
+ * @param {string} params.id - The call log UUID or callId.
79
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
80
+ * @throws {Error} If id is missing.
81
+ * @returns {Promise<Object>} `{ success, data: { callId, url, expiresIn } }`.
82
+ */
83
+ async getRecording({ id, options = {} }) {
84
+ this.client._require({ id });
85
+ const safeId = encodeURIComponent(id);
86
+ return this.client(`/voice/api/v1/call-logs/${safeId}/recording`, { method: "GET", ...options });
87
+ }
88
+
89
+ /**
90
+ * Mark a voicemail (a call log with a recording) read or unread. Account-scoped.
91
+ * @async
92
+ * @roles api, admin, developer
93
+ * @param {Object} params - Request parameters.
94
+ * @param {string} params.id - The call log UUID or callId.
95
+ * @param {boolean} [params.read=true] - true = read, false = unread.
96
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
97
+ * @throws {Error} If id is missing.
98
+ * @returns {Promise<Object>} `{ success, data: { id, voicemailReadAt } }`.
99
+ */
100
+ async markVoicemailRead({ id, read = true, options = {} }) {
101
+ this.client._require({ id });
102
+ const safeId = encodeURIComponent(id);
103
+ return this.client(`/voice/api/v1/call-logs/${safeId}/voicemail-read`, { method: "PATCH", body: { read }, ...options });
104
+ }
105
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @typedef {Object} GlobalVoiceSettingsData
3
+ * @property {string[]} [acceptedRegions] - Accepted regions as country calling codes; empty = all allowed.
4
+ * @property {number|null} [maxSpendPerMinute] - Baseline max spend per minute (dollars); null = no limit.
5
+ * @property {boolean} [e911Enabled] - Whether numbers may dial 911.
6
+ */
7
+
8
+ /**
9
+ * Global Voice Settings — account-wide voice defaults (accepted regions, max
10
+ * spend per minute, E911). SIP trunks and Programmable Voice Profiles override
11
+ * these. Calls the voice-backend service mounted under /voice. Accessed via
12
+ * `sdk.voice.globalVoiceSettings`.
13
+ */
14
+ export class GlobalVoiceSettings {
15
+ constructor(client, enableAdmin) {
16
+ this.client = client;
17
+ this.enableAdmin = enableAdmin;
18
+ }
19
+
20
+ /**
21
+ * Get the current account's global voice settings. Returns `data: null` if the
22
+ * account has never configured them (defaults apply).
23
+ * @async
24
+ * @roles api, admin, developer, billing, user
25
+ * @param {Object} [params] - Request parameters.
26
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
27
+ * @returns {Promise<Object>} `{ success, data: { accountId, acceptedRegions, maxSpendPerMinute, e911Enabled, ... } | null }`.
28
+ */
29
+ async get({ options = {} } = {}) {
30
+ return this.client(`/voice/api/v1/global-voice-settings`, { method: "GET", ...options });
31
+ }
32
+
33
+ /**
34
+ * Upsert the account's global voice settings. Partial — only the fields you
35
+ * provide are written; omitted fields are preserved.
36
+ * @async
37
+ * @roles api, admin, developer
38
+ * @param {Object} params - Request parameters.
39
+ * @param {GlobalVoiceSettingsData} params.settingsData - Fields to set.
40
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
41
+ * @throws {Error} If settingsData is missing.
42
+ * @returns {Promise<Object>} `{ success, data: { ...settings } }`.
43
+ */
44
+ async update({ settingsData, options = {} }) {
45
+ this.client._require({ settingsData });
46
+ return this.client(`/voice/api/v1/global-voice-settings`, { method: "PUT", body: settingsData, ...options });
47
+ }
48
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * @typedef {Object} CreateProgrammableVoiceProfileData
3
+ * @property {string} name - Profile name (required).
4
+ * @property {string[]} subgroupIds - Subgroups this profile spans (required, at least one).
5
+ * @property {string} [region] - Region/POP label.
6
+ * @property {"FORWARD"|"WEBRTC"|"SIP_TRUNK"|"SIP_PROFILE"} [routeAction] - Inbound call action (default "FORWARD"). Deliver the call to a PSTN number (FORWARD), ring the profile's subgroup registered softphones (WEBRTC), send to a SIP trunk (SIP_TRUNK), or ring a registered SIP endpoint (SIP_PROFILE).
7
+ * @property {string} [forwardToE164] - Destination number, required when routeAction is "FORWARD".
8
+ * @property {number} [forwardAfterSeconds] - Ring seconds before the action fires.
9
+ * @property {string} [routeSipTrunkId] - SIP trunk id, required when routeAction is "SIP_TRUNK".
10
+ * @property {string} [routeSipProfileId] - SIP endpoint id, required when routeAction is "SIP_PROFILE".
11
+ * @property {boolean} [recordingEnabled] - Record calls on this profile.
12
+ * @property {boolean} [enabled] - Active state (default true).
13
+ */
14
+
15
+ /**
16
+ * @typedef {Object} UpdateProgrammableVoiceProfileData
17
+ * @property {string} [name] - Profile name.
18
+ * @property {string[]} [subgroupIds] - Subgroups this profile spans.
19
+ * @property {string|null} [region] - Region/POP label.
20
+ * @property {"FORWARD"|"WEBRTC"|"SIP_TRUNK"|"SIP_PROFILE"} [routeAction] - Inbound call action (FORWARD | WEBRTC | SIP_TRUNK | SIP_PROFILE).
21
+ * @property {string|null} [forwardToE164] - FORWARD destination.
22
+ * @property {number|null} [forwardAfterSeconds] - Ring seconds before the action fires.
23
+ * @property {string|null} [routeSipTrunkId] - SIP trunk id (required when routeAction is "SIP_TRUNK").
24
+ * @property {string|null} [routeSipProfileId] - SIP endpoint id (required when routeAction is "SIP_PROFILE").
25
+ * @property {boolean} [recordingEnabled] - Record calls on this profile.
26
+ * @property {boolean} [enabled] - Active state.
27
+ */
28
+
29
+ /**
30
+ * Programmable Voice Profile management. A Programmable Voice Profile groups a
31
+ * set of the account's numbers (across one or more subgroups) under a single
32
+ * inbound call-handling decision routed through Signal House's own voice servers
33
+ * — the same idea as a SIP trunk, but without a customer SBC. A number can
34
+ * belong to at most one profile and is mutually exclusive with SIP trunks.
35
+ *
36
+ * Calls the voice-backend service mounted under /voice. Accessed via
37
+ * `sdk.voice.programmableVoiceProfiles`.
38
+ */
39
+ export class ProgrammableVoiceProfiles {
40
+ constructor(client, enableAdmin) {
41
+ this.client = client;
42
+ this.enableAdmin = enableAdmin;
43
+ }
44
+
45
+ /**
46
+ * List all Programmable Voice Profiles for the current account.
47
+ * @async
48
+ * @roles api, admin, developer, billing, user
49
+ * @param {Object} [params] - Request parameters.
50
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
51
+ * @returns {Promise<Object>} `{ profiles: [...] }`.
52
+ */
53
+ async list({ options = {} } = {}) {
54
+ return this.client(`/voice/api/v1/programmable-voice-profiles`, { method: "GET", ...options });
55
+ }
56
+
57
+ /**
58
+ * Get a single Programmable Voice Profile by ID.
59
+ * @async
60
+ * @roles api, admin, developer, billing, user
61
+ * @param {Object} params - Request parameters.
62
+ * @param {string} params.id - The profile UUID.
63
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
64
+ * @throws {Error} If id is missing.
65
+ * @returns {Promise<Object>} `{ profile: {...} }`.
66
+ */
67
+ async get({ id, options = {} }) {
68
+ this.client._require({ id });
69
+ const safeId = encodeURIComponent(id);
70
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}`, { method: "GET", ...options });
71
+ }
72
+
73
+ /**
74
+ * Create a Programmable Voice Profile.
75
+ * @async
76
+ * @roles api, admin, developer
77
+ * @param {Object} params - Request parameters.
78
+ * @param {CreateProgrammableVoiceProfileData} params.profileData - The profile to create.
79
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
80
+ * @throws {Error} If profileData is missing.
81
+ * @returns {Promise<Object>} `{ profile: {...} }`.
82
+ */
83
+ async create({ profileData, options = {} }) {
84
+ this.client._require({ profileData });
85
+ return this.client(`/voice/api/v1/programmable-voice-profiles`, { method: "POST", body: profileData, ...options });
86
+ }
87
+
88
+ /**
89
+ * Update an existing Programmable Voice Profile (partial).
90
+ * @async
91
+ * @roles api, admin, developer
92
+ * @param {Object} params - Request parameters.
93
+ * @param {string} params.id - The profile UUID.
94
+ * @param {UpdateProgrammableVoiceProfileData} params.updateData - Fields to update.
95
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
96
+ * @throws {Error} If id or updateData is missing.
97
+ * @returns {Promise<Object>} `{ profile: {...} }`.
98
+ */
99
+ async update({ id, updateData, options = {} }) {
100
+ this.client._require({ id, updateData });
101
+ const safeId = encodeURIComponent(id);
102
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}`, { method: "PATCH", body: updateData, ...options });
103
+ }
104
+
105
+ /**
106
+ * Toggle a profile active/inactive.
107
+ * @async
108
+ * @roles api, admin, developer
109
+ * @param {Object} params - Request parameters.
110
+ * @param {string} params.id - The profile UUID.
111
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
112
+ * @throws {Error} If id is missing.
113
+ * @returns {Promise<Object>} `{ profile: {...} }`.
114
+ */
115
+ async toggleActive({ id, options = {} }) {
116
+ this.client._require({ id });
117
+ const safeId = encodeURIComponent(id);
118
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}/toggle-active`, { method: "POST", ...options });
119
+ }
120
+
121
+ /**
122
+ * Delete a Programmable Voice Profile. Its number assignments cascade away,
123
+ * so those numbers fall back to subgroup/global routing.
124
+ * @async
125
+ * @roles api, admin, developer
126
+ * @param {Object} params - Request parameters.
127
+ * @param {string} params.id - The profile UUID.
128
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
129
+ * @throws {Error} If id is missing.
130
+ * @returns {Promise<Object>} `{ message: string }`.
131
+ */
132
+ async delete({ id, options = {} }) {
133
+ this.client._require({ id });
134
+ const safeId = encodeURIComponent(id);
135
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}`, { method: "DELETE", ...options });
136
+ }
137
+
138
+ /**
139
+ * Assign a phone number (by E.164) to this profile. Rejected if the number is
140
+ * already configured on a SIP trunk/endpoint or another profile — it must be
141
+ * de-configured there first (mutual exclusivity).
142
+ * @async
143
+ * @roles api, admin, developer
144
+ * @param {Object} params - Request parameters.
145
+ * @param {string} params.id - The profile UUID.
146
+ * @param {string} params.e164 - The phone number in E.164 (a leading "+" is optional).
147
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
148
+ * @throws {Error} If id or e164 is missing.
149
+ * @returns {Promise<Object>} `{ profile: {...} }`.
150
+ */
151
+ async assignNumber({ id, e164, options = {} }) {
152
+ this.client._require({ id, e164 });
153
+ const safeId = encodeURIComponent(id);
154
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}/assign-number`, {
155
+ method: "POST",
156
+ body: { e164 },
157
+ ...options,
158
+ });
159
+ }
160
+
161
+ /**
162
+ * Unassign a phone number (by E.164) from this profile.
163
+ * @async
164
+ * @roles api, admin, developer
165
+ * @param {Object} params - Request parameters.
166
+ * @param {string} params.id - The profile UUID.
167
+ * @param {string} params.e164 - The phone number in E.164 (a leading "+" is optional).
168
+ * @param {import('../../SignalHouseSDK').RequestOptions} [params.options] - Additional options for the request.
169
+ * @throws {Error} If id or e164 is missing.
170
+ * @returns {Promise<Object>} `{ profile: {...} }`.
171
+ */
172
+ async unassignNumber({ id, e164, options = {} }) {
173
+ this.client._require({ id, e164 });
174
+ const safeId = encodeURIComponent(id);
175
+ return this.client(`/voice/api/v1/programmable-voice-profiles/${safeId}/unassign-number`, {
176
+ method: "POST",
177
+ body: { e164 },
178
+ ...options,
179
+ });
180
+ }
181
+ }