@unboundcx/sdk 4.13.50 → 4.13.53

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/base.js CHANGED
@@ -387,8 +387,15 @@ export class BaseSDK {
387
387
 
388
388
  // Add query parameters
389
389
  if (query) {
390
- const params = new URLSearchParams(query).toString();
391
- url += `?${params}`;
390
+ // Drop undefined/null so optional args don't serialize as the literal
391
+ // string "undefined" (URLSearchParams would send limit=undefined).
392
+ const cleanQuery = Object.fromEntries(
393
+ Object.entries(query).filter(
394
+ ([, v]) => v !== undefined && v !== null,
395
+ ),
396
+ );
397
+ const params = new URLSearchParams(cleanQuery).toString();
398
+ if (params) url += `?${params}`;
392
399
  }
393
400
 
394
401
  // Handle body
package/index.js CHANGED
@@ -74,10 +74,17 @@ class UnboundSDK extends BaseSDK {
74
74
  }
75
75
  } else {
76
76
  // New object-based parameters
77
- const { namespace, callId, token, fwRequestId, url, socketStore } =
78
- options;
77
+ const {
78
+ namespace,
79
+ callId,
80
+ token,
81
+ fwRequestId,
82
+ url,
83
+ socketStore,
84
+ baseURL,
85
+ } = options;
79
86
 
80
- super({ namespace, callId, token, fwRequestId });
87
+ super({ namespace, callId, token, fwRequestId, baseURL });
81
88
 
82
89
  // Handle client-side specific parameters
83
90
  if (url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.13.50",
3
+ "version": "4.13.53",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -642,6 +642,7 @@ export class EmailService {
642
642
  limit = 25,
643
643
  offset = 0,
644
644
  assignedUserId,
645
+ includeTasked = false,
645
646
  } = {},
646
647
  ) {
647
648
  this.sdk.validateParams(
@@ -655,6 +656,7 @@ export class EmailService {
655
656
  limit,
656
657
  offset,
657
658
  assignedUserId,
659
+ includeTasked,
658
660
  },
659
661
  {
660
662
  mailboxId: { type: 'string', required: true },
@@ -666,12 +668,14 @@ export class EmailService {
666
668
  limit: { type: 'number', required: false },
667
669
  offset: { type: 'number', required: false },
668
670
  assignedUserId: { type: 'string', required: false },
671
+ includeTasked: { type: 'boolean', required: false },
669
672
  },
670
673
  );
671
674
 
672
675
  const query = { folder, includeDrafts, sortBy, sortOrder, limit, offset };
673
676
  if (search) query.search = search;
674
677
  if (assignedUserId) query.assignedUserId = assignedUserId;
678
+ if (includeTasked) query.includeTasked = true;
675
679
 
676
680
  const params = {
677
681
  query,
@@ -1291,6 +1291,81 @@ export class ObjectsService {
1291
1291
  });
1292
1292
  }
1293
1293
 
1294
+ /**
1295
+ * Engagement-session participant roster (requester + durable CCs).
1296
+ * `recordId` is an engagement session id.
1297
+ *
1298
+ * GET /object/:id/participants
1299
+ *
1300
+ * @param {string} recordId
1301
+ * @returns {Promise<{participants: Array<{
1302
+ * id: string,
1303
+ * engagementSessionId: string,
1304
+ * role: string,
1305
+ * email: string|null,
1306
+ * peopleId: string|null,
1307
+ * displayName: string|null,
1308
+ * addedBy: {id: string, name: string|null}|null,
1309
+ * createdAt: string
1310
+ * }>}>}
1311
+ */
1312
+ async listParticipants(recordId) {
1313
+ this.sdk.validateParams(
1314
+ { recordId },
1315
+ { recordId: { type: 'string', required: true } },
1316
+ );
1317
+ return internalRequest(
1318
+ this.sdk,
1319
+ `/object/${recordId}/participants`,
1320
+ 'GET',
1321
+ );
1322
+ }
1323
+
1324
+ /**
1325
+ * Add a durable CC on an engagement session (PR5).
1326
+ * PUT /object/:id/participants
1327
+ *
1328
+ * @param {string} recordId engagement session id
1329
+ * @param {{email: string, displayName?: string, peopleId?: string}} body
1330
+ */
1331
+ async addParticipant(recordId, body = {}) {
1332
+ this.sdk.validateParams(
1333
+ { recordId, email: body.email },
1334
+ {
1335
+ recordId: { type: 'string', required: true },
1336
+ email: { type: 'string', required: true },
1337
+ },
1338
+ );
1339
+ return internalRequest(
1340
+ this.sdk,
1341
+ `/object/${recordId}/participants`,
1342
+ 'PUT',
1343
+ { body },
1344
+ );
1345
+ }
1346
+
1347
+ /**
1348
+ * Remove a durable CC from an engagement session (PR5).
1349
+ * DELETE /object/:id/participants/:participantId
1350
+ *
1351
+ * @param {string} recordId engagement session id
1352
+ * @param {string} participantId
1353
+ */
1354
+ async removeParticipant(recordId, participantId) {
1355
+ this.sdk.validateParams(
1356
+ { recordId, participantId },
1357
+ {
1358
+ recordId: { type: 'string', required: true },
1359
+ participantId: { type: 'string', required: true },
1360
+ },
1361
+ );
1362
+ return internalRequest(
1363
+ this.sdk,
1364
+ `/object/${recordId}/participants/${participantId}`,
1365
+ 'DELETE',
1366
+ );
1367
+ }
1368
+
1294
1369
  /**
1295
1370
  * Marketing programs dashboard (programs + totals).
1296
1371
  * @returns {Promise<object>}
@@ -23,6 +23,9 @@ export class TaskService {
23
23
  * @param {boolean} [options.createEngagement=false] - Whether to automatically create an engagement session for this task
24
24
  * @param {string} [options.relatedObject] - Related object type for metadata tracking (automatically set if createEngagement is true)
25
25
  * @param {string} [options.relatedId] - Related object ID for metadata tracking (automatically set if createEngagement is true)
26
+ * @param {string} [options.parentTaskId] - Parent task id (wrapUp follow-up / requeue)
27
+ * @param {string} [options.preferredWorkerId] - Preferred worker id persisted on create INSERT
28
+ * @param {string} [options.source] - Engagement `source` when `createEngagement` is true (e.g. `'portal'`)
26
29
  * @param {Object} [options.metadata] - Arbitrary metadata to attach to the task at creation (e.g. `{ textConversationId }`). Passed through as-is; whether it is persisted depends on the receiving endpoint honoring `metadata` in the request body.
27
30
  * @returns {Promise<Object>} Object containing the created task information
28
31
  * @returns {string} result.id - The unique identifier for the created task
@@ -84,7 +87,11 @@ export class TaskService {
84
87
  cdrId,
85
88
  sipCallId,
86
89
  aiChatSessionId,
90
+ parentTaskId,
91
+ preferredWorkerId,
92
+ isRoutable,
87
93
  metadata,
94
+ source,
88
95
  } = options;
89
96
 
90
97
  this.sdk.validateParams(
@@ -104,7 +111,11 @@ export class TaskService {
104
111
  relatedId,
105
112
  sipCallId,
106
113
  aiChatSessionId,
114
+ parentTaskId,
115
+ preferredWorkerId,
116
+ isRoutable,
107
117
  metadata,
118
+ source,
108
119
  },
109
120
  {
110
121
  type: { type: 'string', required: true },
@@ -122,7 +133,11 @@ export class TaskService {
122
133
  relatedId: { type: 'string', required: false },
123
134
  sipCallId: { type: 'string', required: false },
124
135
  aiChatSessionId: { type: 'string', required: false },
136
+ parentTaskId: { type: 'string', required: false },
137
+ preferredWorkerId: { type: 'string', required: false },
138
+ isRoutable: { type: 'boolean', required: false },
125
139
  metadata: { type: 'object', required: false },
140
+ source: { type: 'string', required: false },
126
141
  },
127
142
  );
128
143
 
@@ -185,10 +200,26 @@ export class TaskService {
185
200
  params.body.aiChatSessionId = aiChatSessionId;
186
201
  }
187
202
 
203
+ if (parentTaskId !== undefined) {
204
+ params.body.parentTaskId = parentTaskId;
205
+ }
206
+
207
+ if (preferredWorkerId !== undefined) {
208
+ params.body.preferredWorkerId = preferredWorkerId;
209
+ }
210
+
211
+ if (isRoutable !== undefined) {
212
+ params.body.isRoutable = isRoutable;
213
+ }
214
+
188
215
  if (metadata !== undefined) {
189
216
  params.body.metadata = metadata;
190
217
  }
191
218
 
219
+ if (source !== undefined) {
220
+ params.body.source = source;
221
+ }
222
+
192
223
  const result = await internalRequest(this.sdk, '/taskRouter/tasks', 'POST', params);
193
224
  return result;
194
225
  }
@@ -444,44 +475,53 @@ export class TaskService {
444
475
  }
445
476
 
446
477
  /**
447
- * Toggle task hold status
478
+ * Toggle or set task hold status
448
479
  * Place a connected task on hold or resume a held task.
449
- * If the task is currently 'connected', it will be set to 'hold'.
450
- * If the task is currently 'hold', it will be set back to 'connected'.
480
+ * If `held` is omitted, the current status is toggled: 'connected' -> 'hold'
481
+ * and 'hold' -> 'connected'. If `held` is a boolean, it explicitly sets the
482
+ * target status: `true` -> 'hold', `false` -> 'connected'.
451
483
  *
452
484
  * @param {Object} options - Parameters
453
485
  * @param {string} options.taskId - The task ID to hold/resume (required)
486
+ * @param {boolean} [options.held] - Explicit target hold state; omit for legacy toggle behavior
454
487
  * @returns {Promise<Object>} Object containing the task ID and new status
455
488
  * @returns {string} result.taskId - The task ID that was modified
456
489
  * @returns {string} result.status - The new status ('hold' or 'connected')
490
+ * @returns {boolean} result.changed - Whether the status actually changed
457
491
  *
458
492
  * @example
459
493
  * // Put a connected task on hold
460
- * const result = await sdk.taskRouter.task.hold({ taskId: 'task123' });
494
+ * const result = await sdk.taskRouter.task.hold({ taskId: 'task123', held: true });
461
495
  * console.log(result.status); // "hold"
462
496
  *
463
497
  * @example
464
498
  * // Resume a held task
465
- * const result = await sdk.taskRouter.task.hold({ taskId: 'task123' });
499
+ * const result = await sdk.taskRouter.task.hold({ taskId: 'task123', held: false });
466
500
  * console.log(result.status); // "connected"
501
+ *
502
+ * @example
503
+ * // Legacy toggle (no `held`)
504
+ * const result = await sdk.taskRouter.task.hold({ taskId: 'task123' });
467
505
  */
468
506
  async hold(options = {}) {
469
- const { taskId } = options;
507
+ const { taskId, held } = options;
470
508
 
471
509
  this.sdk.validateParams(
472
- { taskId },
510
+ { taskId, held },
473
511
  {
474
512
  taskId: { type: 'string', required: true },
513
+ held: { type: 'boolean', required: false },
475
514
  },
476
515
  );
477
516
 
478
517
  const params = {
479
518
  body: {
480
519
  taskId,
520
+ ...(typeof held === 'boolean' && { held }),
481
521
  },
482
522
  };
483
523
 
484
- const result = await internalRequest(this.sdk,
524
+ const result = await internalRequest(this.sdk,
485
525
  '/taskRouter/tasks/hold',
486
526
  'PUT',
487
527
  params,
@@ -880,6 +920,92 @@ export class TaskService {
880
920
  );
881
921
  }
882
922
 
923
+ /**
924
+ * Resume a parked task ("customer is back"). Flips the task back to
925
+ * pending so the distributor re-offers it; preferredWorkerId (stamped by
926
+ * park) is left alone so the parking worker gets first refusal.
927
+ *
928
+ * PUT /taskRouter/tasks/unpark
929
+ *
930
+ * @param {Object} options - Parameters
931
+ * @param {string} options.taskId - The parked task ID (required)
932
+ * @returns {Promise<Object>} { taskId, status: 'pending' }
933
+ */
934
+ async unpark(options = {}) {
935
+ const { taskId } = options;
936
+
937
+ this.sdk.validateParams(
938
+ { taskId },
939
+ { taskId: { type: 'string', required: true } },
940
+ );
941
+
942
+ const params = { body: { taskId } };
943
+
944
+ return await internalRequest(
945
+ this.sdk,
946
+ '/taskRouter/tasks/unpark',
947
+ 'PUT',
948
+ params,
949
+ );
950
+ }
951
+
952
+ /**
953
+ * Public reply on a ticket task: writes the customer-visible post and
954
+ * emails requester + CCs (transactional) from the queue mailbox.
955
+ *
956
+ * POST /taskRouter/tasks/:id/public-reply
957
+ *
958
+ * @param {Object} options
959
+ * @param {string} options.taskId
960
+ * @param {string} [options.html]
961
+ * @param {string} [options.text]
962
+ * @param {string|string[]} [options.extraTo]
963
+ * @param {string|string[]} [options.extraCc]
964
+ * @param {string|string[]} [options.extraBcc]
965
+ * @param {string|string[]} [options.attachments]
966
+ * @param {boolean} [options.addExtrasToTicket]
967
+ * @param {string} [options.retryVisitorMessageId]
968
+ */
969
+ async publicReply(options = {}) {
970
+ const {
971
+ taskId,
972
+ html,
973
+ text,
974
+ extraTo,
975
+ extraCc,
976
+ extraBcc,
977
+ attachments,
978
+ addExtrasToTicket,
979
+ retryVisitorMessageId,
980
+ } = options;
981
+
982
+ this.sdk.validateParams(
983
+ { taskId },
984
+ { taskId: { type: 'string', required: true } },
985
+ );
986
+
987
+ const body = {};
988
+ if (html !== undefined) body.html = html;
989
+ if (text !== undefined) body.text = text;
990
+ if (extraTo !== undefined) body.extraTo = extraTo;
991
+ if (extraCc !== undefined) body.extraCc = extraCc;
992
+ if (extraBcc !== undefined) body.extraBcc = extraBcc;
993
+ if (attachments !== undefined) body.attachments = attachments;
994
+ if (addExtrasToTicket !== undefined) {
995
+ body.addExtrasToTicket = addExtrasToTicket;
996
+ }
997
+ if (retryVisitorMessageId !== undefined) {
998
+ body.retryVisitorMessageId = retryVisitorMessageId;
999
+ }
1000
+
1001
+ return await internalRequest(
1002
+ this.sdk,
1003
+ `/taskRouter/tasks/${taskId}/public-reply`,
1004
+ 'POST',
1005
+ { body },
1006
+ );
1007
+ }
1008
+
883
1009
  /**
884
1010
  * Mark a task's inbound messages as read for one channel (or all).
885
1011
  * Channel ids: sms | webchat | email | whatsApp | rcs | all.