@cemscale-voip/voip-sdk 2.0.10 → 2.0.12

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/dist/types.d.ts CHANGED
@@ -1,30 +1,108 @@
1
+ /**
2
+ * Parameters for logging in as an extension user (softphone / SDK consumer).
3
+ *
4
+ * Extensions authenticate with their extension number + password, scoped to a tenant.
5
+ * After login you receive a JWT token valid for the tenant.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * const { token, user } = await voip.login({
10
+ * username: '1001',
11
+ * password: 'my-sip-password',
12
+ * tenantId: 'uuid-of-tenant',
13
+ * });
14
+ * ```
15
+ */
1
16
  export interface LoginParams {
17
+ /** Extension number (e.g. `"1001"`, `"2005"`). 3-6 digit identifier within a tenant. */
2
18
  username: string;
19
+ /** SIP password set on the extension. Case-sensitive, min 6 characters. */
3
20
  password: string;
21
+ /** UUID of the tenant this extension belongs to. Obtain this from the dashboard or from the list of tenants. */
4
22
  tenantId: string;
5
23
  }
24
+ /**
25
+ * Parameters for logging in as a tenant admin (dashboard user).
26
+ *
27
+ * Tenant admins authenticate with email + password and get a JWT that allows
28
+ * management of their own tenant's resources (extensions, DIDs, queues, etc.).
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const { token, tenant } = await voip.adminLogin({
33
+ * email: 'admin@acme.com',
34
+ * password: 'admin-password',
35
+ * });
36
+ * ```
37
+ */
6
38
  export interface AdminLoginParams {
39
+ /** Admin email address for the tenant (set during tenant creation). */
7
40
  email: string;
41
+ /** Admin password for the tenant admin account. */
8
42
  password: string;
9
43
  }
44
+ /**
45
+ * Response from both `login()` (extension) and `adminLogin()` (admin).
46
+ *
47
+ * Always contains a JWT `token`. The SDK stores this internally and attaches it
48
+ * to subsequent requests as `Authorization: Bearer <token>`.
49
+ *
50
+ * For extension login, `user` is populated with the extension profile.
51
+ * For admin login, `tenant` is populated. Global superadmin login populates both.
52
+ */
10
53
  export interface AuthResponse {
54
+ /** JWT token (HS256). Store this and pass it to the SDK constructor or set on the client. Expires based on server config (default 24h). */
11
55
  token: string;
56
+ /** Extension profile — populated when logging in as an extension user. Null for admin logins. */
12
57
  user?: {
58
+ /** Extension UUID in the database (not the extension number). */
13
59
  id: string;
60
+ /** Extension number string (e.g. `"1001"`). */
14
61
  extension: string;
62
+ /** Human-readable display name. `null` if not set on the extension. */
15
63
  displayName: string | null;
64
+ /** UUID of the tenant this extension belongs to. */
16
65
  tenantId: string;
17
66
  };
67
+ /** Tenant info — populated when logging in as an admin or superadmin. Null for extension-only logins. */
18
68
  tenant?: {
69
+ /** Tenant UUID. */
19
70
  id: string;
71
+ /** Subdomain slug (e.g. `"acme"`). Used in URLs like `acme.demo.cemscale.com`. */
20
72
  subdomain: string;
73
+ /** Company/tenant display name (e.g. `"Acme Corp"`). */
21
74
  companyName: string;
22
75
  };
23
76
  }
77
+ /**
78
+ * TURN/STUN credentials for WebRTC ICE negotiation.
79
+ *
80
+ * The TURN server relays media when a direct peer-to-peer connection can't be
81
+ * established (NAT, firewall). These credentials are time-limited (default 86400s).
82
+ *
83
+ * Retrieve via `getTurnCredentials()`. Pass to the WebRTCPhone constructor.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const creds = await voip.getTurnCredentials();
88
+ * const phone = new WebRTCPhone({
89
+ * ...sipConfig,
90
+ * turnServers: [{
91
+ * urls: creds.urls,
92
+ * username: creds.username,
93
+ * credential: creds.credential,
94
+ * }],
95
+ * });
96
+ * ```
97
+ */
24
98
  export interface TurnCredentials {
99
+ /** TURN server URLs (e.g. `["turn:3.20.219.41:3478?transport=udp", "turns:3.20.219.41:5349?transport=tcp"]`). */
25
100
  urls: string[];
101
+ /** Temporary TURN username generated for this session. Format: `<expiry_timestamp>:<extension>`. */
26
102
  username: string;
103
+ /** HMAC-SHA1 credential derived from the TURN secret + username. Valid only until expiry. */
27
104
  credential: string;
105
+ /** Time-to-live in seconds (default 86400 = 24 hours). Credentials expire after this. */
28
106
  ttl: number;
29
107
  }
30
108
  /**
@@ -77,20 +155,31 @@ export interface TurnCredentials {
77
155
  * database, send them in emails, or cache them indefinitely.
78
156
  */
79
157
  export interface CallRecord {
158
+ /** Database record UUID. Use this for `getCall(id)`, `getRecordingUrl(id)`, and the recording audio endpoint `/api/recordings/<id>/audio`. Distinguish from `call_uuid` which is the FreeSWITCH-level identifier. */
80
159
  id: string;
160
+ /** Tenant UUID the call belongs to. Present for tenant-scoped queries; may be absent if querying as superadmin across all tenants. */
81
161
  tenant_id?: string;
162
+ /** FreeSWITCH call UUID. This is the server-side channel identifier used in FreeSWITCH commands (e.g. `uuid_kill`). Different from `id` (the DB record UUID). Use `call_uuid` for live call operations, `id` for API/CDR lookups. Example: `"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`. */
82
163
  call_uuid: string;
164
+ /** Direction of the call relative to the tenant: `'inbound'` (PSTN → extension), `'outbound'` (extension → PSTN), `'internal'` (extension → extension within same tenant). */
83
165
  direction: 'inbound' | 'outbound' | 'internal';
166
+ /** Display name from the caller's SIP header. `null` if not provided by the carrier. Example: `"John Doe"`. */
84
167
  caller_id_name: string | null;
168
+ /** E.164 caller phone number. `null` if the caller number was hidden/unknown. Example: `"+17865551234"`. */
85
169
  caller_id_number: string | null;
170
+ /** Destination of the call. For inbound calls this is the DID or extension DNIS. For outbound calls this is the dialed PSTN number. `null` for some internal calls. Example: `"+17869876543"` or `"1001"`. */
86
171
  destination: string | null;
172
+ /** Call outcome: `'completed'` (answered + hung up normally), `'failed'` (never connected), `'missed'` (rang but not answered), `'voicemail'` (went to voicemail). `null` for in-progress or unknown state. */
87
173
  status: string | null;
174
+ /** FreeSWITCH hangup cause code string (e.g. `"NORMAL_CLEARING"`, `"NO_ANSWER"`, `"USER_BUSY"`). `null` if the call hasn't ended. */
88
175
  hangup_cause: string | null;
176
+ /** Total call duration in seconds from start to end. `null` for active/unfinished calls. */
89
177
  duration_seconds: number | null;
178
+ /** Billable seconds — from answer to hangup. `null` if call was never answered. Always ≤ `duration_seconds`. */
90
179
  billsec: number | null;
91
- /** External number the call was forwarded to. Null if no forwarding occurred. */
180
+ /** External number the call was forwarded to. Null if no forwarding occurred. Example: `"+13051234567"`. */
92
181
  forwarded_to?: string | null;
93
- /** Whether a voicemail message was left by the caller */
182
+ /** Whether a voicemail message was left by the caller. `true` = voicemail left, `false` or `null` = no voicemail. */
94
183
  was_voicemail?: boolean | null;
95
184
  /**
96
185
  * API endpoint URL for streaming/downloading the call recording.
@@ -138,42 +227,81 @@ export interface CallRecord {
138
227
  * ```
139
228
  */
140
229
  cdn_url: string | null;
230
+ /** ISO 8601 timestamp when the call was initiated. `null` for pre-CDR records. Example: `"2025-06-04T14:30:00.000Z"`. */
141
231
  started_at: string | null;
232
+ /** ISO 8601 timestamp when the call was answered (picked up). `null` if never answered. Example: `"2025-06-04T14:30:05.000Z"`. */
142
233
  answered_at: string | null;
234
+ /** ISO 8601 timestamp when the call ended (hung up). `null` for active calls. Example: `"2025-06-04T14:32:30.000Z"`. */
143
235
  ended_at: string | null;
236
+ /** Arbitrary key-value metadata attached to the call. `null` if no metadata was set. */
144
237
  metadata?: Record<string, unknown> | null;
145
- /** Present when queried as global superadmin */
238
+ /** Present when queried as global superadmin — contains tenant details for this call. */
146
239
  tenants?: TenantInfo;
147
- /** Present when queried as global superadmin (convenience top-level fields) */
240
+ /** Present when queried as global superadmin (convenience top-level fields). Tenant company name. */
148
241
  company_name?: string;
242
+ /** Present when queried as global superadmin (convenience top-level fields). Tenant subdomain. */
149
243
  subdomain?: string;
150
244
  }
245
+ /**
246
+ * Standard pagination metadata returned by all list endpoints.
247
+ *
248
+ * Use `pages` to render page controls and `total` for summary text.
249
+ * On the last page, `page` === `pages`.
250
+ */
151
251
  export interface PaginationInfo {
252
+ /** Current page number (1-indexed). Example: `1`. */
152
253
  page: number;
254
+ /** Number of items per page as requested. Example: `50`. */
153
255
  limit: number;
256
+ /** Total number of items across all pages. Example: `142`. */
154
257
  total: number;
258
+ /** Total number of pages. Example: `3`. Computed as `Math.ceil(total / limit)`. */
155
259
  pages: number;
156
260
  }
261
+ /**
262
+ * Response from `listCalls()` — a paginated list of call detail records.
263
+ *
264
+ * Use `pagination.total` for a summary badge and `pagination.pages` for pagination controls.
265
+ */
157
266
  export interface CallListResponse {
158
267
  calls: CallRecord[];
159
268
  pagination: PaginationInfo;
160
269
  }
270
+ /**
271
+ * Filtering/pagination params for `listCalls()`.
272
+ *
273
+ * All fields are optional. Omitted fields are not filtered (returns all matching records).
274
+ * Combine filters: `{ direction: 'inbound', status: 'completed', dateFrom: '2025-06-01' }`
275
+ * returns only completed inbound calls since June 1.
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * // Get last 10 missed inbound calls
280
+ * const { calls } = await voip.listCalls({
281
+ * direction: 'inbound',
282
+ * status: 'missed',
283
+ * limit: 10,
284
+ * });
285
+ * ```
286
+ */
161
287
  export interface CallListParams {
288
+ /** Page number (1-indexed). Default: `1`. */
162
289
  page?: number;
163
- /** Max 500 per page */
290
+ /** Max 500 per page. Default: `50`. */
164
291
  limit?: number;
292
+ /** Filter by call direction. Default: `'all'` (no filter). */
165
293
  direction?: 'inbound' | 'outbound' | 'internal' | 'all';
166
- /** Filter by call status */
294
+ /** Filter by call status. Default: `'all'` (no filter). */
167
295
  status?: 'completed' | 'failed' | 'missed' | 'voicemail' | 'all';
168
- /** ISO date string — calls started on or after this date */
296
+ /** ISO date string — calls started on or after this date. Example: `"2025-06-01T00:00:00.000Z"`. */
169
297
  dateFrom?: string;
170
- /** ISO date string — calls started on or before this date */
298
+ /** ISO date string — calls started on or before this date. Example: `"2025-06-04T23:59:59.999Z"`. */
171
299
  dateTo?: string;
172
- /** Filter by extension number (matches caller or destination) */
300
+ /** Filter by extension number (matches caller or destination). Example: `"1001"`. */
173
301
  extension?: string;
174
- /** Search by phone number or name (matches caller_id_number, destination, caller_id_name) */
302
+ /** Search by phone number or name (matches caller_id_number, destination, caller_id_name). Partial match, case-insensitive. Example: `"+1786"`. */
175
303
  number?: string;
176
- /** Filter by tenant ID (superadmin only — alternative to X-Tenant-ID header) */
304
+ /** Filter by tenant ID (superadmin only — alternative to X-Tenant-ID header). */
177
305
  tenantId?: string;
178
306
  /**
179
307
  * Filter by forwarding status.
@@ -182,52 +310,178 @@ export interface CallListParams {
182
310
  * - omit: return all calls regardless of forwarding
183
311
  */
184
312
  forwarded?: 'true' | 'false';
185
- /** Search by the external number the call was forwarded to (partial match, case-insensitive) */
313
+ /** Search by the external number the call was forwarded to (partial match, case-insensitive). */
186
314
  forwardedTo?: string;
187
315
  }
316
+ /**
317
+ * Parameters for originating (placing) a new outbound call from a webphone/extension.
318
+ *
319
+ * The server creates a new call leg from the specified extension to the PSTN number
320
+ * and bridges it to the extension's WebRTC session.
321
+ *
322
+ * @example
323
+ * ```ts
324
+ * const { callUuid } = await voip.originateCall({
325
+ * fromExtension: '1001',
326
+ * toNumber: '+17865551234',
327
+ * });
328
+ * ```
329
+ */
188
330
  export interface OriginateParams {
331
+ /** Extension number to originate the call from. Must be an active extension in your tenant. Example: `"1001"`. */
189
332
  fromExtension: string;
333
+ /** Destination PSTN number in E.164 format. Example: `"+17865551234"`. */
190
334
  toNumber: string;
191
335
  }
336
+ /**
337
+ * Response from `originateCall()`.
338
+ *
339
+ * Contains the FreeSWITCH UUID of the outbound call leg for monitoring.
340
+ */
192
341
  export interface OriginateResponse {
342
+ /** Human-readable status message. Example: `"Call originated successfully"`. */
193
343
  message: string;
344
+ /** FreeSWITCH call UUID of the originated outbound leg. Use this to track the call. */
194
345
  callUuid: string;
195
346
  }
347
+ /**
348
+ * Parameters for transferring an active call to another destination.
349
+ *
350
+ * Supports blind transfer (immediate, no consultation) and attended transfer
351
+ * (speak to the target first, then merge).
352
+ *
353
+ * @example
354
+ * ```ts
355
+ * // Blind transfer — caller goes directly to target
356
+ * await voip.transferCall(callId, {
357
+ * targetExtension: '1002',
358
+ * type: 'blind',
359
+ * });
360
+ *
361
+ * // Attended transfer — you speak first, then complete the transfer
362
+ * await voip.transferCall(callId, {
363
+ * targetExtension: '1002',
364
+ * type: 'attended',
365
+ * });
366
+ * ```
367
+ */
196
368
  export interface TransferParams {
369
+ /** Target extension number to transfer the call to. Example: `"1002"`. */
197
370
  targetExtension: string;
371
+ /** Transfer type. `'blind'` = immediate (SIP REFER). `'attended'` = consult then complete (conference bridge). Default: `'blind'`. */
198
372
  type?: 'blind' | 'attended';
199
373
  }
374
+ /**
375
+ * Response from call eavesdropping (listen in on an active call).
376
+ *
377
+ * Returned by `eavesdropCall()`. Supports whisper (coach can talk to the agent
378
+ * without the caller hearing), spy (listen-only), and full (3-way join).
379
+ */
200
380
  export interface EavesdropResponse {
381
+ /** Human-readable message. Example: `"Whisper session started"`. */
201
382
  message: string;
383
+ /** FreeSWITCH UUID of the eavesdrop channel. */
202
384
  spyUuid: string;
385
+ /** FreeSWITCH UUID of the target call being monitored. */
203
386
  targetCallUuid: string;
387
+ /** Eavesdrop mode: `'whisper'`, `'spy'`, or `'full'`. */
204
388
  mode: string;
205
389
  }
390
+ /**
391
+ * A currently active (in-progress) call on the FreeSWITCH server.
392
+ *
393
+ * Returned by `listActiveCalls()`. Each entry represents one call leg (channel).
394
+ * For a two-party call, you'll see two entries — one for each leg.
395
+ *
396
+ * **Key field:** `uuid` — this is the FreeSWITCH channel UUID required for
397
+ * three-way calling operations (`addCallParticipant`, `mergeCalls`).
398
+ * It is NOT the same as `call_uuid` (which is the logical call identifier).
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * // List all active calls and show the caller/destination
403
+ * const { calls } = await voip.listActiveCalls();
404
+ * calls.forEach(c => {
405
+ * console.log(`${c.caller_id_number} → ${c.destination} (${c.status})`);
406
+ * });
407
+ * ```
408
+ */
206
409
  export interface ActiveCall {
410
+ /** FreeSWITCH channel UUID — required for three-way operations. This is the server-side channel identifier. Example: `"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`. */
411
+ uuid: string;
412
+ /** Logical call UUID (same across both legs of a bridged call). Use for CDR matching and WebSocket event correlation. Example: `"c0ffee00-dead-beef-0000-000000000001"`. */
207
413
  call_uuid: string;
414
+ /** Caller display name from SIP header. Example: `"John Smith"`. */
208
415
  caller_id_name: string;
416
+ /** Caller phone number. Example: `"+17865551234"` or `"1001"` for internal calls. */
209
417
  caller_id_number: string;
418
+ /** Destination of the call. For outbound calls this is the PSTN number being dialed. For inbound this is the DID or extension. Example: `"+17869876543"`. */
210
419
  destination: string;
420
+ /** Call direction: `'inbound'`, `'outbound'`, or `'internal'`. */
211
421
  direction: string;
422
+ /** Channel state: `'CS_EXECUTE'`, `'CS_CONSUME_MEDIA'`, `'CS_HIBERNATE'`, etc. Use `answered` boolean for high-level state. */
212
423
  status: string;
424
+ /** ISO 8601 timestamp when the call channel was created. Example: `"2025-06-04T14:30:00Z"`. */
213
425
  started_at: string;
426
+ /** Whether the call has been answered (both parties connected). `false` during ringing/early-media. */
214
427
  answered: boolean;
428
+ /** Whether this channel currently has music-on-hold active. */
215
429
  on_hold: boolean;
216
430
  }
431
+ /**
432
+ * Quick call statistics for the current tenant (today and this month).
433
+ *
434
+ * Returned by `getCallStats()`. Use for dashboard summary badges.
435
+ *
436
+ * @example
437
+ * ```ts
438
+ * const { stats } = await voip.getCallStats();
439
+ * console.log(`Today: ${stats.total} calls (${stats.inbound} in, ${stats.outbound} out)`);
440
+ * ```
441
+ */
217
442
  export interface CallStats {
218
443
  stats: {
444
+ /** Total calls in the current period. */
219
445
  total: number;
446
+ /** Inbound calls in the current period. */
220
447
  inbound: number;
448
+ /** Outbound calls in the current period. */
221
449
  outbound: number;
450
+ /** Internal (extension-to-extension) calls in the current period. */
222
451
  internal: number;
452
+ /** Period description: `'today'` or `'month'`. */
223
453
  period: string;
224
454
  };
225
455
  }
456
+ /**
457
+ * A SIP extension (phone user) within a tenant.
458
+ *
459
+ * Extensions are the core entity — every person who makes/receives calls has one.
460
+ * Each extension has a SIP password for registration, voicemail settings,
461
+ * call forwarding rules, recording preferences, and real-time presence status.
462
+ *
463
+ * Returned by `listExtensions()`, `getExtension()`, `createExtension()`, and `updateExtension()`.
464
+ *
465
+ * @example
466
+ * ```ts
467
+ * // List all extensions and show their presence
468
+ * const { extensions } = await voip.listExtensions();
469
+ * extensions.forEach(ext => {
470
+ * const presenceIcon = ext.presence === 'on_call' ? '🟢' : ext.presence === 'offline' ? '⚫' : '⭕';
471
+ * console.log(`${presenceIcon} ${ext.extension} — ${ext.display_name}`);
472
+ * });
473
+ * ```
474
+ */
226
475
  export interface Extension {
476
+ /** Database UUID of the extension record. */
227
477
  id: string;
478
+ /** Tenant UUID this extension belongs to. Present for tenant-scoped queries; may be absent in superadmin global lists. */
228
479
  tenant_id?: string;
480
+ /** Extension number (3-6 digits). Used for dialing internally and as the SIP username. Example: `"1001"`. */
229
481
  extension: string;
482
+ /** Human-readable display name shown to other callers and in the dashboard. `null` if not set. Example: `"John Smith"`. */
230
483
  display_name: string | null;
484
+ /** Whether voicemail is enabled for this extension. When enabled, unanswered calls go to voicemail after the ring timeout. Example: `true`. */
231
485
  voicemail_enabled: boolean;
232
486
  /**
233
487
  * Whether call recording is enabled for this extension.
@@ -241,31 +495,68 @@ export interface Extension {
241
495
  * The tenant must also have `noise_cancellation_enabled = true` (the default) for NC to apply.
242
496
  */
243
497
  noise_cancellation_enabled?: boolean;
498
+ /** 4-6 digit PIN required to access voicemail via *97. `null` if no PIN is set (voicemail has no password protection). */
244
499
  voicemail_pin?: string | null;
500
+ /** Type of voicemail greeting: `'default'`, `'name'`, `'custom_audio'`, `'custom_tts'`. `null` if using default system greeting. */
245
501
  voicemail_greeting_type?: VoicemailGreetingType | null;
502
+ /** Text spoken for TTS greeting. `null` if not using TTS. Example: `"You've reached John. Please leave a message."`. */
246
503
  voicemail_greeting_text?: string | null;
504
+ /** Server path to uploaded custom audio greeting file. `null` if no custom audio. */
247
505
  voicemail_greeting_audio_path?: string | null;
506
+ /** Whether Do Not Disturb is enabled. When true, calls go directly to voicemail without ringing. */
248
507
  do_not_disturb: boolean;
508
+ /** Outbound caller ID (DID number) shown to recipients when this extension makes outbound calls. Falls back to tenant's first active DID if `null`. Example: `"+17868392727"`. */
249
509
  outbound_caller_id?: string | null;
510
+ /** Extension status: `'active'` (can register and make/receive calls), `'disabled'` (cannot register or call). */
250
511
  status: string;
251
- /** Real-time presence status: 'available', 'on_call', 'offline', 'do_not_disturb' */
512
+ /** Real-time presence status from Redis: `'available'`, `'on_call'`, `'offline'`, `'do_not_disturb'`. Use for presence indicators in the UI. Not persisted — comes from live registration/call state. */
252
513
  presence?: string;
514
+ /** ISO 8601 timestamp when this extension was created. Example: `"2025-01-15T10:30:00.000Z"`. */
253
515
  created_at: string;
516
+ /** Whether unconditional call forwarding is enabled. */
254
517
  call_forward_enabled?: boolean;
518
+ /** Number to forward calls to when `call_forward_enabled` is true. E.164 for external, extension number for internal. Example: `"+13051234567"`. */
255
519
  call_forward_number?: string | null;
520
+ /** Forward type: `'always'`, `'busy'`, or `'no-answer'`. `null` when forwarding is disabled. */
256
521
  call_forward_type?: string | null;
522
+ /** External CRM user ID for integration mapping. `null` if not linked to a CRM record. */
257
523
  crm_user_id?: string | null;
524
+ /** Arbitrary JSON metadata from CRM integration. `null` if no metadata set. */
258
525
  crm_metadata?: Record<string, unknown> | null;
259
- /** Present when queried as global superadmin */
526
+ /** Present when queried as global superadmin — contains tenant details. */
260
527
  tenants?: TenantInfo;
261
528
  }
529
+ /**
530
+ * Parameters for creating a new SIP extension.
531
+ *
532
+ * At minimum, provide `extension`, `password`, and `displayName`.
533
+ * All other fields are optional with sensible defaults.
534
+ *
535
+ * @example
536
+ * ```ts
537
+ * // Create a basic extension with voicemail PIN
538
+ * const { extension: ext } = await voip.createExtension({
539
+ * extension: '2001',
540
+ * password: 'secure123',
541
+ * displayName: 'Alice Jones',
542
+ * voicemailEnabled: true,
543
+ * voicemailPin: '1234',
544
+ * outboundCallerId: '+17868392727',
545
+ * }, { tenantId: 'tenant-uuid' });
546
+ * ```
547
+ */
262
548
  export interface CreateExtensionParams {
549
+ /** Extension number (3-6 digits). Must be unique within the tenant. Example: `"2001"`. */
263
550
  extension: string;
551
+ /** SIP password for registration. Min 6 characters. Used by desk phones and WebRTC softphones to authenticate. Example: `"secure123"`. */
264
552
  password: string;
553
+ /** Human-readable display name. Shown to other callers via caller ID name. Example: `"Alice Jones"`. */
265
554
  displayName: string;
555
+ /** Enable voicemail for this extension. Default: `true`. */
266
556
  voicemailEnabled?: boolean;
267
- /** 4-6 digit PIN required to check voicemail via *97. If not set, voicemail has no PIN protection. */
557
+ /** 4-6 digit PIN required to check voicemail via *97. If not set, voicemail has no PIN protection. Example: `"1234"`. */
268
558
  voicemailPin?: string;
559
+ /** Initial Do Not Disturb state. Default: `false`. */
269
560
  doNotDisturb?: boolean;
270
561
  /**
271
562
  * Whether to record all calls for this extension.
@@ -290,13 +581,38 @@ export interface CreateExtensionParams {
290
581
  */
291
582
  outboundCallerId?: string | null;
292
583
  }
584
+ /**
585
+ * Parameters for updating an existing SIP extension.
586
+ *
587
+ * All fields are optional — only include fields you want to change.
588
+ * Send `null` for string fields (like `voicemailPin`, `outboundCallerId`, `crmUserId`) to clear them.
589
+ *
590
+ * @example
591
+ * ```ts
592
+ * // Rename extension and enable DND
593
+ * await voip.updateExtension(extId, {
594
+ * displayName: 'Alice J.',
595
+ * doNotDisturb: true,
596
+ * });
597
+ *
598
+ * // Clear the outbound caller ID (revert to tenant default)
599
+ * await voip.updateExtension(extId, {
600
+ * outboundCallerId: null,
601
+ * });
602
+ * ```
603
+ */
293
604
  export interface UpdateExtensionParams {
605
+ /** New display name. Example: `"Alice Jones-Smith"`. */
294
606
  displayName?: string;
607
+ /** New SIP password. Min 6 characters. */
295
608
  password?: string;
609
+ /** Enable or disable voicemail. */
296
610
  voicemailEnabled?: boolean;
297
611
  /** 4-6 digit PIN for voicemail access. Set to null to remove PIN protection. */
298
612
  voicemailPin?: string | null;
613
+ /** Enable or disable Do Not Disturb. When true, calls go straight to voicemail. */
299
614
  doNotDisturb?: boolean;
615
+ /** Set extension status: `'active'` (can register/call) or `'disabled'` (blocked). */
300
616
  status?: 'active' | 'disabled';
301
617
  /**
302
618
  * Enable or disable call recording for this extension.
@@ -317,7 +633,9 @@ export interface UpdateExtensionParams {
317
633
  * Set to `null` to clear (calls will fall back to the tenant's first active DID).
318
634
  */
319
635
  outboundCallerId?: string | null;
636
+ /** CRM user ID for integration mapping. Set to `null` to unlink. */
320
637
  crmUserId?: string | null;
638
+ /** Arbitrary JSON metadata from CRM. Set to `null` to clear. */
321
639
  crmMetadata?: Record<string, unknown> | null;
322
640
  }
323
641
  export interface Tenant {
@@ -741,12 +1059,21 @@ export interface RecordingUrlResponse {
741
1059
  /** Always `null` — CDN URLs and local URLs don't expire. */
742
1060
  expiresIn: number | null;
743
1061
  }
1062
+ /**
1063
+ * Filtering/pagination params for `listRecordings()`.
1064
+ *
1065
+ * All fields optional. Omit a field to skip that filter.
1066
+ */
744
1067
  export interface RecordingListParams {
1068
+ /** Page number (1-indexed). Default: `1`. */
745
1069
  page?: number;
1070
+ /** Items per page. Default: `50`. */
746
1071
  limit?: number;
1072
+ /** ISO date string — recordings after this date. Example: `"2025-06-01T00:00:00.000Z"`. */
747
1073
  dateFrom?: string;
1074
+ /** ISO date string — recordings before this date. Example: `"2025-06-04T23:59:59.999Z"`. */
748
1075
  dateTo?: string;
749
- /** Filter by direction */
1076
+ /** Filter by direction. `'inbound'` for incoming call recordings, `'outbound'` for outgoing. */
750
1077
  direction?: 'inbound' | 'outbound';
751
1078
  }
752
1079
  export interface RecordingListResponse {
@@ -754,6 +1081,25 @@ export interface RecordingListResponse {
754
1081
  pagination: PaginationInfo;
755
1082
  }
756
1083
  export type PresenceStatus = 'available' | 'registered' | 'on_call' | 'busy' | 'ringing' | 'dnd' | 'idle' | 'offline';
1084
+ /**
1085
+ * Map of extension numbers to their current presence status.
1086
+ *
1087
+ * Keys are extension numbers (e.g. `"1001"`), values are presence status strings.
1088
+ * Use this for a quick presence overview — iterate keys and show status badges.
1089
+ *
1090
+ * For detailed presence (with call UUID, caller info, direction, timestamps),
1091
+ * use `listPresenceSubscriptions()` which returns `PresenceDetailMap`.
1092
+ *
1093
+ * @example
1094
+ * ```ts
1095
+ * const presence = await voip.getPresence();
1096
+ * // presence = { "1001": "on_call", "1002": "available", "1003": "offline" }
1097
+ * Object.entries(presence).forEach(([ext, status]) => {
1098
+ * const color = status === 'on_call' ? 'green' : status === 'offline' ? 'gray' : 'blue';
1099
+ * renderBadge(ext, status, color);
1100
+ * });
1101
+ * ```
1102
+ */
757
1103
  export interface PresenceMap {
758
1104
  [extension: string]: PresenceStatus | string;
759
1105
  }
@@ -885,16 +1231,75 @@ export interface WebRTCConfig {
885
1231
  */
886
1232
  jwtToken?: string;
887
1233
  }
1234
+ /**
1235
+ * Information about an active WebRTC call managed by the SIP.js softphone.
1236
+ *
1237
+ * ## CRITICAL: `id` vs `fsChannelUuid` — these are NOT the same thing
1238
+ *
1239
+ * | Field | What it is | When populated | Used for |
1240
+ * |-------|-----------|---------------|----------|
1241
+ * | `id` | sip.js session ID (local, client-generated) | Immediately (ringing) | Answer, reject, hold, mute, DTMF, hangup |
1242
+ * | `fsChannelUuid` | FreeSWITCH channel UUID (server-side) | AFTER connect (`state: 'established'`) | Three-way operations (`addCallParticipant`, `mergeCalls`) |
1243
+ *
1244
+ * **Never pass `id` to three-way methods.** Always use `fsChannelUuid` for
1245
+ * server-side operations. `id` is a sip.js internal identifier and has no meaning
1246
+ * on the FreeSWITCH server.
1247
+ *
1248
+ * ## Lifecycle
1249
+ *
1250
+ * ```
1251
+ * idle → connecting → ringing → established → terminating → terminated
1252
+ * ↕
1253
+ * held
1254
+ * ```
1255
+ *
1256
+ * - `idle`: No active call. Phone ready.
1257
+ * - `connecting`: Outbound call — sending INVITE, waiting for provisional response.
1258
+ * - `ringing`: Outbound — receiving 180 Ringing. Inbound — incoming call, waiting for answer.
1259
+ * - `established`: Call is connected, audio flows. `fsChannelUuid` becomes non-null here.
1260
+ * - `held`: Call is on hold (re-INVITE with sendonly). Music-on-hold plays to remote party.
1261
+ * - `terminating`: BYE sent or received, waiting for final ACK.
1262
+ * - `terminated`: Call ended. Resources released.
1263
+ *
1264
+ * ## UI guidelines
1265
+ *
1266
+ * - Show DTMF dialpad when `state === 'established'` and NOT `held`.
1267
+ * - Grey out hold button during `connecting` and `ringing` — hold is only available when established.
1268
+ * - Show "Calling..." during `connecting`, "Ringing..." during `ringing`, timer during `established`.
1269
+ * - If `isPaging === true`, auto-answer and route audio to speaker (no earpiece).
1270
+ * - Use `pagingGroup` and `pagingInitiator` to display "Paging: Sales Floor by 1001" in the UI.
1271
+ * - Use `callType` to show "WhatsApp Call" badge in the call screen.
1272
+ */
888
1273
  export interface WebRTCCallInfo {
1274
+ /**
1275
+ * sip.js session ID — a locally generated string (e.g. `"abc123"`).
1276
+ *
1277
+ * **This is NOT the FreeSWITCH UUID.** Use this for client-side operations only:
1278
+ * `answer()`, `reject()`, `hold()`, `mute()`, `sendDtmf()`, `hangup()`.
1279
+ *
1280
+ * Available immediately when the call is created (during `ringing` state).
1281
+ *
1282
+ * **DO NOT** pass this to `addCallParticipant()`, `mergeCalls()`, or any other
1283
+ * server-side method — those require `fsChannelUuid`.
1284
+ */
889
1285
  id: string;
1286
+ /** Call direction relative to this phone: `'inbound'` (someone called us) or `'outbound'` (we called someone). */
890
1287
  direction: 'inbound' | 'outbound';
1288
+ /** Remote party's phone number. For inbound calls this is the caller's number. For outbound calls this is the dialed number. Example: `"+17865551234"`. Initially `"unknown"` during very early ringing. */
891
1289
  remoteIdentity: string;
1290
+ /** Remote party's display name from SIP header. Example: `"John Doe"`. May be empty string if not provided. */
892
1291
  remoteDisplayName: string;
1292
+ /** Current state in the call lifecycle. See the type-level docs for the full state machine. */
893
1293
  state: WebRTCCallState;
1294
+ /** `Date` object when the call started (outbound INVITE sent or inbound INVITE received). `null` when `state === 'idle'`. */
894
1295
  startTime: Date | null;
1296
+ /** `Date` object when the call was answered (200 OK received/sent). `null` until `state === 'established'`. */
895
1297
  answerTime: Date | null;
1298
+ /** Whether the local user has placed this call on hold. `true` only during `held` state. */
896
1299
  held: boolean;
1300
+ /** Whether the local microphone is muted. Toggle via `phone.mute()`. The remote party hears silence but the call stays active. */
897
1301
  muted: boolean;
1302
+ /** String of DTMF digits sent during this call. Example: `""` (none sent), `"123"` (digits 1, 2, 3 sent). Accumulates over the call life. */
898
1303
  dtmfSent: string;
899
1304
  /**
900
1305
  * Channel type of the inbound call.
@@ -916,6 +1321,8 @@ export interface WebRTCCallInfo {
916
1321
  /**
917
1322
  * Whether this is a paging/intercom broadcast call.
918
1323
  * When `true`, the phone should auto-answer and play through the speaker (bocina).
1324
+ * The SDK handles auto-answer automatically via the `autoAnswer` config and the
1325
+ * `pagingCall` event on `WebRTCEventMap`.
919
1326
  */
920
1327
  isPaging?: boolean;
921
1328
  /**
@@ -928,6 +1335,23 @@ export interface WebRTCCallInfo {
928
1335
  * @example "1001"
929
1336
  */
930
1337
  pagingInitiator?: string | null;
1338
+ /**
1339
+ * FreeSWITCH channel UUID for this call.
1340
+ *
1341
+ * This is the server-side UUID that must be used for three-way calling
1342
+ * operations (`addCallParticipant`, `mergeCalls`, `mergeDirectCalls`).
1343
+ * It is resolved automatically after the call connects by matching the
1344
+ * caller/callee info against FreeSWITCH's active channels.
1345
+ *
1346
+ * **Populated in `state: 'established'`** — `null` during ringing/connecting.
1347
+ *
1348
+ * **Critical:** Do NOT confuse this with `id` (the sip.js session ID).
1349
+ * `fsChannelUuid` is the FreeSWITCH channel UUID needed for server-side operations.
1350
+ * `id` is the local sip.js session ID for client-side operations.
1351
+ *
1352
+ * @example "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
1353
+ */
1354
+ fsChannelUuid: string | null;
931
1355
  }
932
1356
  export interface WebRTCEventMap {
933
1357
  registered: () => void;
@@ -953,40 +1377,104 @@ export interface WebRTCEventMap {
953
1377
  }) => void;
954
1378
  error: (error: Error) => void;
955
1379
  }
1380
+ /**
1381
+ * A participant in a conference room.
1382
+ *
1383
+ * Each member has a FreeSWITCH channel UUID (`uuid`) and a conference-specific
1384
+ * member ID (`id`). Use `id` for conference operations like mute/kick/deaf.
1385
+ */
956
1386
  export interface ConferenceMember {
1387
+ /** Conference member ID (integer). Used for mute/kick/deaf operations within the conference. Example: `"1"`. */
957
1388
  id: string;
1389
+ /** FreeSWITCH channel UUID for this member's call leg. Example: `"a1b2c3d4-e5f6-7890-abcd-ef1234567890"`. */
958
1390
  uuid: string;
1391
+ /** Caller ID name for this participant. Example: `"John Smith"`. */
959
1392
  callerIdName: string;
1393
+ /** Caller ID number for this participant. Example: `"+17865551234"` or `"1001"`. */
960
1394
  callerIdNumber: string;
1395
+ /** Whether this member is muted (cannot speak into conference). */
961
1396
  muted: boolean;
1397
+ /** Whether this member is deafened (cannot hear conference). */
962
1398
  deaf: boolean;
1399
+ /** ISO 8601 timestamp when this member joined the conference. Example: `"2025-06-04T14:30:00Z"`. */
963
1400
  joinTime: string;
964
1401
  }
1402
+ /**
1403
+ * An active conference room on the FreeSWITCH server.
1404
+ *
1405
+ * Returned by `listConferences()`. Shows the conference name, number of members,
1406
+ * lock status, runtime, and full member list with mute/deaf state.
1407
+ *
1408
+ * @example
1409
+ * ```ts
1410
+ * const { conferences } = await voip.listConferences();
1411
+ * conferences.forEach(conf => {
1412
+ * console.log(`${conf.name}: ${conf.memberCount} members, ${conf.locked ? 'locked' : 'open'}`);
1413
+ * conf.members.forEach(m => {
1414
+ * console.log(` ${m.callerIdNumber} muted=${m.muted} deaf=${m.deaf}`);
1415
+ * });
1416
+ * });
1417
+ * ```
1418
+ */
965
1419
  export interface Conference {
1420
+ /** Conference room name (e.g. `"conf_<uuid>"` or a custom name). */
966
1421
  name: string;
1422
+ /** Number of active participants currently in the room. */
967
1423
  memberCount: number;
1424
+ /** Whether the conference is locked (no new members can join). */
968
1425
  locked: boolean;
1426
+ /** Human-readable runtime string (e.g. `"0:05:23"` for 5 minutes, 23 seconds). */
969
1427
  runTime: string;
1428
+ /** List of all active participants with their mute/deaf state. */
970
1429
  members: ConferenceMember[];
971
1430
  }
1431
+ /**
1432
+ * A member (extension) within a ring group.
1433
+ *
1434
+ * Each member has a priority (for sequential strategy) and a delay before ringing.
1435
+ */
972
1436
  export interface RingGroupMember {
1437
+ /** Database UUID of this member entry. */
973
1438
  id: string;
1439
+ /** Extension number (e.g. `"1001"`). */
974
1440
  extension: string;
1441
+ /** Ring priority for sequential strategy. Lower numbers ring first. `1`-based. */
975
1442
  priority: number;
1443
+ /** Seconds to wait before ringing this member (applies to sequential strategy). Default: `0`. */
976
1444
  delay_seconds: number;
977
1445
  }
1446
+ /**
1447
+ * A ring group — rings multiple extensions when a call arrives.
1448
+ *
1449
+ * Strategies:
1450
+ * - `simultaneous`: Ring all members at once. First to answer gets the call.
1451
+ * - `sequential`: Ring members one at a time in priority order. Move to next after `ring_timeout`.
1452
+ * - `random`: Ring a random member.
1453
+ *
1454
+ * If no one answers within `ring_timeout`, execute `no_answer_action` on `no_answer_target`.
1455
+ */
978
1456
  export interface RingGroup {
1457
+ /** Database UUID of the ring group. */
979
1458
  id: string;
1459
+ /** Tenant UUID. Present for tenant-scoped queries. */
980
1460
  tenant_id?: string;
1461
+ /** Human-readable ring group name. Example: `"Sales Team"`. */
981
1462
  name: string;
1463
+ /** Ring strategy: `'simultaneous'`, `'sequential'`, or `'random'`. */
982
1464
  strategy: string;
1465
+ /** Total ring time in seconds before executing `no_answer_action`. Example: `30`. */
983
1466
  ring_timeout: number;
1467
+ /** Action when no one answers: `'voicemail'`, `'ivr'`, `'extension'`, `'hangup'`, `'ai_agent'`. */
984
1468
  no_answer_action: string;
1469
+ /** Target for `no_answer_action` (extension number, IVR UUID, etc.). `null` when action is `'hangup'`. */
985
1470
  no_answer_target: string | null;
1471
+ /** Status: `'active'` or `'disabled'`. */
986
1472
  status: string;
1473
+ /** ISO 8601 timestamp of creation. */
987
1474
  created_at: string;
1475
+ /** Ordered list of ring group members. */
988
1476
  members: RingGroupMember[];
989
- /** Present when queried as global superadmin */
1477
+ /** Present when queried as global superadmin. */
990
1478
  tenant?: TenantInfo;
991
1479
  }
992
1480
  export interface CreateRingGroupParams {
@@ -1001,33 +1489,75 @@ export interface CreateRingGroupParams {
1001
1489
  delaySeconds?: number;
1002
1490
  }>;
1003
1491
  }
1492
+ /**
1493
+ * An agent (extension) assigned to a call queue.
1494
+ *
1495
+ * Agents receive calls distributed by the queue strategy.
1496
+ * Queue managers (ext 6000) can add/remove themselves via `*45`/`*46`.
1497
+ */
1004
1498
  export interface QueueAgent {
1499
+ /** Database UUID of this agent assignment. */
1005
1500
  id: string;
1501
+ /** Extension number assigned to the queue. Example: `"1001"`. */
1006
1502
  extension: string;
1503
+ /** Agent tier/priority. Lower numbers = higher priority (gets calls first). Default: `5`. */
1007
1504
  priority: number;
1505
+ /** Agent penalty (post-call wrap-up delay). Higher = longer delay before next call. Default: `0`. */
1008
1506
  penalty: number;
1507
+ /** Agent status: `'available'`, `'on_call'`, `'paused'`, `'logged_out'`. */
1009
1508
  status: string;
1010
1509
  }
1510
+ /**
1511
+ * A call queue — distributes incoming calls among agents based on a strategy.
1512
+ *
1513
+ * Callers hear music-on-hold while waiting for an available agent.
1514
+ * Queue managers can log in/log out, pause, and monitor via FreeSWITCH codes.
1515
+ *
1516
+ * Strategies:
1517
+ * - `round-robin`: Cycle through agents in fixed order.
1518
+ * - `longest-idle`: Agent idle the longest gets the next call.
1519
+ * - `ring-all`: Ring all available agents simultaneously.
1520
+ * - `least-calls`: Agent with fewest calls today gets the next.
1521
+ * - `random`: Pick a random available agent.
1522
+ */
1011
1523
  export interface CallQueue {
1524
+ /** Database UUID of the queue. */
1012
1525
  id: string;
1526
+ /** Tenant UUID. */
1013
1527
  tenant_id?: string;
1528
+ /** Queue name displayed in dashboards and reports. Example: `"Support Queue"`. */
1014
1529
  name: string;
1530
+ /** Distribution strategy: `'round-robin'`, `'longest-idle'`, `'ring-all'`, `'least-calls'`, `'random'`. */
1015
1531
  strategy: string;
1532
+ /** Music-on-hold sound file to play while caller waits. Example: `"default"` or custom audio file name. */
1016
1533
  moh_sound: string;
1534
+ /** Announcement audio played to callers on entry (position in queue, estimated wait). `null` if disabled. */
1017
1535
  announce_sound: string | null;
1536
+ /** How often (seconds) to replay the announcement to waiting callers. Example: `60`. */
1018
1537
  announce_frequency: number;
1538
+ /** Maximum wait time in seconds before caller times out. Example: `300` (5 min). */
1019
1539
  max_wait_time: number;
1540
+ /** Maximum number of simultaneous callers allowed in the queue. Example: `20`. */
1020
1541
  max_callers: number;
1542
+ /** Seconds each agent's phone rings before moving to next agent. Example: `30`. */
1021
1543
  ring_timeout: number;
1544
+ /** Seconds after a call ends before the agent gets the next call (post-call work). Example: `10`. */
1022
1545
  wrap_up_time: number;
1546
+ /** Action when no agents are logged in: `'voicemail'`, `'ivr'`, `'extension'`, `'hangup'`, `'ai_agent'`. */
1023
1547
  no_agent_action: string;
1548
+ /** Target for `no_agent_action`. `null` when action is `'hangup'`. */
1024
1549
  no_agent_target: string | null;
1550
+ /** Action when caller times out (exceeds `max_wait_time`): `'voicemail'`, `'ivr'`, `'extension'`, `'hangup'`, `'ai_agent'`. */
1025
1551
  timeout_action: string;
1552
+ /** Target for `timeout_action`. `null` when action is `'hangup'`. */
1026
1553
  timeout_target: string | null;
1554
+ /** Queue status: `'active'` or `'disabled'`. */
1027
1555
  status: string;
1556
+ /** ISO 8601 timestamp of creation. */
1028
1557
  created_at: string;
1558
+ /** List of agents assigned to this queue. */
1029
1559
  agents: QueueAgent[];
1030
- /** Present when queried as global superadmin */
1560
+ /** Present when queried as global superadmin. */
1031
1561
  tenant?: TenantInfo;
1032
1562
  }
1033
1563
  export interface CreateQueueParams {
@@ -1050,18 +1580,43 @@ export interface CreateQueueParams {
1050
1580
  penalty?: number;
1051
1581
  }>;
1052
1582
  }
1583
+ /**
1584
+ * A webhook subscription that sends call events to an external URL.
1585
+ *
1586
+ * Webhooks fire on call lifecycle events (`call.started`, `call.ended`, etc.),
1587
+ * voicemail events, and extension registration changes.
1588
+ * Each delivery is retried up to `maxRetries` times with exponential backoff.
1589
+ *
1590
+ * @example
1591
+ * ```ts
1592
+ * const { webhooks } = await voip.listWebhooks();
1593
+ * webhooks.forEach(w => {
1594
+ * console.log(`${w.name} → ${w.url} [${w.events.join(',')}] ${w.active ? 'ACTIVE' : 'PAUSED'}`);
1595
+ * });
1596
+ * ```
1597
+ */
1053
1598
  export interface Webhook {
1599
+ /** Database UUID of the webhook subscription. */
1054
1600
  id: string;
1601
+ /** Human-readable name for this webhook. Example: `"CRM Call Logger"`. */
1055
1602
  name: string;
1603
+ /** Destination URL where HTTP POST requests are sent. Must be HTTPS. Example: `"https://my-crm.example.com/api/webhooks/voip"`. */
1056
1604
  url: string;
1605
+ /** Array of event types this webhook listens to. Example: `["call.started", "call.ended", "call.answered"]`. */
1057
1606
  events: string[];
1607
+ /** Whether this webhook is actively sending events. `false` means paused. */
1058
1608
  active: boolean;
1609
+ /** Whether a signing secret is configured. Always `false` in GET responses — the secret is never returned. */
1059
1610
  hasSecret: boolean;
1611
+ /** Custom HTTP headers included in each delivery. `null` if none. Example: `{"X-Custom-Auth": "token123"}`. */
1060
1612
  headers: Record<string, string> | null;
1613
+ /** Maximum retry attempts for failed deliveries. Default: `3`. After exhausting retries, the delivery is marked as failed. */
1061
1614
  maxRetries?: number;
1615
+ /** Base delay in seconds between retries (exponential backoff). Default: `10`. Delays: 10s, 20s, 40s, ... */
1062
1616
  retryDelaySeconds?: number;
1617
+ /** ISO 8601 timestamp of webhook creation. */
1063
1618
  createdAt: string;
1064
- /** Present when queried as global superadmin */
1619
+ /** Present when queried as global superadmin. */
1065
1620
  tenant?: TenantInfo;
1066
1621
  }
1067
1622
  export interface WebhookDelivery {
@@ -1075,13 +1630,36 @@ export interface WebhookDelivery {
1075
1630
  error: string | null;
1076
1631
  deliveredAt: string;
1077
1632
  }
1633
+ /**
1634
+ * Parameters for creating a new webhook subscription.
1635
+ *
1636
+ * At minimum, provide `name`, `url`, and `events`. The server validates the URL
1637
+ * is HTTPS (non-negotiable for security) and tests connectivity before saving.
1638
+ *
1639
+ * @example
1640
+ * ```ts
1641
+ * await voip.createWebhook({
1642
+ * name: 'CRM Call Logger',
1643
+ * url: 'https://my-crm.example.com/api/webhooks/voip',
1644
+ * events: ['call.started', 'call.answered', 'call.ended', 'voicemail.new'],
1645
+ * secret: 'whsec_my-signing-secret',
1646
+ * });
1647
+ * ```
1648
+ */
1078
1649
  export interface CreateWebhookParams {
1650
+ /** Human-readable name for this webhook. Example: `"CRM Call Logger"`. */
1079
1651
  name: string;
1652
+ /** Destination HTTPS URL. Must be reachable from the server. Example: `"https://my-crm.example.com/webhook"`. */
1080
1653
  url: string;
1654
+ /** Array of event types to subscribe to. Full list: see `WebhookEventType`. Example: `["call.started", "call.ended"]`. */
1081
1655
  events: string[];
1656
+ /** Signing secret for HMAC payload verification. The server includes `X-Webhook-Signature` header. Optional but recommended. */
1082
1657
  secret?: string;
1658
+ /** Custom HTTP headers to include in each delivery. Example: `{"X-API-Key": "mykey"}`. */
1083
1659
  headers?: Record<string, string>;
1660
+ /** Maximum retry attempts for failed deliveries. Default: `3`. */
1084
1661
  maxRetries?: number;
1662
+ /** Base delay in seconds between retries (exponential: delay * 2^attempt). Default: `10`. */
1085
1663
  retryDelaySeconds?: number;
1086
1664
  }
1087
1665
  /** Supported webhook event names */
@@ -1222,23 +1800,55 @@ export interface VoicemailCallInfo {
1222
1800
  audio_url: string | null;
1223
1801
  started_at: string | null;
1224
1802
  }
1803
+ /**
1804
+ * A voicemail message left by a caller.
1805
+ *
1806
+ * Returned by `listVoicemails()`. Each message includes the caller's info,
1807
+ * duration, read/urgent flags, an audio URL, and optionally a linked call record.
1808
+ *
1809
+ * ## Playback
1810
+ *
1811
+ * Use `audio_url` with `voip.buildUrl()` to append the API key for browser playback:
1812
+ * ```html
1813
+ * <audio src={voip.buildUrl(message.audio_url)} controls />
1814
+ * ```
1815
+ *
1816
+ * @example
1817
+ * ```ts
1818
+ * const { messages, unread } = await voip.listVoicemails({ unreadOnly: true });
1819
+ * messages.forEach(msg => {
1820
+ * console.log(`From ${msg.caller_id}: ${msg.duration_seconds}s ${msg.is_urgent ? 'URGENT' : ''}`);
1821
+ * });
1822
+ * ```
1823
+ */
1225
1824
  export interface VoicemailMessage {
1825
+ /** Database UUID of the voicemail message. */
1226
1826
  id: string;
1827
+ /** Tenant UUID this message belongs to. */
1227
1828
  tenant_id: string;
1829
+ /** Extension number that received the voicemail. Example: `"1001"`. */
1228
1830
  extension: string;
1831
+ /** Caller's phone number. Example: `"+17865551234"`. */
1229
1832
  caller_id: string;
1833
+ /** Caller's display name. `null` if not provided by carrier. Example: `"John Doe"`. */
1230
1834
  caller_name: string | null;
1835
+ /** Duration of the voicemail recording in seconds. Example: `42`. */
1231
1836
  duration_seconds: number;
1837
+ /** Whether the voicemail has been listened to. */
1232
1838
  is_read: boolean;
1839
+ /** Whether this voicemail was marked as urgent by the caller (pressed * during recording). */
1233
1840
  is_urgent: boolean;
1841
+ /** ISO 8601 timestamp when the voicemail was left. Example: `"2025-06-04T15:30:00.000Z"`. */
1234
1842
  created_at: string;
1843
+ /** ISO 8601 timestamp when the voicemail was read. `null` if unread. */
1235
1844
  read_at: string | null;
1236
- /** Direct URL to stream this voicemail's audio. Append `?apiKey=...` for browser playback. */
1845
+ /** Direct URL to stream this voicemail's audio. Append `?apiKey=...` for browser playback. Example: `"/api/voicemail/abc123/audio"`. */
1237
1846
  audio_url: string;
1238
- /** Linked call record with status, recording, etc. (null if no matching call found) */
1847
+ /** Linked call record with status, recording, etc. (null if no matching call found). Available since SDK 1.55.4. */
1239
1848
  call: VoicemailCallInfo | null;
1240
- /** Present when queried as global superadmin */
1849
+ /** Present when queried as global superadmin. Tenant company name. */
1241
1850
  company_name?: string;
1851
+ /** Present when queried as global superadmin. Tenant subdomain. */
1242
1852
  subdomain?: string;
1243
1853
  }
1244
1854
  export interface VoicemailListParams {
@@ -1318,11 +1928,28 @@ export interface PresenceDetail {
1318
1928
  extension?: string;
1319
1929
  }
1320
1930
  export type PresenceDetailMap = Record<string, PresenceDetail>;
1931
+ /**
1932
+ * Dashboard summary counters for the current tenant.
1933
+ *
1934
+ * Returned by `getDashboardStats()`. Use for summary cards/badges at the top
1935
+ * of a CRM dashboard showing: total extensions, active DIDs, calls today, and calls this month.
1936
+ *
1937
+ * @example
1938
+ * ```ts
1939
+ * const { dashboard } = await voip.getDashboardStats();
1940
+ * console.log(`Extensions: ${dashboard.extensions}, DIDs: ${dashboard.dids}`);
1941
+ * console.log(`Calls today: ${dashboard.callsToday}, This month: ${dashboard.callsThisMonth}`);
1942
+ * ```
1943
+ */
1321
1944
  export interface DashboardStats {
1322
1945
  dashboard: {
1946
+ /** Total number of extensions configured in this tenant. */
1323
1947
  extensions: number;
1948
+ /** Total number of active DID phone numbers. */
1324
1949
  dids: number;
1950
+ /** Number of calls made/received today (midnight to now). */
1325
1951
  callsToday: number;
1952
+ /** Number of calls made/received this calendar month. */
1326
1953
  callsThisMonth: number;
1327
1954
  };
1328
1955
  }
@@ -1344,19 +1971,46 @@ export interface TopExtension {
1344
1971
  export interface TopExtensionsResponse {
1345
1972
  topExtensions: TopExtension[];
1346
1973
  }
1974
+ /**
1975
+ * A blocked phone number in the tenant's blocklist.
1976
+ *
1977
+ * Calls to/from blocked numbers are automatically rejected by FreeSWITCH.
1978
+ * Works for both inbound and outbound directions.
1979
+ */
1347
1980
  export interface BlockedNumber {
1981
+ /** Database UUID of the blocklist entry. */
1348
1982
  id: string;
1983
+ /** Tenant UUID. */
1349
1984
  tenant_id: string;
1985
+ /** Blocked phone number in E.164 format. Example: `"+17865559999"`. */
1350
1986
  number: string;
1987
+ /** Reason for blocking. `null` if no reason was provided. Example: `"Spam caller"`. */
1351
1988
  reason: string | null;
1989
+ /** Direction this block applies to: `'inbound'`, `'outbound'`, `'both'`. `null` = `'both'`. */
1352
1990
  direction: string | null;
1991
+ /** ISO 8601 timestamp when this number was blocked. `null` for legacy entries. */
1353
1992
  created_at: string | null;
1354
- /** Present when queried as global superadmin */
1993
+ /** Present when queried as global superadmin. */
1355
1994
  tenants?: TenantInfo;
1356
1995
  }
1996
+ /**
1997
+ * Parameters for adding a number to the blocklist.
1998
+ *
1999
+ * @example
2000
+ * ```ts
2001
+ * await voip.blockNumber({
2002
+ * number: '+17865559999',
2003
+ * reason: 'Repeated spam calls',
2004
+ * direction: 'inbound',
2005
+ * });
2006
+ * ```
2007
+ */
1357
2008
  export interface CreateBlockedNumberParams {
2009
+ /** E.164 phone number to block. Example: `"+17865559999"`. */
1358
2010
  number: string;
2011
+ /** Optional reason (shown in dashboard). Example: `"Spam caller"`. */
1359
2012
  reason?: string;
2013
+ /** Which direction to block: `'inbound'` (calls FROM this number), `'outbound'` (calls TO this number), `'both'`. Default: `'both'`. */
1360
2014
  direction?: 'inbound' | 'outbound' | 'both';
1361
2015
  }
1362
2016
  export interface BlocklistCheckResponse {
@@ -1373,27 +2027,81 @@ export interface Holiday {
1373
2027
  date: string;
1374
2028
  name?: string;
1375
2029
  }
2030
+ /**
2031
+ * A business hours schedule for a tenant.
2032
+ *
2033
+ * Defines which days/times the business is open. When a call arrives outside
2034
+ * business hours, the `after_hours_action` is executed (e.g. send to voicemail).
2035
+ *
2036
+ * Includes weekly `schedules` (day of week + start/end time) and `holidays`
2037
+ * (specific dates when the business is closed regardless of weekly schedule).
2038
+ *
2039
+ * @example
2040
+ * ```ts
2041
+ * const { schedules } = await voip.listSchedules();
2042
+ * schedules.forEach(s => {
2043
+ * const status = await voip.checkScheduleStatus(s.id);
2044
+ * console.log(`${s.name}: ${status.isOpen ? 'OPEN' : 'CLOSED'}`);
2045
+ * });
2046
+ * ```
2047
+ */
1376
2048
  export interface BusinessSchedule {
2049
+ /** Database UUID of the schedule. */
1377
2050
  id: string;
2051
+ /** Tenant UUID. */
1378
2052
  tenant_id: string;
2053
+ /** Human-readable schedule name. Example: `"Standard Business Hours"`. */
1379
2054
  name: string;
1380
- /** Present when queried as global superadmin */
2055
+ /** Present when queried as global superadmin. */
1381
2056
  tenants?: TenantInfo;
2057
+ /** IANA timezone for this schedule. `null` = tenant default. Example: `"America/New_York"`. */
1382
2058
  timezone: string | null;
2059
+ /** Weekly day schedule array. Each entry defines open hours for one day. `null` if not configured. */
1383
2060
  schedules: DaySchedule[] | null;
2061
+ /** Holiday list — specific dates the business is closed. `null` if no holidays configured. */
1384
2062
  holidays: Holiday[] | null;
2063
+ /** Action when a call arrives outside business hours: `'voicemail'`, `'ivr'`, `'extension'`, `'hangup'`, `'external'`, `'ai_agent'`. `null` if not configured. */
1385
2064
  after_hours_action: string | null;
2065
+ /** Target for `after_hours_action`. `null` when action is `'hangup'` or not configured. */
1386
2066
  after_hours_target: string | null;
2067
+ /** Schedule status: `'active'` or `'disabled'`. `null` for legacy entries. */
1387
2068
  status: string | null;
2069
+ /** ISO 8601 timestamp of creation. `null` for legacy entries. */
1388
2070
  created_at: string | null;
2071
+ /** ISO 8601 timestamp of last update. `null` for legacy entries. */
1389
2072
  updated_at: string | null;
1390
2073
  }
2074
+ /**
2075
+ * Parameters for creating a business hours schedule.
2076
+ *
2077
+ * @example
2078
+ * ```ts
2079
+ * await voip.createSchedule({
2080
+ * name: 'Standard Business Hours',
2081
+ * timezone: 'America/New_York',
2082
+ * schedules: [
2083
+ * { day: 'monday', enabled: true, startTime: '09:00', endTime: '17:00' },
2084
+ * { day: 'tuesday', enabled: true, startTime: '09:00', endTime: '17:00' },
2085
+ * { day: 'saturday', enabled: false },
2086
+ * { day: 'sunday', enabled: false },
2087
+ * ],
2088
+ * afterHoursAction: 'voicemail',
2089
+ * afterHoursTarget: '1001',
2090
+ * });
2091
+ * ```
2092
+ */
1391
2093
  export interface CreateScheduleParams {
2094
+ /** Human-readable name. Example: `"Standard Business Hours"`. */
1392
2095
  name: string;
2096
+ /** IANA timezone. Default: tenant's configured timezone or `"America/New_York"`. */
1393
2097
  timezone?: string;
2098
+ /** Weekly day schedule array. Each entry specifies open hours for a single day. Example: `[{ day: 'monday', enabled: true, startTime: '09:00', endTime: '17:00' }]`. */
1394
2099
  schedules?: DaySchedule[];
2100
+ /** Specific dates the business is closed. Example: `[{ date: '2025-12-25', name: 'Christmas' }]`. */
1395
2101
  holidays?: Holiday[];
2102
+ /** Action when a call arrives outside business hours. Defaults to no special handling. */
1396
2103
  afterHoursAction?: 'voicemail' | 'ivr' | 'extension' | 'hangup' | 'external' | 'ai_agent';
2104
+ /** Target for `afterHoursAction` (extension number, IVR UUID, phone number, etc.). */
1397
2105
  afterHoursTarget?: string;
1398
2106
  }
1399
2107
  export interface ScheduleStatusResponse {
@@ -1402,15 +2110,104 @@ export interface ScheduleStatusResponse {
1402
2110
  timezone: string;
1403
2111
  currentTime: string;
1404
2112
  }
2113
+ /**
2114
+ * Live gateway status reported by FreeSWITCH for a SIP trunk.
2115
+ *
2116
+ * Included in `SipTrunk` responses when the gateway is registered and available.
2117
+ * Shows the current call count, failures, and uptime.
2118
+ */
1405
2119
  export interface GatewayStatus {
2120
+ /** Gateway state: `'UP'` or `'DOWN'`. */
1406
2121
  state: string;
2122
+ /** Registration status: `'REGED'` (registered), `'UNREG'` (unregistered), `'TRYING'`. */
1407
2123
  status: string;
2124
+ /** Number of active inbound calls on this trunk. */
1408
2125
  callsIn: number;
2126
+ /** Number of active outbound calls on this trunk. */
1409
2127
  callsOut: number;
2128
+ /** Cumulative failed inbound calls. */
1410
2129
  failedIn?: number;
2130
+ /** Cumulative failed outbound calls. */
1411
2131
  failedOut?: number;
2132
+ /** Human-readable uptime string (e.g. `"3d 12h 45m"`). */
1412
2133
  uptime?: string;
1413
2134
  }
2135
+ /**
2136
+ * A SIP trunk gateway connecting the platform to an external telephony provider
2137
+ * (Twilio, Telnyx, Vonage, etc.).
2138
+ *
2139
+ * SIP trunks handle PSTN connectivity — outbound calls go through trunks to reach
2140
+ * phone numbers; inbound calls from phone numbers arrive via trunks.
2141
+ *
2142
+ * Global trunks (`tenant_id = null`) are shared across all tenants.
2143
+ * Tenant-specific trunks are only usable by that tenant.
2144
+ */
2145
+ export interface SipTrunk {
2146
+ /** Database UUID of the trunk. */
2147
+ id: string;
2148
+ /** NULL for global trunks (shared, not tied to any tenant). */
2149
+ tenant_id: string | null;
2150
+ /** Present when queried as global superadmin. */
2151
+ tenants?: TenantInfo;
2152
+ /** Human-readable trunk name. Example: `"Twilio Primary"`. */
2153
+ name: string;
2154
+ /** Provider name: `'twilio'`, `'telnyx'`, `'vonage'`, etc. `null` for custom/unspecified. */
2155
+ provider: string | null;
2156
+ /** Gateway IP or hostname. Example: `"sip.twilio.com"`. */
2157
+ gateway: string;
2158
+ /** SIP username for registration/auth. `null` for IP-based auth trunks. */
2159
+ username: string | null;
2160
+ /** SIP password (masked in API responses — always `"****"` except on create). `null` for IP-auth. */
2161
+ password: string | null;
2162
+ /** Authentication type: `'ip'`, `'registration'`, `'both'`. `null` for legacy entries. */
2163
+ auth_type: string | null;
2164
+ /** Whether outbound calling is enabled through this trunk. `null` = disabled. */
2165
+ outbound_enabled: boolean | null;
2166
+ /** Whether inbound calling is accepted from this trunk. `null` = disabled. */
2167
+ inbound_enabled: boolean | null;
2168
+ /** Priority for outbound routing. Lower = preferred. Example: `10`. */
2169
+ priority: number | null;
2170
+ /** Maximum simultaneous channels. `null` = unlimited. */
2171
+ max_channels: number | null;
2172
+ /** Trunk status: `'active'` or `'inactive'`. `null` = `'inactive'`. */
2173
+ status: string | null;
2174
+ /** SIP realm for authentication. `null` = auto. */
2175
+ realm: string | null;
2176
+ /** Outbound proxy address. `null` = direct to gateway. */
2177
+ proxy: string | null;
2178
+ /** Whether FreeSWITCH registers with the trunk provider. `null` = false. */
2179
+ register: boolean | null;
2180
+ /** From: username for SIP requests. `null` = use `username`. */
2181
+ from_user: string | null;
2182
+ /** From: domain for SIP requests. `null` = use gateway. */
2183
+ from_domain: string | null;
2184
+ /** SIP transport protocol: `'udp'`, `'tcp'`, `'tls'`. `null` = `'udp'`. */
2185
+ transport: string | null;
2186
+ /** Preferred codec list (comma-separated). `null` = platform default. Example: `"PCMU,PCMA"`. */
2187
+ codec_prefs: string | null;
2188
+ /** Whether to include caller ID in From: header. `null` = true. */
2189
+ caller_id_in_from: boolean | null;
2190
+ /** SIP OPTIONS ping interval in seconds. `null` = no ping. */
2191
+ ping: number | null;
2192
+ /** Seconds between registration retries. `null` = default. */
2193
+ retry_seconds: number | null;
2194
+ /** Registration expiry in seconds. `null` = default (3600). */
2195
+ expire_seconds: number | null;
2196
+ /** Separate proxy for REGISTER requests. `null` = use `proxy` or `gateway`. */
2197
+ register_proxy: string | null;
2198
+ /** Extra SIP contact header parameters. `null` = none. */
2199
+ contact_params: string | null;
2200
+ /** Whether this is a global trunk (usable by all tenants). `null` = false. */
2201
+ is_global: boolean | null;
2202
+ /** FreeSWITCH gateway profile name. `null` = auto-generated from name. */
2203
+ gateway_name: string | null;
2204
+ /** Live gateway status from FreeSWITCH (when available). Shows state, calls, failures, uptime. */
2205
+ gatewayStatus?: GatewayStatus | null;
2206
+ /** ISO 8601 timestamp of creation. `null` for legacy entries. */
2207
+ created_at: string | null;
2208
+ /** ISO 8601 timestamp of last update. `null` for legacy entries. */
2209
+ updated_at: string | null;
2210
+ }
1414
2211
  export interface SipTrunk {
1415
2212
  id: string;
1416
2213
  /** NULL for global trunks (shared, not tied to any tenant) */
@@ -1475,21 +2272,48 @@ export interface CreateTrunkParams {
1475
2272
  contactParams?: string;
1476
2273
  isGlobal?: boolean;
1477
2274
  }
2275
+ /**
2276
+ * Real-time and today's statistics for a single call queue.
2277
+ *
2278
+ * Returned by `getQueueStats()`. Shows agent availability, today's call metrics,
2279
+ * and service level percentage. Use for queue wallboards and agent dashboards.
2280
+ *
2281
+ * @example
2282
+ * ```ts
2283
+ * const stats = await voip.getQueueStats('queue-uuid');
2284
+ * console.log(`${stats.queueName}: ${stats.today.answered}/${stats.today.totalCalls} answered`);
2285
+ * console.log(`Agents: ${stats.agents.available} available, ${stats.agents.busy} busy`);
2286
+ * ```
2287
+ */
1478
2288
  export interface QueueStats {
2289
+ /** Queue name. Example: `"Support Queue"`. */
1479
2290
  queueName: string;
2291
+ /** Distribution strategy. Example: `"longest-idle"`. */
1480
2292
  strategy: string;
2293
+ /** Agent availability breakdown. */
1481
2294
  agents: {
2295
+ /** Total number of agents assigned to this queue. */
1482
2296
  total: number;
2297
+ /** Agents currently available to take calls. */
1483
2298
  available: number;
2299
+ /** Agents who are paused (taking a break). */
1484
2300
  paused: number;
2301
+ /** Agents currently on a call. */
1485
2302
  busy: number;
2303
+ /** Agents who are logged out. */
1486
2304
  loggedOut: number;
1487
2305
  };
2306
+ /** Today's call statistics (from midnight). */
1488
2307
  today: {
2308
+ /** Total calls offered to the queue today. */
1489
2309
  totalCalls: number;
2310
+ /** Calls successfully answered by an agent today. */
1490
2311
  answered: number;
2312
+ /** Calls abandoned by the caller before reaching an agent. */
1491
2313
  abandoned: number;
2314
+ /** Service level: percentage of calls answered within the SLA (default 20 seconds). Range 0-100. */
1492
2315
  serviceLevel: number;
2316
+ /** Average call duration in seconds for answered calls today. */
1493
2317
  avgDuration: number;
1494
2318
  };
1495
2319
  }
@@ -1499,38 +2323,118 @@ export interface ExportCallsParams {
1499
2323
  direction?: 'inbound' | 'outbound' | 'internal' | 'all';
1500
2324
  limit?: number;
1501
2325
  }
2326
+ /**
2327
+ * SIP registration credentials for WebRTC softphone authentication.
2328
+ *
2329
+ * Returned by `getSipCredentials()` when creating an extension or manually.
2330
+ * Pass these to the `WebRTCPhone` constructor for SIP registration.
2331
+ *
2332
+ * @example
2333
+ * ```ts
2334
+ * const creds = await voip.getSipCredentials('1001');
2335
+ * const phone = new WebRTCPhone({
2336
+ * extension: creds.extension,
2337
+ * password: creds.password,
2338
+ * displayName: creds.displayName,
2339
+ * sipDomain: creds.sipDomain,
2340
+ * wsUri: creds.wsUri,
2341
+ * });
2342
+ * ```
2343
+ */
1502
2344
  export interface SipCredentials {
2345
+ /** Extension number used as SIP username. Example: `"1001"`. */
1503
2346
  extension: string;
2347
+ /** Display name for caller ID. Example: `"John Smith"`. */
1504
2348
  displayName: string;
2349
+ /** SIP password for registration. */
1505
2350
  password: string;
2351
+ /** SIP domain the phone should register to. Example: `"demo.sip.cemscale.com"`. */
1506
2352
  sipDomain: string;
2353
+ /** WebSocket URI for SIP signaling. Example: `"wss://sip.cemscale.com/ws"`. */
1507
2354
  wsUri: string;
2355
+ /** SIP registrar address (same as sipDomain but explicit for sip.js config). Example: `"sip.cemscale.com"`. */
1508
2356
  registrar: string;
1509
2357
  }
2358
+ /**
2359
+ * An API key for authenticating API requests.
2360
+ *
2361
+ * API keys are the recommended way to authenticate SDK requests in production
2362
+ * (server-side integrations, CRMs, automation scripts). They never expire by
2363
+ * default and have role-based scopes.
2364
+ *
2365
+ * **CRITICAL:** The full key is only returned on `createApiKey()` and
2366
+ * `regenerateApiKey()` responses. After that, only `keyPreview` is shown.
2367
+ * Store the full key securely — it cannot be retrieved again.
2368
+ *
2369
+ * @example
2370
+ * ```ts
2371
+ * const { apiKey } = await voip.createApiKey({
2372
+ * name: 'CRM Integration',
2373
+ * role: 'admin',
2374
+ * });
2375
+ * console.log(apiKey.key); // "csk_live_abc123def456..." — STORE THIS NOW
2376
+ * console.log(apiKey.keyPreview); // "csk_live_••••••••def456"
2377
+ * ```
2378
+ */
1510
2379
  export interface ApiKey {
2380
+ /** Database UUID of the API key. */
1511
2381
  id: string;
2382
+ /** Human-readable name for this key. Example: `"CRM Integration"`. */
1512
2383
  name: string;
1513
- /** Full raw key — only present on create and regenerate responses */
2384
+ /** Full raw key — only present on create and regenerate responses. Format: `"csk_live_..."`. **Store securely.** Will be `undefined` in list/get responses. */
1514
2385
  key?: string;
1515
- /** Masked preview: csk_live_••••••••abcd1234 */
2386
+ /** Masked preview: csk_live_••••••••abcd1234. Safe to display in UI. Always present. */
1516
2387
  keyPreview: string;
2388
+ /** Role determining permissions: `'admin'`, `'readonly'`, `'user'`, `'superadmin'`. */
1517
2389
  role: string;
2390
+ /** API permission scopes. Example: `["read:calls", "write:extensions"]`. */
1518
2391
  scopes: string[];
2392
+ /** ISO 8601 timestamp of last usage. `null` if never used. */
1519
2393
  lastUsedAt: string | null;
2394
+ /** ISO 8601 timestamp when this key expires. `null` if never expires. */
1520
2395
  expiresAt: string | null;
2396
+ /** Key status: `'active'` or `'revoked'`. */
1521
2397
  status: string;
2398
+ /** Who created this key (email or user ID). `null` for legacy keys. */
1522
2399
  createdBy: string | null;
2400
+ /** ISO 8601 timestamp of creation. `null` for legacy keys. */
1523
2401
  createdAt: string | null;
2402
+ /** ISO 8601 timestamp when this key was revoked. `null` if active. */
1524
2403
  revokedAt?: string | null;
1525
2404
  }
2405
+ /**
2406
+ * Parameters for creating a new API key.
2407
+ *
2408
+ * At minimum, provide a `name`. The role defaults to `'admin'`.
2409
+ * Use `scopes` for fine-grained permission control.
2410
+ *
2411
+ * Superadmin-only fields (`global`, `tenantId`) allow creating keys
2412
+ * that span tenants or target specific tenants.
2413
+ *
2414
+ * @example
2415
+ * ```ts
2416
+ * // Admin key with full CRUD
2417
+ * const { apiKey } = await voip.createApiKey({ name: 'CRM Admin', role: 'admin' });
2418
+ *
2419
+ * // Read-only key for reporting
2420
+ * const { apiKey } = await voip.createApiKey({ name: 'Reports', role: 'readonly' });
2421
+ *
2422
+ * // Superadmin: create global key for all tenants
2423
+ * const { apiKey } = await voip.createApiKey({ name: 'Global Ops', role: 'superadmin', global: true });
2424
+ * ```
2425
+ */
1526
2426
  export interface CreateApiKeyParams {
2427
+ /** Human-readable name. Example: `"CRM Integration"`. */
1527
2428
  name: string;
2429
+ /** Role for permission level: `'admin'` (full CRUD), `'readonly'` (GET only), `'user'` (limited), `'superadmin'` (full PBX). Default: `'admin'`. */
1528
2430
  role?: 'admin' | 'readonly' | 'user' | 'superadmin';
2431
+ /** API permission scopes for fine-grained control. Example: `["read:calls", "write:extensions", "read:recordings"]`. */
1529
2432
  scopes?: string[];
2433
+ /** ISO 8601 expiration date. `null` (omit) = never expires. Example: `"2026-12-31T23:59:59Z"`. */
1530
2434
  expiresAt?: string;
1531
- /** Superadmin only: create a global API key with no tenant (full PBX control) */
2435
+ /** Superadmin only: create a global API key with no tenant (full PBX control). */
1532
2436
  global?: boolean;
1533
- /** Superadmin only: specify which tenant this key belongs to */
2437
+ /** Superadmin only: specify which tenant this key belongs to. */
1534
2438
  tenantId?: string;
1535
2439
  }
1536
2440
  export interface UpdateApiKeyParams {
@@ -1592,17 +2496,201 @@ export interface VoIPClientConfig {
1592
2496
  /** Request timeout in ms (default 15000) */
1593
2497
  timeout?: number;
1594
2498
  }
2499
+ /**
2500
+ * A participant in a three-way conference.
2501
+ *
2502
+ * Each participant is identified by their FreeSWITCH channel UUID (`uuid`)
2503
+ * and their conference member ID (`memberId`). Use `memberId` for mute/kick
2504
+ * operations, and `uuid` for swap/merge operations.
2505
+ *
2506
+ * **Status:** `'active'` = in conference and talking. `'held'` = kicked out
2507
+ * of the conference and placed on hold (hears music). Swapping toggles these.
2508
+ */
1595
2509
  export interface ThreeWayParticipant {
1596
- /** FreeSWITCH channel UUID of this leg */
2510
+ /** FreeSWITCH channel UUID of this leg. Use for swap operations (`keepUuid`/`holdUuid` in `swapParticipant()`). Example: `"a1b2c3d4-..."`. */
1597
2511
  uuid: string;
1598
- /** FreeSWITCH conference member ID (needed for kick/mute) */
2512
+ /** FreeSWITCH conference member ID (integer). Use for mute/kick operations within the conference. Example: `"1"`. */
1599
2513
  memberId: string;
2514
+ /** Caller ID number of this participant. Example: `"+17865551234"` or `"1002"`. */
1600
2515
  callerIdNumber: string;
2516
+ /** Caller ID name of this participant. Example: `"John Smith"`. */
1601
2517
  callerIdName: string;
2518
+ /** Whether this participant is muted in the conference. */
1602
2519
  muted: boolean;
1603
- /** 'active' = in conference, 'held' = kicked out and on hold */
2520
+ /** `'active'` = in conference, `'held'` = kicked out and on hold (hears music). */
1604
2521
  status: 'active' | 'held';
1605
2522
  }
2523
+ /**
2524
+ * State machine for a three-way call session.
2525
+ *
2526
+ * ## Lifecycle
2527
+ *
2528
+ * ```
2529
+ * [Call A active] → addCallParticipant(...)
2530
+ * → adding (hold A, dialing B, waiting for answer)
2531
+ * → [B answers] → mergeCalls(...)
2532
+ * → active (all three in conference)
2533
+ * → swapParticipant(keep, hold) → swapping → active
2534
+ * → kickParticipant(...)
2535
+ * → [caller hangs up] → conference auto-destroys
2536
+ * ```
2537
+ *
2538
+ * ## Key fields
2539
+ *
2540
+ * - `conferenceName`: The FreeSWITCH conference room name. Needed for swap/kick operations.
2541
+ * - `state`: Current phase of the three-way call.
2542
+ * - `pendingUuid`: UUID of Person B's leg while dialing (before merge). `null` after merge.
2543
+ * - `originalBridgedLeg`: UUID of Person A's PSTN bridge leg. Needed to merge correctly.
2544
+ *
2545
+ * @example
2546
+ * ```ts
2547
+ * // Listen for three-way session state
2548
+ * voip.on('threeWayUpdate', (session: ThreeWaySession) => {
2549
+ * if (session.state === 'active') {
2550
+ * console.log('Three-way conference active:', session.conferenceName);
2551
+ * session.participants.forEach(p => {
2552
+ * console.log(` ${p.callerIdNumber} (${p.status}) muted=${p.muted}`);
2553
+ * });
2554
+ * }
2555
+ * });
2556
+ * ```
2557
+ */
2558
+ export interface ThreeWaySession {
2559
+ /** FreeSWITCH conference room name. Pass to `swapParticipant()`, `kickParticipant()`, `endConference()`. Example: `"conf_abc123"`. */
2560
+ conferenceName: string;
2561
+ /** Current state of the three-way session. Use for UI state: show "Adding participant..." during `'adding'`, show participant list during `'active'`. */
2562
+ state: ThreeWayState;
2563
+ /** List of participants currently in the conference. */
2564
+ participants: ThreeWayParticipant[];
2565
+ /** UUID of the second call leg while dialing (before merge). `null` after successful merge or if no pending dial. */
2566
+ pendingUuid?: string;
2567
+ /** UUID of the bridged PSTN leg of the original call. Required for correct merge — returned by `addCallParticipant()`. `null` if the original call had no bridged PSTN leg. */
2568
+ originalBridgedLeg?: string;
2569
+ }
2570
+ /**
2571
+ * Parameters for adding a participant to an active call (first step of three-way calling).
2572
+ *
2573
+ * Provide either `toNumber` (external PSTN) or `toExtension` (internal extension),
2574
+ * but not both. The active call is placed on hold while dialing the new party.
2575
+ *
2576
+ * On success, you receive `newCallUuid` (the new leg's UUID) and `originalBridgedLeg`
2577
+ * (the original call's bridged PSTN leg UUID). Pass both to `mergeCalls()`.
2578
+ *
2579
+ * @example
2580
+ * ```ts
2581
+ * // Add external participant
2582
+ * const { newCallUuid, originalBridgedLeg } = await voip.addCallParticipant(callUuid, {
2583
+ * toNumber: '+17865559999',
2584
+ * });
2585
+ *
2586
+ * // Add internal extension
2587
+ * const { newCallUuid } = await voip.addCallParticipant(callUuid, {
2588
+ * toExtension: '1002',
2589
+ * });
2590
+ * ```
2591
+ */
2592
+ export interface AddParticipantParams {
2593
+ /** PSTN number to call in E.164 format. Example: `"+17865559999"`. */
2594
+ toNumber?: string;
2595
+ /** Internal extension to add. Example: `"1002"`. */
2596
+ toExtension?: string;
2597
+ }
2598
+ /**
2599
+ * Response from `addCallParticipant()`.
2600
+ *
2601
+ * Use `newCallUuid` and `originalBridgedLeg` in the subsequent `mergeCalls()` call
2602
+ * to join all three parties into a conference.
2603
+ */
2604
+ export interface AddParticipantResponse {
2605
+ /** Human-readable message. Example: `"Second call (UUID xxx) established — ready to merge"`. */
2606
+ message: string;
2607
+ /** FreeSWITCH channel UUID of the new call leg (Person B). Pass as `callUuidB` to `mergeCalls()`. */
2608
+ newCallUuid: string;
2609
+ /** FreeSWITCH channel UUID of the original call's bridged PSTN leg. Pass as `bridgedLegA` to `mergeCalls()` for correct merging. */
2610
+ originalBridgedLeg: string;
2611
+ }
2612
+ /**
2613
+ * Parameters for merging two calls into a three-way conference (second step).
2614
+ *
2615
+ * After `addCallParticipant()` succeeds, call `mergeCalls()` with the UUIDs to
2616
+ * join Person A, Person B, and the original PSTN caller into one conference.
2617
+ *
2618
+ * @example
2619
+ * ```ts
2620
+ * const result = await voip.mergeCalls({
2621
+ * callUuidA: activeCall.uuid, // User's WebRTC session
2622
+ * callUuidB: addResult.newCallUuid, // New call leg from addCallParticipant
2623
+ * bridgedLegA: addResult.originalBridgedLeg, // Original PSTN leg
2624
+ * });
2625
+ * console.log('Conference created:', result.conferenceName);
2626
+ * ```
2627
+ */
2628
+ export interface MergeCallsParams {
2629
+ /** UUID of the user's WebRTC session (first call). Use `WebRTCCallInfo.fsChannelUuid`. */
2630
+ callUuidA: string;
2631
+ /** UUID returned by addCallParticipant (Person B's direct channel). */
2632
+ callUuidB: string;
2633
+ /**
2634
+ * Person A's PSTN leg UUID — returned as `originalBridgedLeg` by addCallParticipant.
2635
+ * Passing this avoids an extra ESL lookup and ensures the correct leg is used.
2636
+ */
2637
+ bridgedLegA?: string;
2638
+ /** Optional custom conference name — auto-generated if omitted. */
2639
+ conferenceName?: string;
2640
+ }
2641
+ /**
2642
+ * Response from `mergeCalls()`.
2643
+ *
2644
+ * Contains the conference name (needed for swap/kick/end operations) and the
2645
+ * full participant list with UUIDs, caller IDs, and mute/held status.
2646
+ */
2647
+ export interface MergeCallsResponse {
2648
+ /** Human-readable message. Example: `"Calls merged into conference conf_abc123"`. */
2649
+ message: string;
2650
+ /** FreeSWITCH conference room name. Store this for subsequent swap/kick/end operations. */
2651
+ conferenceName: string;
2652
+ /** List of all participants now in the conference. Use for rendering participant cards in the UI. */
2653
+ participants: ThreeWayParticipant[];
2654
+ }
2655
+ /**
2656
+ * Parameters for directly merging two active WebRTC calls (no PSTN party).
2657
+ *
2658
+ * Use this when the user has two WebRTC calls active simultaneously (e.g.
2659
+ * call on line 1 and line 2) and wants to merge them directly.
2660
+ *
2661
+ * Unlike `addCallParticipant` → `mergeCalls`, this doesn't need a bridged leg UUID
2662
+ * because both calls are WebRTC (no PSTN bridge).
2663
+ *
2664
+ * @example
2665
+ * ```ts
2666
+ * const result = await voip.mergeDirectCalls({
2667
+ * callUuidA: call1.fsChannelUuid,
2668
+ * callUuidB: call2.fsChannelUuid,
2669
+ * });
2670
+ * ```
2671
+ */
2672
+ export interface MergeDirectParams {
2673
+ /** UUID of the first call (WebRTC leg). Use `WebRTCCallInfo.fsChannelUuid`. */
2674
+ callUuidA: string;
2675
+ /** UUID of the second call to merge (WebRTC leg). Use `WebRTCCallInfo.fsChannelUuid`. */
2676
+ callUuidB: string;
2677
+ /** Optional custom conference name — auto-generated if omitted. */
2678
+ conferenceName?: string;
2679
+ }
2680
+ /**
2681
+ * Response from `mergeDirectCalls()`.
2682
+ *
2683
+ * Same structure as `MergeCallsResponse`. All participants are now in the
2684
+ * named conference and can be swapped/kicked independently.
2685
+ */
2686
+ export interface MergeDirectResponse {
2687
+ /** Human-readable message. Example: `"Calls merged into conference conf_abc123"`. */
2688
+ message: string;
2689
+ /** FreeSWITCH conference room name. Store for subsequent operations. */
2690
+ conferenceName: string;
2691
+ /** List of all participants now in the conference. */
2692
+ participants: ThreeWayParticipant[];
2693
+ }
1606
2694
  export type ThreeWayState = 'adding' | 'active' | 'swapping';
1607
2695
  export interface ThreeWaySession {
1608
2696
  conferenceName: string;
@@ -1725,7 +2813,25 @@ export interface AiAgentListResponse {
1725
2813
  agents: AiAgent[];
1726
2814
  total: number;
1727
2815
  }
2816
+ /**
2817
+ * Response containing a single AI agent.
2818
+ *
2819
+ * Returned by `createAiAgent()`, `getAiAgent()`, and `updateAiAgent()`.
2820
+ * The `agent` field contains the full agent configuration from the AI Bridge.
2821
+ *
2822
+ * @example
2823
+ * ```ts
2824
+ * const { agent } = await voip.createAiAgent({
2825
+ * agent_id: 'acme-sales',
2826
+ * display_name: 'Sofia',
2827
+ * voice: 'Aoede',
2828
+ * tools: ['transfer_to_human', 'end_call'],
2829
+ * });
2830
+ * console.log(`Agent "${agent.display_name}" (${agent.agent_id}) created`);
2831
+ * ```
2832
+ */
1728
2833
  export interface AiAgentResponse {
2834
+ /** The full AI agent object from the AI Bridge. */
1729
2835
  agent: AiAgent;
1730
2836
  }
1731
2837
  export interface AiBridgeHealthResponse {
@@ -1905,32 +3011,81 @@ export interface AiCallHistoryResponse {
1905
3011
  calls: AiCallRecord[];
1906
3012
  total: number;
1907
3013
  }
3014
+ /**
3015
+ * A paging group for one-way audio broadcast (intercom/PA system).
3016
+ *
3017
+ * When someone dials `extension_code` (e.g. `*801`), all online members auto-answer
3018
+ * and hear the caller through their speaker. This is a one-way broadcast — members
3019
+ * hear the caller but cannot speak back.
3020
+ *
3021
+ * @example
3022
+ * ```ts
3023
+ * const { pagingGroups } = await voip.listPagingGroups();
3024
+ * pagingGroups.forEach(pg => {
3025
+ * console.log(`${pg.name} (${pg.extension_code}): ${pg.members.length} members`);
3026
+ * });
3027
+ * ```
3028
+ */
1908
3029
  export interface PagingGroup {
3030
+ /** Database UUID of the paging group. */
1909
3031
  id: string;
3032
+ /** Tenant UUID. */
1910
3033
  tenant_id: string;
3034
+ /** Human-readable name. Example: `"All users"`. */
1911
3035
  name: string;
3036
+ /** Optional description. `null` if not set. */
1912
3037
  description: string | null;
3038
+ /** Dial code to trigger paging (e.g. `"*801"`). Must start with `*8`. Unique per tenant. */
1913
3039
  extension_code: string;
3040
+ /** Group status: `'active'` or `'disabled'`. */
1914
3041
  status: string;
3042
+ /** ISO 8601 timestamp of creation. */
1915
3043
  created_at: string;
3044
+ /** ISO 8601 timestamp of last update. */
1916
3045
  updated_at: string;
3046
+ /** List of extension numbers that are members of this paging group. */
1917
3047
  members: PagingGroupMember[];
3048
+ /** Tenant summary — present when queried as global superadmin. */
1918
3049
  tenant?: {
1919
3050
  id: string;
1920
3051
  company_name: string;
1921
3052
  subdomain: string;
1922
3053
  };
1923
3054
  }
3055
+ /**
3056
+ * A member extension within a paging group.
3057
+ */
1924
3058
  export interface PagingGroupMember {
3059
+ /** Database UUID of this member entry. */
1925
3060
  id: string;
3061
+ /** Parent paging group UUID. */
1926
3062
  paging_group_id: string;
3063
+ /** Extension number. Example: `"1001"`. */
1927
3064
  extension: string;
3065
+ /** ISO 8601 timestamp when this member was added. */
1928
3066
  created_at: string;
1929
3067
  }
3068
+ /**
3069
+ * Parameters for creating a new paging group with members.
3070
+ *
3071
+ * @example
3072
+ * ```ts
3073
+ * await voip.createPagingGroup({
3074
+ * name: 'Sales Floor',
3075
+ * description: 'Broadcast to all sales agents',
3076
+ * extensionCode: '*802',
3077
+ * members: [{ extension: '1001' }, { extension: '1002' }, { extension: '1005' }],
3078
+ * });
3079
+ * ```
3080
+ */
1930
3081
  export interface CreatePagingGroupParams {
3082
+ /** Human-readable group name. Example: `"Sales Floor"`. Must be unique within the tenant. */
1931
3083
  name: string;
3084
+ /** Optional description of the group's purpose. */
1932
3085
  description?: string;
3086
+ /** Dial code to activate paging (e.g. `"*802"`). Must start with `*8` and be unique within the tenant. */
1933
3087
  extensionCode: string;
3088
+ /** Array of extensions to include as members. Each entry requires `extension` field. */
1934
3089
  members: {
1935
3090
  extension: string;
1936
3091
  }[];