@signalhousellc/sdk 1.4.2 → 1.4.3

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.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "Signal House SDK for use with the Signal House platform",
5
5
  "type": "module",
6
6
  "main": "src/SignalHouseSDK.js",
@@ -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);
@@ -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
+ }
@@ -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);
@@ -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
+ }