@unboundcx/sdk 4.13.23 → 4.13.25

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/index.js CHANGED
@@ -20,6 +20,7 @@ import { ContentService } from './services/content.js';
20
20
  import { VerificationService } from './services/verification.js';
21
21
  import { PortalsService } from './services/portals.js';
22
22
  import { SipEndpointsService } from './services/sipEndpoints.js';
23
+ import { WebchatService } from './services/webchat.js';
23
24
  import { ExternalOAuthService } from './services/externalOAuth.js';
24
25
  import { GoogleCalendarService } from './services/googleCalendar.js';
25
26
  import { DriveService } from './services/drive.js';
@@ -105,6 +106,7 @@ class UnboundSDK extends BaseSDK {
105
106
  this.verification = new VerificationService(this);
106
107
  this.portals = new PortalsService(this);
107
108
  this.sipEndpoints = new SipEndpointsService(this);
109
+ this.webchat = new WebchatService(this);
108
110
  this.externalOAuth = new ExternalOAuthService(this);
109
111
  this.googleCalendar = new GoogleCalendarService(this);
110
112
  this.drive = new DriveService(this);
@@ -297,6 +299,11 @@ export {
297
299
  export { VerificationService } from './services/verification.js';
298
300
  export { PortalsService } from './services/portals.js';
299
301
  export { SipEndpointsService } from './services/sipEndpoints.js';
302
+ export {
303
+ WebchatService,
304
+ WebchatWidgetsService,
305
+ WebchatWidgetKeysService,
306
+ } from './services/webchat.js';
300
307
  export { ExternalOAuthService } from './services/externalOAuth.js';
301
308
  export { GoogleCalendarService } from './services/googleCalendar.js';
302
309
  export { DriveService } from './services/drive.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.13.23",
3
+ "version": "4.13.25",
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",
@@ -63,6 +63,8 @@ export class EmailService {
63
63
  * @param {boolean} [params.tracking=true] - Enable email tracking (opens, clicks)
64
64
  * @param {string} [params.mailboxId] - Specific mailbox to send from
65
65
  * @param {string} [params.draftId] - Convert existing draft to sent email
66
+ * @param {string} [params.engagementSessionId] - Engagement to attach this email to
67
+ * @param {string} [params.taskId] - Task the email was sent under (multi-channel-per-task attribution)
66
68
  * @returns {Promise<Object>} Email send result with ID and threading info
67
69
  */
68
70
  async send({
@@ -86,6 +88,7 @@ export class EmailService {
86
88
  mailboxId,
87
89
  draftId,
88
90
  engagementSessionId,
91
+ taskId,
89
92
  }) {
90
93
  // Validate required params (relaxed when using draftId)
91
94
  if (!draftId) {
@@ -118,6 +121,7 @@ export class EmailService {
118
121
  mailboxId,
119
122
  draftId,
120
123
  engagementSessionId,
124
+ taskId,
121
125
  },
122
126
  {
123
127
  html: { type: 'string', required: false },
@@ -135,6 +139,7 @@ export class EmailService {
135
139
  mailboxId: { type: 'string', required: false },
136
140
  draftId: { type: 'string', required: false },
137
141
  engagementSessionId: { type: 'string', required: false },
142
+ taskId: { type: 'string', required: false },
138
143
  },
139
144
  );
140
145
 
@@ -161,6 +166,7 @@ export class EmailService {
161
166
  if (draftId) emailData.draftId = draftId;
162
167
  if (engagementSessionId)
163
168
  emailData.engagementSessionId = engagementSessionId;
169
+ if (taskId) emailData.taskId = taskId;
164
170
 
165
171
  const options = {
166
172
  body: emailData,
@@ -19,6 +19,8 @@ export class SmsService {
19
19
  * @param {string} [params.webhookUrl] - Webhook URL for delivery status
20
20
  * @param {string} [params.relatedId] - Task/engagement (or other record) id
21
21
  * to attach this message to, so it surfaces on that record's feed
22
+ * @param {string} [params.engagementSessionId] - Engagement to attach this message to
23
+ * @param {string} [params.taskId] - Task the message was sent under (multi-channel-per-task attribution)
22
24
  * @returns {Promise<Object>} Message details
23
25
  */
24
26
  async send({
@@ -30,6 +32,8 @@ export class SmsService {
30
32
  mediaUrls,
31
33
  webhookUrl,
32
34
  relatedId,
35
+ engagementSessionId,
36
+ taskId,
33
37
  }) {
34
38
  const messageData = {};
35
39
  if (from) messageData.from = from;
@@ -39,6 +43,8 @@ export class SmsService {
39
43
  if (mediaUrls) messageData.mediaUrls = mediaUrls;
40
44
  if (webhookUrl) messageData.webhookUrl = webhookUrl;
41
45
  if (relatedId) messageData.relatedId = relatedId;
46
+ if (engagementSessionId) messageData.engagementSessionId = engagementSessionId;
47
+ if (taskId) messageData.taskId = taskId;
42
48
 
43
49
  this.sdk.validateParams(
44
50
  { to, ...messageData },
@@ -51,6 +57,8 @@ export class SmsService {
51
57
  mediaUrls: { type: 'array', required: false },
52
58
  webhookUrl: { type: 'string', required: false },
53
59
  relatedId: { type: 'string', required: false },
60
+ engagementSessionId: { type: 'string', required: false },
61
+ taskId: { type: 'string', required: false },
54
62
  },
55
63
  );
56
64
 
@@ -372,6 +372,7 @@ export class ObjectsService {
372
372
  * @param {string} args.object
373
373
  * @param {object} args.where
374
374
  * @param {boolean} [args.skipTriggers=false] - Do not run triggers for this write
375
+ * @param {object} [args.options] - Extra delete options (e.g. { deleteEmails: true })
375
376
  * @returns {Promise} Delete result
376
377
  *
377
378
  * @example
@@ -379,15 +380,17 @@ export class ObjectsService {
379
380
  * object: 'people',
380
381
  * where: { id: '013…' },
381
382
  * skipTriggers: true,
383
+ * options: { deleteEmails: true },
382
384
  * });
383
385
  */
384
- async delete({ object, where, skipTriggers = false }) {
386
+ async delete({ object, where, skipTriggers = false, options }) {
385
387
  this.sdk.validateParams(
386
- { object, where, skipTriggers },
388
+ { object, where, skipTriggers, options },
387
389
  {
388
390
  object: { type: 'string', required: true },
389
391
  where: { type: 'object', required: true },
390
392
  skipTriggers: { type: 'boolean', required: false },
393
+ options: { type: 'object', required: false },
391
394
  },
392
395
  );
393
396
 
@@ -396,6 +399,7 @@ export class ObjectsService {
396
399
  where,
397
400
  },
398
401
  };
402
+ if (options) params.body.options = options;
399
403
  if (skipTriggers) params.query = { skipTriggers: true };
400
404
 
401
405
  const result = await internalRequest(this.sdk, `/object/${object}`, 'DELETE', params);
@@ -409,18 +413,25 @@ export class ObjectsService {
409
413
  * @param {string} args.object
410
414
  * @param {string} args.id
411
415
  * @param {boolean} [args.skipTriggers=false] - Do not run triggers for this write
416
+ * @param {object} [args.options] - Extra delete options (e.g. { deleteEmails: true })
412
417
  * @returns {Promise} Delete result
413
418
  *
414
419
  * @example
415
- * await sdk.objects.deleteById({ object: 'people', id: '013…', skipTriggers: true });
420
+ * await sdk.objects.deleteById({
421
+ * object: 'people',
422
+ * id: '013…',
423
+ * skipTriggers: true,
424
+ * options: { deleteEmails: true },
425
+ * });
416
426
  */
417
- async deleteById({ object, id, skipTriggers = false }) {
427
+ async deleteById({ object, id, skipTriggers = false, options }) {
418
428
  this.sdk.validateParams(
419
- { object, id, skipTriggers },
429
+ { object, id, skipTriggers, options },
420
430
  {
421
431
  object: { type: 'string', required: true },
422
432
  id: { type: 'string', required: true },
423
433
  skipTriggers: { type: 'boolean', required: false },
434
+ options: { type: 'object', required: false },
424
435
  },
425
436
  );
426
437
 
@@ -431,12 +442,84 @@ export class ObjectsService {
431
442
  },
432
443
  },
433
444
  };
445
+ if (options) params.body.options = options;
434
446
  if (skipTriggers) params.query = { skipTriggers: true };
435
447
 
436
448
  const result = await internalRequest(this.sdk, `/object/${object}`, 'DELETE', params);
437
449
  return result;
438
450
  }
439
451
 
452
+ /**
453
+ * Merge 1–3 records into a survivor record. people|company only (v1 gate,
454
+ * enforced server-side). Related-record cleanup runs sync (owned child rows)
455
+ * + async (high-volume history) after the survivor is updated and the
456
+ * losers are deleted.
457
+ *
458
+ * @param {object} args
459
+ * @param {string} args.object
460
+ * @param {string} args.survivorId - Record that remains after the merge.
461
+ * @param {string[]} args.mergeIds - 1-3 ids to merge into the survivor (then deleted).
462
+ * @param {object} [args.fields] - Map of column -> the record id whose value wins.
463
+ * Omitted columns keep the survivor's value.
464
+ * @returns {Promise<{survivorId: string, queued: boolean}>}
465
+ *
466
+ * @example
467
+ * await sdk.objects.merge({
468
+ * object: 'people',
469
+ * survivorId: '013…',
470
+ * mergeIds: ['014…', '015…'],
471
+ * fields: { firstName: '014…', email: '015…' },
472
+ * });
473
+ */
474
+ async merge({ object, survivorId, mergeIds, fields }) {
475
+ this.sdk.validateParams(
476
+ { object, survivorId, mergeIds, fields },
477
+ {
478
+ object: { type: 'string', required: true },
479
+ survivorId: { type: 'string', required: true },
480
+ mergeIds: { type: 'array', required: true },
481
+ fields: { type: 'object', required: false },
482
+ },
483
+ );
484
+
485
+ const body = { survivorId, mergeIds };
486
+ if (fields) body.fields = fields;
487
+
488
+ const params = { body };
489
+
490
+ const result = await internalRequest(this.sdk, `/object/${object}/merge`, 'POST', params);
491
+ return result;
492
+ }
493
+
494
+ /**
495
+ * Get merge backfill status for a record (pending Bucket B re-point count
496
+ * from the recordMerges ledger).
497
+ *
498
+ * @param {object} args
499
+ * @param {string} args.object
500
+ * @param {string} args.id
501
+ * @returns {Promise<{pending: number, lastMergedAt: string|null}>}
502
+ *
503
+ * @example
504
+ * await sdk.objects.mergeStatus({ object: 'people', id: '013…' });
505
+ */
506
+ async mergeStatus({ object, id }) {
507
+ this.sdk.validateParams(
508
+ { object, id },
509
+ {
510
+ object: { type: 'string', required: true },
511
+ id: { type: 'string', required: true },
512
+ },
513
+ );
514
+
515
+ const result = await internalRequest(
516
+ this.sdk,
517
+ `/object/${object}/merge-status/${id}`,
518
+ 'GET',
519
+ );
520
+ return result;
521
+ }
522
+
440
523
  /**
441
524
  * List saved list-views for an object (system + shared + own).
442
525
  * @param {Object} params
@@ -86,6 +86,26 @@ export class PortalsService {
86
86
  * @param {string} [updates.name] - New display name for the portal.
87
87
  * @param {string} [updates.domain] - New custom domain for the portal.
88
88
  * @param {object} [updates.settings] - Updated portal configuration settings.
89
+ * @param {object} [updates.settings.profile] - P11.2: visitor-editable
90
+ * profile fields. `{ people: { enabled, fields: [{ name, editable }] },
91
+ * company: { enabled, fields: [{ name, editable }], editorMode,
92
+ * editorFilter } }`. `fields` (max 40 each) is the allowlist of
93
+ * people/company columns exposed on the portal's `GET /portal-profile` —
94
+ * never system columns (id/*At/*By/isDeleted/recordTypeId), foreign
95
+ * keys, or encrypted fields (the API 400s on save otherwise).
96
+ * `company.editorMode` is `'none'|'all'|'rules'` — whether a signed-in
97
+ * visitor may edit their OWN company record: nobody / everyone signed
98
+ * in / only people matching `editorFilter` (same `{'<field>::<op>':
99
+ * value}` AND-map as `settings.peopleFilter`, ≥1 rule required for
100
+ * `'rules'`). A per-person override (people custom field
101
+ * `portalCompanyEdit__c`, `'allow'|'deny'`, set via `sdk.objects.update`)
102
+ * wins over `editorMode`/`editorFilter` when present.
103
+ * @param {string} [updates.settings.companyTicketsMode] - `'none'|'all'|'rules'`
104
+ * — successor to the legacy `settings.companyTickets` boolean
105
+ * (`true`→`'all'`, `false`→`'none'`); same 3-way shape as
106
+ * `settings.profile.company.editorMode` above, paired with
107
+ * `settings.companyTicketsFilter`. Prefer this over the boolean going
108
+ * forward — both may be sent, `companyTicketsMode` wins.
89
109
  * @param {boolean} [updates.isPublic] - Updated public accessibility flag.
90
110
  * @param {string} [updates.customCss] - Updated custom CSS for the portal.
91
111
  * @param {string} [updates.customJs] - Updated custom JavaScript for the portal.
@@ -0,0 +1,172 @@
1
+ import { internalRequest } from '../base.js';
2
+
3
+ // WebChat P0: agent-facing widget CRUD. Endpoints live under
4
+ // /webchat/widgets/ (checkApiAuth) -- deliberately not bare /webchat/, which
5
+ // is reserved for the future unauthenticated visitor surface (plan §5).
6
+ export class WebchatWidgetKeysService {
7
+ constructor(sdk) {
8
+ this.sdk = sdk;
9
+ }
10
+
11
+ /**
12
+ * List signing keys for a widget.
13
+ * @param {string} widgetId
14
+ * @returns {Promise<Array<Object>>}
15
+ */
16
+ async list(widgetId) {
17
+ this.sdk.validateParams(
18
+ { widgetId },
19
+ { widgetId: { type: 'string', required: true } },
20
+ );
21
+
22
+ return internalRequest(
23
+ this.sdk,
24
+ `/webchat/widgets/${widgetId}/keys`,
25
+ 'GET',
26
+ );
27
+ }
28
+
29
+ /**
30
+ * Create a signing key for a widget. The token is server-generated and
31
+ * returned once in this response only.
32
+ * @param {string} widgetId
33
+ * @param {Object} [options]
34
+ * @param {string[]} [options.urls] - allowlisted origins/domains for this key
35
+ * @returns {Promise<Object>}
36
+ */
37
+ async create(widgetId, options = {}) {
38
+ this.sdk.validateParams(
39
+ { widgetId },
40
+ { widgetId: { type: 'string', required: true } },
41
+ );
42
+
43
+ const params = { body: { ...options } };
44
+
45
+ return internalRequest(
46
+ this.sdk,
47
+ `/webchat/widgets/${widgetId}/keys`,
48
+ 'POST',
49
+ params,
50
+ );
51
+ }
52
+
53
+ /**
54
+ * Delete (soft) a widget signing key.
55
+ * @param {string} widgetId
56
+ * @param {string} keyId
57
+ * @returns {Promise<Object>}
58
+ */
59
+ async delete(widgetId, keyId) {
60
+ this.sdk.validateParams(
61
+ { widgetId, keyId },
62
+ {
63
+ widgetId: { type: 'string', required: true },
64
+ keyId: { type: 'string', required: true },
65
+ },
66
+ );
67
+
68
+ return internalRequest(
69
+ this.sdk,
70
+ `/webchat/widgets/${widgetId}/keys/${keyId}`,
71
+ 'DELETE',
72
+ );
73
+ }
74
+ }
75
+
76
+ export class WebchatWidgetsService {
77
+ constructor(sdk) {
78
+ this.sdk = sdk;
79
+ this.keys = new WebchatWidgetKeysService(sdk);
80
+ }
81
+
82
+ /**
83
+ * List WebChat widgets for the authenticated account.
84
+ * @returns {Promise<Array<Object>>}
85
+ */
86
+ async list() {
87
+ return internalRequest(this.sdk, '/webchat/widgets', 'GET');
88
+ }
89
+
90
+ /**
91
+ * Get a single WebChat widget by id.
92
+ * @param {string} widgetId
93
+ * @returns {Promise<Object>}
94
+ */
95
+ async get(widgetId) {
96
+ this.sdk.validateParams(
97
+ { widgetId },
98
+ { widgetId: { type: 'string', required: true } },
99
+ );
100
+
101
+ return internalRequest(this.sdk, `/webchat/widgets/${widgetId}`, 'GET');
102
+ }
103
+
104
+ /**
105
+ * Create a WebChat widget. identitySecret is always server-generated and
106
+ * returned once (plaintext) in this response only -- never on list/get.
107
+ * @param {Object} options
108
+ * @param {string} options.name
109
+ * @param {'draft'|'active'|'paused'} [options.status]
110
+ * @param {'workflow'|'queue'} [options.routeType]
111
+ * @param {'full'|'first'|'firstLastInitial'} [options.agentNameDisplay]
112
+ * @returns {Promise<Object>}
113
+ */
114
+ async create(options) {
115
+ this.sdk.validateParams(
116
+ { name: options?.name },
117
+ { name: { type: 'string', required: true } },
118
+ );
119
+
120
+ const params = { body: { ...options } };
121
+
122
+ return internalRequest(this.sdk, '/webchat/widgets', 'POST', params);
123
+ }
124
+
125
+ /**
126
+ * Update a WebChat widget. Pass regenerateIdentitySecret:true to rotate
127
+ * the identity secret (returned once, plaintext, in this response).
128
+ * @param {string} widgetId
129
+ * @param {Object} options
130
+ * @returns {Promise<Object>}
131
+ */
132
+ async update(widgetId, options) {
133
+ this.sdk.validateParams(
134
+ { widgetId },
135
+ { widgetId: { type: 'string', required: true } },
136
+ );
137
+
138
+ const params = { body: { ...options } };
139
+
140
+ return internalRequest(
141
+ this.sdk,
142
+ `/webchat/widgets/${widgetId}`,
143
+ 'PUT',
144
+ params,
145
+ );
146
+ }
147
+
148
+ /**
149
+ * Delete (soft) a WebChat widget.
150
+ * @param {string} widgetId
151
+ * @returns {Promise<Object>}
152
+ */
153
+ async delete(widgetId) {
154
+ this.sdk.validateParams(
155
+ { widgetId },
156
+ { widgetId: { type: 'string', required: true } },
157
+ );
158
+
159
+ return internalRequest(
160
+ this.sdk,
161
+ `/webchat/widgets/${widgetId}`,
162
+ 'DELETE',
163
+ );
164
+ }
165
+ }
166
+
167
+ export class WebchatService {
168
+ constructor(sdk) {
169
+ this.sdk = sdk;
170
+ this.widgets = new WebchatWidgetsService(sdk);
171
+ }
172
+ }