@signalhousellc/sdk 1.4.2 → 1.5.0
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 +1 -1
- package/src/SignalHouseSDK.js +2 -0
- package/src/domains/Brands.js +59 -1
- package/src/domains/Campaigns.js +4 -4
- package/src/domains/Messages.js +4 -2
- package/src/domains/Numbers.js +66 -5
- package/src/domains/Onboarding.js +18 -0
- package/src/domains/Subgroups.js +2 -0
- package/src/domains/Templates.js +403 -0
- package/src/domains/Voice.js +3 -0
- package/src/domains/Webhooks.js +7 -1
- package/src/domains/voice/NumberRoutes.js +82 -0
package/package.json
CHANGED
package/src/SignalHouseSDK.js
CHANGED
|
@@ -32,6 +32,7 @@ import { Campaigns } from "./domains/Campaigns.js";
|
|
|
32
32
|
import { Groups } from "./domains/Groups.js";
|
|
33
33
|
import { Landings } from "./domains/Landings.js";
|
|
34
34
|
import { Messages } from "./domains/Messages.js";
|
|
35
|
+
import { Templates } from "./domains/Templates.js";
|
|
35
36
|
import { Numbers } from "./domains/Numbers.js";
|
|
36
37
|
import { Registrations } from "./domains/Registrations.js";
|
|
37
38
|
import { Onboarding } from "./domains/Onboarding.js";
|
|
@@ -78,6 +79,7 @@ export class SignalHouseSDK {
|
|
|
78
79
|
this.groups = new Groups(client, this.enableAdmin);
|
|
79
80
|
this.landings = new Landings(client, multipartClient, this.enableAdmin);
|
|
80
81
|
this.messages = new Messages(client, multipartClient, this.enableAdmin);
|
|
82
|
+
this.templates = new Templates(client, multipartClient, this.enableAdmin);
|
|
81
83
|
this.numbers = new Numbers(client, multipartClient, this.enableAdmin);
|
|
82
84
|
this.registrations = new Registrations(client, this.enableAdmin);
|
|
83
85
|
this.onboarding = new Onboarding(client, this.enableAdmin);
|
package/src/domains/Brands.js
CHANGED
|
@@ -80,6 +80,29 @@
|
|
|
80
80
|
* @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)
|
|
81
81
|
*/
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* @typedef {Object} BusinessVerificationDetails
|
|
85
|
+
* @property {string} businessName - The legal business name (required, at most 255 characters)
|
|
86
|
+
* @property {string} businessType - The business type (required, one of CO_OPERATIVE, CORPORATION, LIMITED_LIABILITY_CORPORATION, NON_PROFIT_CORPORATION, PARTNERSHIP)
|
|
87
|
+
* @property {string} industry - The business industry (required, one of AGRICULTURE, AUTOMOTIVE, BANKING, CONSTRUCTION, CONSUMER, EDUCATION, ELECTRONICS, ENGINEERING, ENERGY, FAST_MOVING_CONSUMER_GOODS, FINANCIAL, FINTECH, FOOD_AND_BEVERAGE, GOVERNMENT, HEALTHCARE, HOSPITALITY, INSURANCE, JEWELRY, LEGAL, MANUFACTURING, MEDIA, NOT_FOR_PROFIT, OIL_AND_GAS, ONLINE, PROFESSIONAL_SERVICES, RAW_MATERIALS, REAL_ESTATE, RELIGION, RETAIL, TECHNOLOGY, TELECOMMUNICATIONS, TRANSPORTATION, TRAVEL)
|
|
88
|
+
* @property {string} registrationType - The business registration type (required, one of EIN, CBN, NEQ, PROVINCIAL_NUMBER, CRN, VAT, ACN, ABN, BRN, SIREN, SIRET, NZBN, UST_IDNR, CIF, NIF, CNPJ, UID, OTHER)
|
|
89
|
+
* @property {string} registrationNumber - The business registration number, e.g. the EIN (required, at most 50 characters)
|
|
90
|
+
* @property {string} website - The business website (required, valid URL, at most 255 characters)
|
|
91
|
+
* @property {string} street - The business street address (required, at most 255 characters)
|
|
92
|
+
* @property {string} [street2] - The second line of the street address (optional, at most 255 characters)
|
|
93
|
+
* @property {string} city - The business city (required, at most 100 characters)
|
|
94
|
+
* @property {string} state - The business state or province (required, 2-letter code)
|
|
95
|
+
* @property {string} postalCode - The business postal code (required, at most 10 characters)
|
|
96
|
+
* @property {string} country - The business country (required, "US" or "CA")
|
|
97
|
+
* @property {Object} representative - The business's authorized representative (required)
|
|
98
|
+
* @property {string} representative.firstName - The representative's first name (required, at most 100 characters)
|
|
99
|
+
* @property {string} representative.lastName - The representative's last name (required, at most 100 characters)
|
|
100
|
+
* @property {string} representative.email - The representative's email address (required, valid email, at most 100 characters)
|
|
101
|
+
* @property {string} representative.phone - The representative's phone number (required, E.164 with an optional leading "+" and no spaces, parentheses or hyphens, e.g. "+17025550100")
|
|
102
|
+
* @property {string} representative.jobTitle - The representative's job title (required, at most 100 characters)
|
|
103
|
+
* @property {string} representative.jobLevel - The representative's job level (required, one of DIRECTOR, GM, VP, CEO, CFO, GENERAL_COUNSEL, OTHER)
|
|
104
|
+
*/
|
|
105
|
+
|
|
83
106
|
/**
|
|
84
107
|
* @typedef {Object} CreateTollFreeBrandData
|
|
85
108
|
* @property {string} subgroupId - The ID of the subgroup to create the brand under (required, exactly 8 characters)
|
|
@@ -140,6 +163,7 @@ export class Brands {
|
|
|
140
163
|
* @param {number} [params.limit] - The number of items per page
|
|
141
164
|
* @param {string} [params.status] - The status of the brand to filter by (PENDING_CREATION, PENDING_APPROVAL, UNVERIFIED, VERIFIED, VETTED_VERIFIED, PENDING_DELETE, DELETED)
|
|
142
165
|
* @param {string} [params.registrationType] - Filter by registration type ("TEN_DLC", "TOLL_FREE", or "SHORT_CODE")
|
|
166
|
+
* @param {boolean} [params.currentTollFreeCarrier] - With subgroupId or groupId: when true, return only toll-free brands that a new toll-free campaign in that group can use (those on the group's current toll-free carrier)
|
|
143
167
|
* @param {string} [params.search] - Staff-only (no id/subgroupId/groupId): case-insensitive substring over brandId, names, EIN, email, group, subgroup and reference ID
|
|
144
168
|
* @param {string} [params.sortBy] - Staff-only: createdAt, brandId, displayName, groupId, subgroupId, status, entityType or country
|
|
145
169
|
* @param {"asc"|"desc"} [params.sortOrder] - Staff-only: sort direction (default asc)
|
|
@@ -153,7 +177,7 @@ export class Brands {
|
|
|
153
177
|
* @param {boolean} [params.excludeDeleted] - Staff-only: hide DELETED brands when no status is given
|
|
154
178
|
* @param {boolean} [params.includeTotal] - Staff-only: with page and limit, return `{ data, total, page, limit, totalPages }` instead of the array
|
|
155
179
|
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
156
|
-
* @returns {Promise<SdkResponse<Array<{ _id: string, brandId: string|null, status: string, registrationType: string, [key: string]: any }>|{ data: Array<Object>, total: number, page: number, limit: number, totalPages: number }>>} 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. The staff-only list (no id/subgroupId/groupId) returns the `{ data, total, page, limit, totalPages }` envelope only when includeTotal is sent with page and limit.
|
|
180
|
+
* @returns {Promise<SdkResponse<Array<{ _id: string, brandId: string|null, status: string, registrationType: string, tollFree?: { isBrandReady?: boolean, maxNumbersPerCampaign?: number, maxOptInMethods?: number, requiresPrivacyPolicy?: boolean, requiresTermsAndConditions?: boolean, maxAppealReasonLength?: number, labelsSamples?: boolean, supportsMms?: boolean, usesBusinessVerification?: boolean, [key: string]: any }, [key: string]: any }>|{ data: Array<Object>, total: number, page: number, limit: number, totalPages: number }>>} 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. Toll-free brands carry `tollFree.isBrandReady` (whether the brand can host campaigns), `tollFree.maxNumbersPerCampaign` (the most numbers one of its campaigns can hold), `tollFree.maxOptInMethods` (the most opt-in methods one of its campaigns can declare), `tollFree.requiresPrivacyPolicy` (whether its campaigns need a privacy policy URL), `tollFree.requiresTermsAndConditions` (whether its campaigns need a terms and conditions URL), `tollFree.maxAppealReasonLength` (the longest appeal reason its campaigns may give), `tollFree.labelsSamples` (whether its campaigns' sample messages each start with their use case's name), `tollFree.supportsMms` (whether its numbers can send MMS and group messages) and `tollFree.usesBusinessVerification` (whether it waits on the group's business verification). The staff-only list (no id/subgroupId/groupId) returns the `{ data, total, page, limit, totalPages }` envelope only when includeTotal is sent with page and limit.
|
|
157
181
|
*/
|
|
158
182
|
async getBrands({
|
|
159
183
|
id,
|
|
@@ -163,6 +187,7 @@ export class Brands {
|
|
|
163
187
|
limit,
|
|
164
188
|
status,
|
|
165
189
|
registrationType,
|
|
190
|
+
currentTollFreeCarrier,
|
|
166
191
|
search,
|
|
167
192
|
sortBy,
|
|
168
193
|
sortOrder,
|
|
@@ -185,6 +210,7 @@ export class Brands {
|
|
|
185
210
|
limit,
|
|
186
211
|
status,
|
|
187
212
|
registrationType,
|
|
213
|
+
currentTollFreeCarrier,
|
|
188
214
|
search,
|
|
189
215
|
sortBy,
|
|
190
216
|
sortOrder,
|
|
@@ -248,6 +274,38 @@ export class Brands {
|
|
|
248
274
|
return this.client(`/brand/toll-free`, { method: 'POST', body: brandData, ...options });
|
|
249
275
|
}
|
|
250
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Get a group's business verification: whether its toll-free brands need one, its status and the submitted details.
|
|
279
|
+
* @async
|
|
280
|
+
* @roles api, admin, developer, billing, user
|
|
281
|
+
* @param {Object} params - The parameters for getting the business verification
|
|
282
|
+
* @param {string} params.groupId - The ID of the group whose business verification to get
|
|
283
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
284
|
+
* @throws {Error} Throws an error if the groupId parameter is missing
|
|
285
|
+
* @returns {Promise<SdkResponse<{ groupId: string, required: boolean, status: string, details: BusinessVerificationDetails|null, submittedAt: string|null, reviewedAt: string|null, rejectionReason: string|null }>>} The group's business verification. `required` says whether the group's new toll-free brands need one; `status` is NOT_STARTED, SUBMITTING, IN_REVIEW, VERIFIED or NEEDS_CHANGES; `rejectionReason` says why it needs changes, or is null. When `required` is true and `status` is NOT_STARTED, submit it before creating the group's first toll-free brand.
|
|
286
|
+
*/
|
|
287
|
+
async getBusinessVerification({ groupId, options = {} }) {
|
|
288
|
+
this.client._require({ groupId });
|
|
289
|
+
return this.client(`/brand/business-verification/${encodeURIComponent(groupId)}`, { method: 'GET', ...options });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Submit or update a group's business verification. Allowed when it is not started or needs changes. Review usually
|
|
294
|
+
* takes 1–2 business days; the group's toll-free brands stay pending until it is verified.
|
|
295
|
+
* @async
|
|
296
|
+
* @roles api, admin, developer, billing, user
|
|
297
|
+
* @param {Object} params - The parameters for submitting the business verification
|
|
298
|
+
* @param {string} params.groupId - The ID of the group whose business verification to submit (must be the caller's active group)
|
|
299
|
+
* @param {BusinessVerificationDetails} params.details - The business verification details (see BusinessVerificationDetails typedef for details)
|
|
300
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
301
|
+
* @throws {Error} Throws an error if the groupId or details parameter is missing
|
|
302
|
+
* @returns {Promise<SdkResponse<{ groupId: string, required: boolean, status: string, details: BusinessVerificationDetails|null, submittedAt: string|null, reviewedAt: string|null, rejectionReason: string|null }>>} A 202 with the group's business verification, now SUBMITTING (the same shape as getBusinessVerification).
|
|
303
|
+
*/
|
|
304
|
+
async submitBusinessVerification({ groupId, details, options = {} }) {
|
|
305
|
+
this.client._require({ groupId, details });
|
|
306
|
+
return this.client(`/brand/business-verification/${encodeURIComponent(groupId)}`, { method: 'PUT', body: details, ...options });
|
|
307
|
+
}
|
|
308
|
+
|
|
251
309
|
/**
|
|
252
310
|
* Create a local Short Code brand for manual Signal House review.
|
|
253
311
|
* @async
|
package/src/domains/Campaigns.js
CHANGED
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
* @property {TollFreeOptIn} [optIns.keyword] - Keyword opt-in details
|
|
97
97
|
* @property {TollFreeOptIn} [optIns.interactiveVoiceResponse] - IVR opt-in details
|
|
98
98
|
* @property {string} [multiNumberReason] - Internally-stored reason the customer requested more than one Toll-Free number on the campaign (optional, at most 500 characters). Required when more than one phone number is assigned; captured for Signal House review only and never sent to the carrier.
|
|
99
|
-
* @property {string[]} [useCases] - The sender's real use-case picks (optional). `useCase` above carries the single value the carrier accepts (MIXED when there is more than one); these are what a Signal House landing page renders a consent checkbox per.
|
|
99
|
+
* @property {string[]} [useCases] - The sender's real use-case picks (optional). `useCase` above carries the single value the carrier accepts (MIXED when there is more than one); these are what a Signal House landing page renders a consent checkbox per. Carriers that need specific use cases rather than MIXED take them from here when present; other carriers never receive them.
|
|
100
100
|
* @property {string[]} [channels] - The channels consent covers ("SMS", "MMS", "VOICE") (optional). Never forwarded to the carrier.
|
|
101
101
|
* @property {string} [landingId] - The Signal House landing page whose consent boxes this campaign's call-to-action describes (optional). Must be an ACTIVE page owned by the same brand and group.
|
|
102
102
|
*/
|
|
@@ -105,11 +105,11 @@
|
|
|
105
105
|
* @typedef {Object} CreateTollFreeCampaignData
|
|
106
106
|
* @property {string} brandId - The ID of the Toll-Free brand associated with the campaign (required, at least 7 characters)
|
|
107
107
|
* @property {TollFreeCampaignDetails} tollFree - The Toll-Free campaign details (required)
|
|
108
|
-
* @property {string} [privacyPolicyLink] - A URL linking to the privacy policy (
|
|
109
|
-
* @property {string} [termsAndConditionsLink] - A URL linking to the terms and conditions (
|
|
108
|
+
* @property {string} [privacyPolicyLink] - A URL linking to the privacy policy (at most 2048 characters). Required when the brand's `tollFree.requiresPrivacyPolicy` is true and the brand has no `privacyPolicyLink` of its own; optional otherwise.
|
|
109
|
+
* @property {string} [termsAndConditionsLink] - A URL linking to the terms and conditions (at most 2048 characters). Required when the brand's `tollFree.requiresTermsAndConditions` is true and the brand has no `termsAndConditionsLink` of its own; optional otherwise.
|
|
110
110
|
* @property {string} [tag] - A tag for the campaign (optional, at most 255 characters)
|
|
111
111
|
* @property {boolean} [autoRenewal=true] - Whether the campaign should auto-renew (optional, defaults to true)
|
|
112
|
-
* @property {Array<string>} phoneNumbers - The Toll-Free numbers to assign to the campaign (required,
|
|
112
|
+
* @property {Array<string>} phoneNumbers - The Toll-Free numbers to assign to the campaign (required, 1 up to the brand's tollFree.maxNumbersPerCampaign and at most 5, each at least 10 digits and digits only). The numbers must be on the brand's toll-free carrier, or the campaign is refused. Numbers are locked to the campaign once assigned and cannot be moved or added afterward.
|
|
113
113
|
*/
|
|
114
114
|
|
|
115
115
|
/**
|
package/src/domains/Messages.js
CHANGED
|
@@ -108,8 +108,10 @@ export class Messages {
|
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* Fetch the latest upstream carrier delivery report for a set of messages by ID (SHGHL-1856).
|
|
111
|
-
* Useful for reconciling messages stuck in a non-terminal state. SMS and MMS messages
|
|
112
|
-
*
|
|
111
|
+
* Useful for reconciling messages stuck in a non-terminal state. Outbound SMS and MMS messages sent from numbers on
|
|
112
|
+
* the carrier whose delivery logs we read are looked up there (the logs are kept about 48 hours); every other message,
|
|
113
|
+
* including P2P messages and messages sent from numbers on other carriers, is returned with its stored status and
|
|
114
|
+
* upstreamReportAvailable false. The request fails with a 500 when the sending numbers can't be read.
|
|
113
115
|
* @async
|
|
114
116
|
* @roles api, admin, developer, billing, user
|
|
115
117
|
* @param {Object} params - The parameters for fetching delivery reports
|
package/src/domains/Numbers.js
CHANGED
|
@@ -81,6 +81,7 @@ export class Numbers {
|
|
|
81
81
|
* @param {string} [params.brandId] - Filter by brand ID
|
|
82
82
|
* @param {string} [params.subgroupId] - Filter by subgroup ID
|
|
83
83
|
* @param {string} [params.groupId] - Filter by group ID
|
|
84
|
+
* @param {boolean} [params.currentTollFreeCarrier] - With subgroupId or groupId: when true, return only toll-free numbers that a new toll-free campaign in that group can use (those on the group's current toll-free carrier)
|
|
84
85
|
* @param {number} [params.page] - The page number for pagination (default: 1)
|
|
85
86
|
* @param {number} [params.limit] - The number of items per page for pagination (default: 20)
|
|
86
87
|
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
@@ -103,8 +104,8 @@ export class Numbers {
|
|
|
103
104
|
* or GET /campaign/health.
|
|
104
105
|
* @returns {Promise<SdkResponse<any>>} A promise that resolves to the list of phone numbers
|
|
105
106
|
*/
|
|
106
|
-
async getPhoneNumbers({ phoneNumber, campaignId, registrationId, brandId, subgroupId, groupId, page, limit, options = {} }) {
|
|
107
|
-
const filters = { phoneNumber, campaignId, registrationId, brandId, subgroupId, groupId, page, limit };
|
|
107
|
+
async getPhoneNumbers({ phoneNumber, campaignId, registrationId, brandId, subgroupId, groupId, currentTollFreeCarrier, page, limit, options = {} }) {
|
|
108
|
+
const filters = { phoneNumber, campaignId, registrationId, brandId, subgroupId, groupId, currentTollFreeCarrier, page, limit };
|
|
108
109
|
const queryString = this.client._getQueryString(filters);
|
|
109
110
|
return this.client(`/number${queryString}`, { method: 'GET', ...options });
|
|
110
111
|
}
|
|
@@ -261,14 +262,18 @@ export class Numbers {
|
|
|
261
262
|
* @roles api, admin, developer, billing, user
|
|
262
263
|
* @param {Object} params - The parameters for reading the order status
|
|
263
264
|
* @param {string} params.orderId - The order id returned by `purchaseTollFreeNumbers`
|
|
265
|
+
* @param {string} [params.groupId] - Staff and service callers only: the order's group, whose carrier the read follows.
|
|
266
|
+
* Their tokens carry no active group; every other caller's order is read by its active group, so a well-formed
|
|
267
|
+
* groupId is ignored and a malformed one gets a 400.
|
|
264
268
|
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
265
269
|
* @throws {Error} Throws an error if the orderId parameter is missing
|
|
266
270
|
* @returns {Promise<SdkResponse<any>>} A promise that resolves to `{ orderId, counts: { ready, provisioning, failed }, failures: [{ reason }], numbers: string[] }`.
|
|
267
271
|
*/
|
|
268
|
-
async getTollFreeOrderStatus({ orderId, options = {} }) {
|
|
272
|
+
async getTollFreeOrderStatus({ orderId, groupId, options = {} }) {
|
|
269
273
|
this.client._require({ orderId });
|
|
270
274
|
const safeOrderId = encodeURIComponent(orderId);
|
|
271
|
-
|
|
275
|
+
const queryString = this.client._getQueryString({ groupId });
|
|
276
|
+
return this.client(`/number/toll-free/order/${safeOrderId}${queryString}`, { method: 'GET', ...options });
|
|
272
277
|
}
|
|
273
278
|
|
|
274
279
|
/**
|
|
@@ -298,6 +303,60 @@ export class Numbers {
|
|
|
298
303
|
});
|
|
299
304
|
}
|
|
300
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Get a phone number's outbound caller name (CNAM) - the name US carriers show for calls from it
|
|
308
|
+
* @async
|
|
309
|
+
* @roles api, admin, developer, billing, user
|
|
310
|
+
* @param {Object} params - The parameters for the request
|
|
311
|
+
* @param {string} params.phoneNumber - The phone number (E.164, leading + optional)
|
|
312
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
313
|
+
* @throws {Error} Throws an error if the phoneNumber parameter is missing
|
|
314
|
+
* @returns {Promise<SdkResponse<any>>} A promise that resolves to `{ phoneNumber, cnam, autoApply, eligible, ineligibleReason }`
|
|
315
|
+
*/
|
|
316
|
+
async getCallerName({ phoneNumber, options = {} }) {
|
|
317
|
+
this.client._require({ phoneNumber });
|
|
318
|
+
const safePhoneNumber = encodeURIComponent(phoneNumber);
|
|
319
|
+
return this.client(`/number/${safePhoneNumber}/cnam`, { method: 'GET', ...options });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Set a phone number's outbound caller name (CNAM). The name may only remove characters from the
|
|
324
|
+
* registered name of the number's brand (its campaign's brand, else `brandId`, else the subgroup's
|
|
325
|
+
* default caller-ID brand), and must be 1-15 letters, digits, spaces, `-` or `_`.
|
|
326
|
+
* @async
|
|
327
|
+
* @roles api, admin, developer
|
|
328
|
+
* @param {Object} params - The parameters for the request
|
|
329
|
+
* @param {string} params.phoneNumber - The phone number (E.164, leading + optional)
|
|
330
|
+
* @param {string} params.name - The caller name to show
|
|
331
|
+
* @param {string} [params.brandId] - The verified brand the name comes from; only for numbers with no campaign
|
|
332
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
333
|
+
* @throws {Error} Throws an error if the phoneNumber or name parameter is missing
|
|
334
|
+
* @returns {Promise<SdkResponse<any>>} A promise that resolves to `{ phoneNumber, cnam, autoApply, eligible, ineligibleReason }`
|
|
335
|
+
*/
|
|
336
|
+
async setCallerName({ phoneNumber, name, brandId, options = {} }) {
|
|
337
|
+
this.client._require({ phoneNumber, name });
|
|
338
|
+
const safePhoneNumber = encodeURIComponent(phoneNumber);
|
|
339
|
+
const body = brandId ? { name, brandId } : { name };
|
|
340
|
+
return this.client(`/number/${safePhoneNumber}/cnam`, { method: 'PUT', body, ...options });
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Remove a phone number's own caller name (CNAM). If its subgroup has caller name auto-apply on, the
|
|
345
|
+
* number goes back to the automatic name.
|
|
346
|
+
* @async
|
|
347
|
+
* @roles api, admin, developer
|
|
348
|
+
* @param {Object} params - The parameters for the request
|
|
349
|
+
* @param {string} params.phoneNumber - The phone number (E.164, leading + optional)
|
|
350
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
351
|
+
* @throws {Error} Throws an error if the phoneNumber parameter is missing
|
|
352
|
+
* @returns {Promise<SdkResponse<any>>} A promise that resolves to `{ phoneNumber, cnam, autoApply, eligible, ineligibleReason }`
|
|
353
|
+
*/
|
|
354
|
+
async deleteCallerName({ phoneNumber, options = {} }) {
|
|
355
|
+
this.client._require({ phoneNumber });
|
|
356
|
+
const safePhoneNumber = encodeURIComponent(phoneNumber);
|
|
357
|
+
return this.client(`/number/${safePhoneNumber}/cnam`, { method: 'DELETE', ...options });
|
|
358
|
+
}
|
|
359
|
+
|
|
301
360
|
/**
|
|
302
361
|
* Submit a port-in request for one or more phone numbers from another provider
|
|
303
362
|
* @async
|
|
@@ -371,7 +430,9 @@ export class Numbers {
|
|
|
371
430
|
* Numbers that are not found, already RELEASED, currently being released (transient PENDING_RELEASE
|
|
372
431
|
* status), already assigned to a different campaign, on a mismatched DCA, or (for a 10DLC campaign
|
|
373
432
|
* without a number pool) beyond the remaining 49-number-per-campaign limit are not queued - each is
|
|
374
|
-
* reported back individually in `skipped` instead of failing the whole request.
|
|
433
|
+
* reported back individually in `skipped` instead of failing the whole request. A Toll-Free campaign
|
|
434
|
+
* whose brand reports `tollFree.maxNumbersPerCampaign` as 1 holds one number: a request that would give
|
|
435
|
+
* it a second is refused with a 400 and nothing is queued.
|
|
375
436
|
* @async
|
|
376
437
|
* @roles api, admin, developer, billing, user
|
|
377
438
|
* @param {Object} params - The parameters for assigning phone numbers to a campaign
|
|
@@ -40,6 +40,24 @@ export class Onboarding {
|
|
|
40
40
|
return this.client(`/group/onboarding/${safeGroupId}`, { method: "GET", ...options });
|
|
41
41
|
},
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Set where a group's new toll-free brands and numbers go (`infobip` or `twilio`). Staff-only. Existing brands,
|
|
45
|
+
* numbers and campaigns stay where they are.
|
|
46
|
+
* @async
|
|
47
|
+
* @roles signalhouse
|
|
48
|
+
* @param {Object} params
|
|
49
|
+
* @param {string} params.groupId - The group ID whose toll-free provider to set
|
|
50
|
+
* @param {string} params.tollFreeProvider - The toll-free provider for the group's new toll-free brands and numbers ("infobip" or "twilio")
|
|
51
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
52
|
+
* @throws {Error} Throws an error if the groupId or tollFreeProvider parameter is missing
|
|
53
|
+
* @returns {Promise<SdkResponse<any>>} - The updated onboarding record
|
|
54
|
+
*/
|
|
55
|
+
setTollFreeProvider: async ({ groupId, tollFreeProvider, options = {} }) => {
|
|
56
|
+
this.client._require({ groupId, tollFreeProvider });
|
|
57
|
+
const safeGroupId = encodeURIComponent(groupId);
|
|
58
|
+
return this.client(`/group/onboarding/${safeGroupId}/toll-free-provider`, { method: "PUT", body: { tollFreeProvider }, ...options });
|
|
59
|
+
},
|
|
60
|
+
|
|
43
61
|
/**
|
|
44
62
|
* Add an internal comment/note to a group's admin Pricing editor. Staff-only.
|
|
45
63
|
* @async
|
package/src/domains/Subgroups.js
CHANGED
|
@@ -40,6 +40,8 @@
|
|
|
40
40
|
* @property {string} [status] - The status of the subgroup (e.g., "active", "inactive")
|
|
41
41
|
* @property {string} [carrierIdFamily] - Optional agency-managed carrier ID selection ("Default", "ATT", "TMobile", "Verizon", "USCellular", "GoogleVoice", "ClearSky", "Interop"). Setting this field requires signalhouse_api, signalhouse_admin, signalhouse_user, api, admin, developer, or billing role.
|
|
42
42
|
* @property {string|null} [carrierIdRegion] - Optional ClearSky region ("PRClaro", "GuamDocomo", "GuamGTA", "GuamITE") or null; only valid when carrierIdFamily is "ClearSky". Setting this field requires signalhouse_api, signalhouse_admin, signalhouse_user, api, admin, developer, or billing role.
|
|
43
|
+
* @property {boolean} [cnamAutoApply] - Turn caller name (CNAM) auto-apply on or off: eligible numbers get their brand's registered name as the name shown on outbound calls. Requires signalhouse_api, signalhouse_admin, signalhouse_user, api, admin or developer role.
|
|
44
|
+
* @property {string|null} [cnamBrandId] - Default caller-ID brand for numbers with no campaign (e.g. voice-only). Must be a verified brand in this subgroup; required when turning auto-apply on in a subgroup with more than one verified brand.
|
|
43
45
|
*/
|
|
44
46
|
|
|
45
47
|
export class Subgroups {
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/** @typedef {import('../SignalHouseSDK.js').RequestOptions} RequestOptions */
|
|
2
|
+
/**
|
|
3
|
+
* @template T
|
|
4
|
+
* @typedef {import('../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A version selector: a positive version number, "draft" or "published". Omitted means the published
|
|
9
|
+
* version when one exists, otherwise the draft; an explicit selector never falls back (404 when missing).
|
|
10
|
+
* @typedef {number|"draft"|"published"} VersionSelector
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} RcsVariable
|
|
15
|
+
* @property {string} key - Placeholder key used as {{key}} (/^[a-z][a-z0-9_]{0,63}$/)
|
|
16
|
+
* @property {string} [label] - Display label
|
|
17
|
+
* @property {"text"|"number"|"dateTime"|"phoneNumber"|"url"} type - Value type
|
|
18
|
+
* @property {boolean} required - Whether a send must supply a value
|
|
19
|
+
* @property {string} [default] - Value used when none is supplied
|
|
20
|
+
* @property {string} [sample] - Example value for previews
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The raw RCS template body. Unknown keys are rejected at every level; see the API docs for the block,
|
|
25
|
+
* suggestion and fallback shapes.
|
|
26
|
+
* @typedef {Object} RcsTemplateBody
|
|
27
|
+
* @property {"rcs"} channel - Always "rcs"
|
|
28
|
+
* @property {"rcs.blocks.v1"} contentFormat - Always "rcs.blocks.v1"
|
|
29
|
+
* @property {{ blocks: Object[] }} content - Ordered blocks of kind text, richCard, video, carousel, suggestions or attachment
|
|
30
|
+
* @property {RcsVariable[]} [variables] - Declared placeholders; omitted means []
|
|
31
|
+
* @property {{ trafficType?: string, ttlSeconds?: number }} [defaults] - Send defaults; omitted means {}
|
|
32
|
+
* @property {{ delivery?: { channel: "sms"|"mms", campaignId?: string, phoneNumber?: string, content: Object }, validitySeconds?: number }} [fallback] - SMS/MMS sent when RCS cannot be delivered; no delivery means no fallback
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @typedef {Object} TemplateHead
|
|
37
|
+
* @property {string} templateId - "TPL" + 10 characters
|
|
38
|
+
* @property {string} groupId - Owning group
|
|
39
|
+
* @property {"rcs"} channel - Template channel
|
|
40
|
+
* @property {string} name - 1-120 characters, unique per group and channel
|
|
41
|
+
* @property {string|null} tag - Tag (1-60 characters) or null
|
|
42
|
+
* @property {string|null} archivedAt - When the template was archived
|
|
43
|
+
* @property {number|null} draftVersion - The current draft version
|
|
44
|
+
* @property {number|null} publishedVersion - The current published version
|
|
45
|
+
* @property {number} revision - Head revision, incremented on every write; starts at 1
|
|
46
|
+
* @property {string} createdBy - Creating user
|
|
47
|
+
* @property {string} createdAt - ISO timestamp
|
|
48
|
+
* @property {string} updatedAt - ISO timestamp
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {TemplateHead & { state: "DRAFT"|"PUBLISHED"|"ARCHIVED", hasUnpublishedChanges: boolean }} TemplateSummary
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @typedef {Object} VersionSummary
|
|
57
|
+
* @property {string} templateId - The template ID
|
|
58
|
+
* @property {string} groupId - Owning group
|
|
59
|
+
* @property {number} version - Version number
|
|
60
|
+
* @property {"DRAFT"|"PUBLISHED"} state - Version state
|
|
61
|
+
* @property {string} contentHash - Hex SHA-256 of the canonical body JSON
|
|
62
|
+
* @property {string} createdBy - Creating user
|
|
63
|
+
* @property {string} createdAt - ISO timestamp
|
|
64
|
+
* @property {string} updatedAt - ISO timestamp
|
|
65
|
+
* @property {string|null} publishedAt - ISO timestamp
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @typedef {VersionSummary & { body: RcsTemplateBody }} TemplateVersion
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @typedef {Object} TemplateDetail
|
|
74
|
+
* @property {TemplateSummary} head - The template head
|
|
75
|
+
* @property {TemplateVersion} version - The selected version
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @template T
|
|
80
|
+
* @typedef {{ data: T[], total: number, page: number, limit: number }} Page
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @typedef {Object} ValidationIssue
|
|
85
|
+
* @property {"error"|"warning"} severity - Issue severity
|
|
86
|
+
* @property {string} path - JSON path of the offending field
|
|
87
|
+
* @property {string} code - Machine-readable code
|
|
88
|
+
* @property {string} message - Human-readable message
|
|
89
|
+
* @property {string} [blockId] - The block the issue belongs to
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @typedef {Object} ValidationResult
|
|
94
|
+
* @property {boolean} valid - Whether the body is publishable
|
|
95
|
+
* @property {string} contentHash - Hex SHA-256 of the canonical body JSON
|
|
96
|
+
* @property {string} validatorVersion - The validator that produced the result
|
|
97
|
+
* @property {ValidationIssue[]} errors - Publication errors
|
|
98
|
+
* @property {ValidationIssue[]} warnings - Non-blocking warnings
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @typedef {Object} MediaAsset
|
|
103
|
+
* @property {string} mediaId - "MED" + 10 characters
|
|
104
|
+
* @property {string} groupId - Owning group
|
|
105
|
+
* @property {string} url - Public HTTPS URL to reference from a template body
|
|
106
|
+
* @property {number} bytes - File size
|
|
107
|
+
* @property {string} mimeType - MIME type
|
|
108
|
+
* @property {"image"|"video"|"audio"|"file"} kind - Media kind
|
|
109
|
+
* @property {number|null} width - Pixel width, when known
|
|
110
|
+
* @property {number|null} height - Pixel height, when known
|
|
111
|
+
* @property {string} createdAt - ISO timestamp
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Server-defined channel capabilities; values come from the server and drive client-side editors.
|
|
116
|
+
* @typedef {Object} Capabilities
|
|
117
|
+
* @property {"rcs"} channel - The channel described
|
|
118
|
+
* @property {"rcs.blocks.v1"} contentFormat - The body format
|
|
119
|
+
* @property {string} validatorVersion - The validator version
|
|
120
|
+
* @property {string[]} blockKinds - Allowed block kinds
|
|
121
|
+
* @property {string[]} actionTypes - Allowed suggestion types
|
|
122
|
+
* @property {string[]} variableTypes - Allowed variable types
|
|
123
|
+
* @property {string[]} trafficTypes - Allowed traffic types
|
|
124
|
+
* @property {string[]} cardWidths - Allowed carousel card widths
|
|
125
|
+
* @property {string[]} mediaHeights - Allowed card media heights
|
|
126
|
+
* @property {Object<string, number>} limits - Length and count limits (maxBlocks, maxTextLength, maxCarouselCards, ...)
|
|
127
|
+
* @property {{ maxUploadBytes: number, mimeTypes: string[] }} media - Upload constraints
|
|
128
|
+
* @property {{ provider: "infobip"|"sinch", adapterVersion: string, limits: Object<string, number>, unsupported: Array<{ feature: string, code: string, message: string }> }|null} provider - Provider-specific limits when a provider was requested
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Build the client-side validation error the SDK throws for a malformed request.
|
|
133
|
+
* @param {string} message - The error message
|
|
134
|
+
* @returns {Error & { status?: number }} - An error named SignalHouseValidationError with status 400
|
|
135
|
+
*/
|
|
136
|
+
function validationError(message) {
|
|
137
|
+
const error = /** @type {Error & { status?: number }} */ (new Error(message));
|
|
138
|
+
error.name = "SignalHouseValidationError";
|
|
139
|
+
error.status = 400;
|
|
140
|
+
return error;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build the update request body; a null tag stays on the wire, an undefined one is dropped.
|
|
145
|
+
* @param {Object} params - The update fields
|
|
146
|
+
* @param {string} params.groupId - The owning group
|
|
147
|
+
* @param {string} [params.name] - New name
|
|
148
|
+
* @param {string|null} [params.tag] - New tag; null clears it, undefined leaves it unchanged
|
|
149
|
+
* @param {RcsTemplateBody} [params.body] - Full replacement body
|
|
150
|
+
* @returns {Object} - The request body
|
|
151
|
+
*/
|
|
152
|
+
export function templateUpdateBody({ groupId, name, tag, body }) {
|
|
153
|
+
return { groupId, name, tag, body };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Build the validate request body from exactly one of body or version.
|
|
158
|
+
* @param {Object} params - The validate fields
|
|
159
|
+
* @param {string} params.groupId - The owning group
|
|
160
|
+
* @param {RcsTemplateBody} [params.body] - An unsaved body to validate
|
|
161
|
+
* @param {VersionSelector} [params.version] - A stored version to validate
|
|
162
|
+
* @throws {Error} Throws a SignalHouseValidationError unless exactly one of body or version is given
|
|
163
|
+
* @returns {Object} - The request body
|
|
164
|
+
*/
|
|
165
|
+
export function templateValidateBody({ groupId, body, version }) {
|
|
166
|
+
const hasBody = body !== undefined && body !== null;
|
|
167
|
+
const hasVersion = version !== undefined && version !== null;
|
|
168
|
+
if (hasBody === hasVersion) throw validationError("Pass exactly one of body or version");
|
|
169
|
+
return hasBody ? { groupId, body } : { groupId, version };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Build the multipart form for a media upload.
|
|
174
|
+
* @param {Object} params - The upload fields
|
|
175
|
+
* @param {string} params.groupId - The owning group
|
|
176
|
+
* @param {Blob|File} params.file - The file to upload
|
|
177
|
+
* @param {string} [params.fileName] - File name to send with the file
|
|
178
|
+
* @returns {FormData} - The form with file and groupId fields
|
|
179
|
+
*/
|
|
180
|
+
export function templateMediaForm({ groupId, file, fileName }) {
|
|
181
|
+
const formData = new FormData();
|
|
182
|
+
if (fileName) formData.append("file", file, fileName);
|
|
183
|
+
else formData.append("file", file);
|
|
184
|
+
formData.append("groupId", groupId);
|
|
185
|
+
return formData;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Channel-generic message templates under /template. Templates keep an immutable published version and
|
|
190
|
+
* at most one editable draft; writes apply directly (last write wins). 404 while the environment's RCS
|
|
191
|
+
* switch is off.
|
|
192
|
+
*/
|
|
193
|
+
export class Templates {
|
|
194
|
+
constructor(client, multipartClient, enableAdmin) {
|
|
195
|
+
this.client = client;
|
|
196
|
+
this.multipartClient = multipartClient;
|
|
197
|
+
this.enableAdmin = enableAdmin;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create a template with draft version 1
|
|
202
|
+
* @async
|
|
203
|
+
* @roles api, admin, developer
|
|
204
|
+
* @param {Object} params - The parameters for creating a template
|
|
205
|
+
* @param {string} params.groupId - The owning group
|
|
206
|
+
* @param {string} params.name - Template name (1-120 characters, unique per group and channel)
|
|
207
|
+
* @param {string} [params.tag] - Tag (1-60 characters)
|
|
208
|
+
* @param {RcsTemplateBody} params.body - The raw template body
|
|
209
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
210
|
+
* @returns {Promise<SdkResponse<TemplateDetail>>} A promise that resolves to the new template and its draft (201)
|
|
211
|
+
*/
|
|
212
|
+
async create({ groupId, name, tag, body, options = {} }) {
|
|
213
|
+
this.client._require({ groupId, name, body });
|
|
214
|
+
return this.client(`/template`, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
body: { groupId, name, tag, body },
|
|
217
|
+
...options,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* List a group's templates
|
|
223
|
+
* @async
|
|
224
|
+
* @roles api, admin, developer, billing, user
|
|
225
|
+
* @param {Object} params - The parameters for listing templates
|
|
226
|
+
* @param {string} params.groupId - The owning group
|
|
227
|
+
* @param {("DRAFT"|"PUBLISHED"|"ARCHIVED")} [params.state] - Filter by state
|
|
228
|
+
* @param {"rcs"} [params.channel] - Filter by channel
|
|
229
|
+
* @param {string} [params.tag] - Filter by tag
|
|
230
|
+
* @param {string} [params.search] - Search template names
|
|
231
|
+
* @param {("name"|"createdAt"|"updatedAt")} [params.sortBy] - Sort field
|
|
232
|
+
* @param {("asc"|"desc")} [params.sortOrder] - Sort direction
|
|
233
|
+
* @param {number} [params.page] - Page number (default 1)
|
|
234
|
+
* @param {number} [params.limit] - Templates per page (default 25, max 100)
|
|
235
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
236
|
+
* @returns {Promise<SdkResponse<Page<TemplateSummary>>>} A promise that resolves to { data, total, page, limit }
|
|
237
|
+
*/
|
|
238
|
+
async list({ groupId, state, channel, tag, search, sortBy, sortOrder, page, limit, options = {} }) {
|
|
239
|
+
this.client._require({ groupId });
|
|
240
|
+
const queryString = this.client._getQueryString({ groupId, state, channel, tag, search, sortBy, sortOrder, page, limit });
|
|
241
|
+
return this.client(`/template${queryString}`, { method: "GET", ...options });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Get a template with one of its versions
|
|
246
|
+
* @async
|
|
247
|
+
* @roles api, admin, developer, billing, user
|
|
248
|
+
* @param {Object} params - The parameters for getting a template
|
|
249
|
+
* @param {string} params.templateId - The template ID
|
|
250
|
+
* @param {string} params.groupId - The owning group
|
|
251
|
+
* @param {VersionSelector} [params.version] - Version to return; defaults to published, else draft
|
|
252
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
253
|
+
* @returns {Promise<SdkResponse<TemplateDetail>>} A promise that resolves to { head, version }
|
|
254
|
+
*/
|
|
255
|
+
async get({ templateId, groupId, version, options = {} }) {
|
|
256
|
+
this.client._require({ templateId, groupId });
|
|
257
|
+
const safeId = encodeURIComponent(templateId);
|
|
258
|
+
const queryString = this.client._getQueryString({ groupId, version });
|
|
259
|
+
return this.client(`/template/${safeId}${queryString}`, { method: "GET", ...options });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Update a template's name, tag or draft body; editing the body of a published template opens a new draft
|
|
264
|
+
* @async
|
|
265
|
+
* @roles api, admin, developer
|
|
266
|
+
* @param {Object} params - The parameters for updating a template
|
|
267
|
+
* @param {string} params.templateId - The template ID
|
|
268
|
+
* @param {string} params.groupId - The owning group
|
|
269
|
+
* @param {string} [params.name] - New name
|
|
270
|
+
* @param {string|null} [params.tag] - New tag; null clears it, omitted leaves it unchanged
|
|
271
|
+
* @param {RcsTemplateBody} [params.body] - Full replacement body
|
|
272
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
273
|
+
* @returns {Promise<SdkResponse<TemplateDetail>>} A promise that resolves to { head, version }
|
|
274
|
+
*/
|
|
275
|
+
async update({ templateId, groupId, name, tag, body, options = {} }) {
|
|
276
|
+
this.client._require({ templateId, groupId });
|
|
277
|
+
const safeId = encodeURIComponent(templateId);
|
|
278
|
+
return this.client(`/template/${safeId}`, {
|
|
279
|
+
method: "PUT",
|
|
280
|
+
body: templateUpdateBody({ groupId, name, tag, body }),
|
|
281
|
+
...options,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Publish a template's current draft; no draft returns 409 NO_DRAFT, a draft changed mid-publish returns 409 DRAFT_CHANGED (publish again) and an incomplete draft returns 400 TEMPLATE_INVALID
|
|
287
|
+
* @async
|
|
288
|
+
* @roles api, admin, developer
|
|
289
|
+
* @param {Object} params - The parameters for publishing a template
|
|
290
|
+
* @param {string} params.templateId - The template ID
|
|
291
|
+
* @param {string} params.groupId - The owning group
|
|
292
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
293
|
+
* @returns {Promise<SdkResponse<TemplateDetail>>} A promise that resolves to { head, version } for the published version
|
|
294
|
+
*/
|
|
295
|
+
async publish({ templateId, groupId, options = {} }) {
|
|
296
|
+
this.client._require({ templateId, groupId });
|
|
297
|
+
const safeId = encodeURIComponent(templateId);
|
|
298
|
+
return this.client(`/template/${safeId}/publish`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
body: { groupId },
|
|
301
|
+
...options,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Archive a template
|
|
307
|
+
* @async
|
|
308
|
+
* @roles api, admin, developer
|
|
309
|
+
* @param {Object} params - The parameters for archiving a template
|
|
310
|
+
* @param {string} params.templateId - The template ID
|
|
311
|
+
* @param {string} params.groupId - The owning group
|
|
312
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
313
|
+
* @returns {Promise<SdkResponse<TemplateHead>>} A promise that resolves to the archived template head
|
|
314
|
+
*/
|
|
315
|
+
async archive({ templateId, groupId, options = {} }) {
|
|
316
|
+
this.client._require({ templateId, groupId });
|
|
317
|
+
const safeId = encodeURIComponent(templateId);
|
|
318
|
+
return this.client(`/template/${safeId}/archive`, {
|
|
319
|
+
method: "POST",
|
|
320
|
+
body: { groupId },
|
|
321
|
+
...options,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* List a template's versions without their bodies
|
|
327
|
+
* @async
|
|
328
|
+
* @roles api, admin, developer, billing, user
|
|
329
|
+
* @param {Object} params - The parameters for listing versions
|
|
330
|
+
* @param {string} params.templateId - The template ID
|
|
331
|
+
* @param {string} params.groupId - The owning group
|
|
332
|
+
* @param {number} [params.page] - Page number (default 1)
|
|
333
|
+
* @param {number} [params.limit] - Versions per page (default 25, max 100)
|
|
334
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
335
|
+
* @returns {Promise<SdkResponse<Page<VersionSummary>>>} A promise that resolves to { data, total, page, limit }
|
|
336
|
+
*/
|
|
337
|
+
async versions({ templateId, groupId, page, limit, options = {} }) {
|
|
338
|
+
this.client._require({ templateId, groupId });
|
|
339
|
+
const safeId = encodeURIComponent(templateId);
|
|
340
|
+
const queryString = this.client._getQueryString({ groupId, page, limit });
|
|
341
|
+
return this.client(`/template/${safeId}/versions${queryString}`, { method: "GET", ...options });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Validate an unsaved body or a stored version without writing anything
|
|
346
|
+
* @async
|
|
347
|
+
* @roles api, admin, developer, billing, user
|
|
348
|
+
* @param {Object} params - The parameters for validation; pass exactly one of body or version
|
|
349
|
+
* @param {string} params.templateId - The template ID
|
|
350
|
+
* @param {string} params.groupId - The owning group
|
|
351
|
+
* @param {RcsTemplateBody} [params.body] - An unsaved body to validate
|
|
352
|
+
* @param {VersionSelector} [params.version] - A stored version to validate
|
|
353
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
354
|
+
* @throws {Error} Throws a SignalHouseValidationError unless exactly one of body or version is given
|
|
355
|
+
* @returns {Promise<SdkResponse<ValidationResult>>} A promise that resolves to { valid, contentHash, validatorVersion, errors, warnings }
|
|
356
|
+
*/
|
|
357
|
+
async validate({ templateId, groupId, body, version, options = {} }) {
|
|
358
|
+
this.client._require({ templateId, groupId });
|
|
359
|
+
const safeId = encodeURIComponent(templateId);
|
|
360
|
+
return this.client(`/template/${safeId}/validate`, {
|
|
361
|
+
method: "POST",
|
|
362
|
+
body: templateValidateBody({ groupId, body, version }),
|
|
363
|
+
...options,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get a channel's template capabilities: allowed blocks, actions, limits and upload constraints
|
|
369
|
+
* @async
|
|
370
|
+
* @roles api, admin, developer, billing, user
|
|
371
|
+
* @param {Object} params - The parameters for getting capabilities
|
|
372
|
+
* @param {string} params.groupId - The owning group
|
|
373
|
+
* @param {"rcs"} [params.channel] - The channel (default "rcs")
|
|
374
|
+
* @param {("infobip"|"sinch")} [params.provider] - Include this provider's limits and unsupported features
|
|
375
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
376
|
+
* @returns {Promise<SdkResponse<Capabilities>>} A promise that resolves to the capabilities
|
|
377
|
+
*/
|
|
378
|
+
async capabilities({ groupId, channel, provider, options = {} }) {
|
|
379
|
+
this.client._require({ groupId });
|
|
380
|
+
const queryString = this.client._getQueryString({ groupId, channel, provider });
|
|
381
|
+
return this.client(`/template/capabilities${queryString}`, { method: "GET", ...options });
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Upload a media file for use in template bodies; returns its public URL. 503 when media storage is not configured
|
|
386
|
+
* @async
|
|
387
|
+
* @roles api, admin, developer
|
|
388
|
+
* @param {Object} params - The parameters for the upload
|
|
389
|
+
* @param {string} params.groupId - The owning group
|
|
390
|
+
* @param {Blob|File} params.file - The file to upload
|
|
391
|
+
* @param {string} [params.fileName] - File name to send with the file
|
|
392
|
+
* @param {RequestOptions} [params.options] - Additional options for the request
|
|
393
|
+
* @returns {Promise<SdkResponse<MediaAsset>>} A promise that resolves to the stored media asset (201)
|
|
394
|
+
*/
|
|
395
|
+
async uploadMedia({ groupId, file, fileName, options = {} }) {
|
|
396
|
+
this.client._require({ groupId, file });
|
|
397
|
+
return this.multipartClient(`/template/media`, {
|
|
398
|
+
method: "POST",
|
|
399
|
+
body: templateMediaForm({ groupId, file, fileName }),
|
|
400
|
+
...options,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
package/src/domains/Voice.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SipTrunks } from "./voice/SipTrunks.js";
|
|
2
2
|
import { SipProfiles } from "./voice/SipProfiles.js";
|
|
3
3
|
import { ProgrammableVoiceProfiles } from "./voice/ProgrammableVoiceProfiles.js";
|
|
4
|
+
import { NumberRoutes } from "./voice/NumberRoutes.js";
|
|
4
5
|
import { Calls } from "./voice/Calls.js";
|
|
5
6
|
import { CallLogs } from "./voice/CallLogs.js";
|
|
6
7
|
import { GlobalVoiceSettings } from "./voice/GlobalVoiceSettings.js";
|
|
@@ -16,6 +17,7 @@ import { Analytics } from "./voice/Analytics.js";
|
|
|
16
17
|
* - `sdk.voice.sipTrunks` — SIP trunk (peer-to-peer connections to PBX/carrier).
|
|
17
18
|
* - `sdk.voice.sipProfiles` — SIP profile / endpoint (single registerable UA).
|
|
18
19
|
* - `sdk.voice.programmableVoiceProfiles` — Programmable Voice Profile (route a set of numbers through Signal House).
|
|
20
|
+
* - `sdk.voice.numberRoutes` — Per-number inbound route (overrides the profile and subgroup settings).
|
|
19
21
|
* - `sdk.voice.calls` — Twilio-compat programmatic call control (create/hangup/etc.).
|
|
20
22
|
* - `sdk.voice.callLogs` — Account-scoped call history: list/get + presigned recording.
|
|
21
23
|
* - `sdk.voice.globalVoiceSettings` — Account-wide voice defaults (accepted regions, max spend, E911).
|
|
@@ -29,6 +31,7 @@ export class Voice {
|
|
|
29
31
|
this.sipTrunks = new SipTrunks(client, enableAdmin);
|
|
30
32
|
this.sipProfiles = new SipProfiles(client, enableAdmin);
|
|
31
33
|
this.programmableVoiceProfiles = new ProgrammableVoiceProfiles(client, enableAdmin);
|
|
34
|
+
this.numberRoutes = new NumberRoutes(client, enableAdmin);
|
|
32
35
|
this.calls = new Calls(client, enableAdmin);
|
|
33
36
|
this.callLogs = new CallLogs(client, enableAdmin);
|
|
34
37
|
this.globalVoiceSettings = new GlobalVoiceSettings(client, enableAdmin);
|
package/src/domains/Webhooks.js
CHANGED
|
@@ -43,6 +43,10 @@ export class Webhooks {
|
|
|
43
43
|
* `verifyWebhookSignature` from `@signalhousellc/sdk/webhooks` (Node only). `webhookData.authType` is one of
|
|
44
44
|
* `NONE` (default), `BASIC`, `API_KEY`, `OAUTH2` or `HMAC`; `HMAC` (SHGHL-3595) sends no credentials, so the
|
|
45
45
|
* receiver authenticates by the signature alone, and must be sent without `credentials` or `apiHeaderPrefix`.
|
|
46
|
+
* `webhookData.eventSettings` (SHGHL-3884) holds per-event options keyed by event name:
|
|
47
|
+
* `NUMBER_HEALTH_BELOW_THRESHOLD` and `CAMPAIGN_HEALTH_BELOW_THRESHOLD` each take `{ threshold }`, the 7-day
|
|
48
|
+
* health score (1.5–10, in 0.5 steps) below which the event fires; a subscribed health event without one is
|
|
49
|
+
* stored with the default, 6.
|
|
46
50
|
* @async
|
|
47
51
|
* @roles api, admin, developer
|
|
48
52
|
* @param {Object} params - The parameters for creating a webhook
|
|
@@ -62,7 +66,9 @@ export class Webhooks {
|
|
|
62
66
|
* to change the endpoint's own credentials in place: credentials are required when switching to an auth
|
|
63
67
|
* type that needs them, and omitted credentials keep the stored ones. Switching to `NONE` or `HMAC` clears
|
|
64
68
|
* the stored credentials; `HMAC` is rejected for an endpoint with no signing secret (call
|
|
65
|
-
* `rotateWebhookSecret` first).
|
|
69
|
+
* `rotateWebhookSecret` first). `updateData.eventSettings` changes per-event options by event name
|
|
70
|
+
* (`{ NUMBER_HEALTH_BELOW_THRESHOLD: { threshold } }`, `{ CAMPAIGN_HEALTH_BELOW_THRESHOLD: { threshold } }`,
|
|
71
|
+
* 1.5–10 in 0.5 steps); events you omit keep their stored settings. Like every read of a webhook, the
|
|
66
72
|
* response carries `hasSigningSecret` and `hasCredentials` (booleans), never a secret or credential.
|
|
67
73
|
* @async
|
|
68
74
|
* @roles api, admin, developer
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** @typedef {import('../../SignalHouseSDK.js').RequestOptions} RequestOptions */
|
|
2
|
+
/**
|
|
3
|
+
* @template T
|
|
4
|
+
* @typedef {import('../../SignalHouseSDK.js').SdkResponse<T>} SdkResponse
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {Object} NumberRouteData
|
|
9
|
+
* @property {"FORWARD"|"WEBRTC"|"SIP_TRUNK"|"SIP_PROFILE"|"CALL_CONTROL"|"AI"|"VOICEMAIL"} routeAction - How inbound calls to the number are handled.
|
|
10
|
+
* @property {string|null} [forwardToE164] - FORWARD destination (required for FORWARD), or the WEBRTC no-answer destination when noAnswerAction is FORWARD.
|
|
11
|
+
* @property {"VOICEMAIL"|"FORWARD"|"KEEP_RINGING"|null} [noAnswerAction] - WEBRTC only: what happens when nobody answers (default VOICEMAIL).
|
|
12
|
+
* @property {number|null} [noAnswerSeconds] - WEBRTC only: seconds to ring before the no-answer action, 5-120 (default 20).
|
|
13
|
+
* @property {string|null} [routeSipTrunkId] - SIP trunk to deliver to (required for SIP_TRUNK).
|
|
14
|
+
* @property {string|null} [routeSipProfileId] - SIP endpoint to ring (required for SIP_PROFILE).
|
|
15
|
+
* @property {string|null} [webhookUrl] - SHML program URL (required for CALL_CONTROL).
|
|
16
|
+
* @property {string|null} [routeAgentProfileId] - AI agent profile id, AGP… (required for AI).
|
|
17
|
+
* @property {boolean} [recordingEnabled] - Record calls handled by this route.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Number Routes — one inbound route set directly on a single phone number. A
|
|
22
|
+
* number's own route takes priority over its Programmable Voice Profile and
|
|
23
|
+
* subgroup voice settings. Calls the voice-backend service mounted under
|
|
24
|
+
* /voice. Accessed via `sdk.voice.numberRoutes`.
|
|
25
|
+
*/
|
|
26
|
+
export class NumberRoutes {
|
|
27
|
+
constructor(client, enableAdmin) {
|
|
28
|
+
this.client = client;
|
|
29
|
+
this.enableAdmin = enableAdmin;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get a number's own route and the routing currently in effect for it.
|
|
34
|
+
* @async
|
|
35
|
+
* @roles api, admin, developer, billing, user
|
|
36
|
+
* @param {Object} params - Request parameters.
|
|
37
|
+
* @param {string} params.e164 - The phone number in E.164 (leading + optional).
|
|
38
|
+
* @param {RequestOptions} [params.options] - Additional options for the request.
|
|
39
|
+
* @throws {Error} If e164 is missing.
|
|
40
|
+
* @returns {Promise<SdkResponse<any>>} `{ e164, route: {...} | null, effective: {...} }`.
|
|
41
|
+
*/
|
|
42
|
+
async get({ e164, options = {} }) {
|
|
43
|
+
this.client._require({ e164 });
|
|
44
|
+
const safeE164 = encodeURIComponent(e164);
|
|
45
|
+
return this.client(`/voice/api/v1/numbers/${safeE164}/route`, { method: "GET", ...options });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set (create or replace) a number's route. The whole route is replaced:
|
|
50
|
+
* fields the new routeAction doesn't use are cleared.
|
|
51
|
+
* @async
|
|
52
|
+
* @roles api, admin, developer
|
|
53
|
+
* @param {Object} params - Request parameters.
|
|
54
|
+
* @param {string} params.e164 - The phone number in E.164 (leading + optional).
|
|
55
|
+
* @param {NumberRouteData} params.routeData - The route to set.
|
|
56
|
+
* @param {RequestOptions} [params.options] - Additional options for the request.
|
|
57
|
+
* @throws {Error} If e164 or routeData is missing.
|
|
58
|
+
* @returns {Promise<SdkResponse<any>>} `{ e164, route: {...}, effective: {...} }`.
|
|
59
|
+
*/
|
|
60
|
+
async set({ e164, routeData, options = {} }) {
|
|
61
|
+
this.client._require({ e164, routeData });
|
|
62
|
+
const safeE164 = encodeURIComponent(e164);
|
|
63
|
+
return this.client(`/voice/api/v1/numbers/${safeE164}/route`, { method: "PUT", body: routeData, ...options });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Remove a number's own route. The number falls back to its Programmable
|
|
68
|
+
* Voice Profile, subgroup voice settings, or the default.
|
|
69
|
+
* @async
|
|
70
|
+
* @roles api, admin, developer
|
|
71
|
+
* @param {Object} params - Request parameters.
|
|
72
|
+
* @param {string} params.e164 - The phone number in E.164 (leading + optional).
|
|
73
|
+
* @param {RequestOptions} [params.options] - Additional options for the request.
|
|
74
|
+
* @throws {Error} If e164 is missing.
|
|
75
|
+
* @returns {Promise<SdkResponse<any>>} `{ e164, route: null, effective: {...} }`.
|
|
76
|
+
*/
|
|
77
|
+
async delete({ e164, options = {} }) {
|
|
78
|
+
this.client._require({ e164 });
|
|
79
|
+
const safeE164 = encodeURIComponent(e164);
|
|
80
|
+
return this.client(`/voice/api/v1/numbers/${safeE164}/route`, { method: "DELETE", ...options });
|
|
81
|
+
}
|
|
82
|
+
}
|