@unboundcx/sdk 4.8.6 → 4.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.8.6",
3
+ "version": "4.8.8",
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",
@@ -15,27 +15,33 @@ export class DirectoryService {
15
15
  }
16
16
 
17
17
  /**
18
- * Add a person or company to directory favorites. Reactivates a
19
- * soft-deleted favorite on unique conflict.
18
+ * Add a person, company, user, or queue to directory favorites.
19
+ * Reactivates a soft-deleted favorite on unique conflict.
20
20
  *
21
21
  * @param {Object} params
22
- * @param {string} params.objectType - 'person' or 'company' (required).
22
+ * @param {string} params.objectType - 'person', 'company', 'user', or 'queue' (required).
23
23
  * @param {string} params.objectId - Id of the favorited record (required).
24
24
  * @param {string[]} [params.channelIds] - Channel ids to pin for this favorite.
25
+ * @param {string} [params.channelId] - Channel id to pin for this favorite.
26
+ * @param {string} [params.numberField] - Number field to associate with this favorite.
25
27
  * @returns {Promise<Object>} The created (or reactivated) favorite.
26
28
  */
27
- async addFavorite({ objectType, objectId, channelIds }) {
29
+ async addFavorite({ objectType, objectId, channelIds, channelId, numberField }) {
28
30
  this.sdk.validateParams(
29
- { objectType, objectId, channelIds },
31
+ { objectType, objectId, channelIds, channelId, numberField },
30
32
  {
31
33
  objectType: { type: 'string', required: true },
32
34
  objectId: { type: 'string', required: true },
33
35
  channelIds: { type: 'array', required: false },
36
+ channelId: { type: 'string', required: false },
37
+ numberField: { type: 'string', required: false },
34
38
  },
35
39
  );
36
40
 
37
41
  const body = { objectType, objectId };
38
42
  if (channelIds !== undefined) body.channelIds = channelIds;
43
+ if (channelId !== undefined) body.channelId = channelId;
44
+ if (numberField !== undefined) body.numberField = numberField;
39
45
 
40
46
  const params = { body };
41
47
 
@@ -49,21 +55,27 @@ export class DirectoryService {
49
55
  * @param {string} favoriteId - Id of the favorite to update (required).
50
56
  * @param {Object} params
51
57
  * @param {string[]} [params.channelIds]
58
+ * @param {string} [params.channelId]
59
+ * @param {string} [params.numberField]
52
60
  * @param {number} [params.sortOrder]
53
61
  * @returns {Promise<Object>} The updated favorite.
54
62
  */
55
- async updateFavorite(favoriteId, { channelIds, sortOrder } = {}) {
63
+ async updateFavorite(favoriteId, { channelIds, channelId, numberField, sortOrder } = {}) {
56
64
  this.sdk.validateParams(
57
- { favoriteId, channelIds, sortOrder },
65
+ { favoriteId, channelIds, channelId, numberField, sortOrder },
58
66
  {
59
67
  favoriteId: { type: 'string', required: true },
60
68
  channelIds: { type: 'array', required: false },
69
+ channelId: { type: 'string', required: false },
70
+ numberField: { type: 'string', required: false },
61
71
  sortOrder: { type: 'number', required: false },
62
72
  },
63
73
  );
64
74
 
65
75
  const body = {};
66
76
  if (channelIds !== undefined) body.channelIds = channelIds;
77
+ if (channelId !== undefined) body.channelId = channelId;
78
+ if (numberField !== undefined) body.numberField = numberField;
67
79
  if (sortOrder !== undefined) body.sortOrder = sortOrder;
68
80
 
69
81
  const params = { body };
@@ -121,4 +133,58 @@ export class DirectoryService {
121
133
  );
122
134
  return result;
123
135
  }
136
+
137
+ /**
138
+ * List the current user's directory contacts.
139
+ *
140
+ * @returns {Promise<{contacts: Object[]}>}
141
+ */
142
+ async listContacts() {
143
+ const result = await this.sdk._fetch('/directory/contacts', 'GET');
144
+ return result;
145
+ }
146
+
147
+ /**
148
+ * Add a person or company to directory contacts.
149
+ *
150
+ * @param {Object} params
151
+ * @param {string} params.objectType - 'person' or 'company' (required).
152
+ * @param {string} params.objectId - Id of the contacted record (required).
153
+ * @returns {Promise<Object>} The created contact.
154
+ */
155
+ async addContact({ objectType, objectId }) {
156
+ this.sdk.validateParams(
157
+ { objectType, objectId },
158
+ {
159
+ objectType: { type: 'string', required: true },
160
+ objectId: { type: 'string', required: true },
161
+ },
162
+ );
163
+
164
+ const params = { body: { objectType, objectId } };
165
+
166
+ const result = await this.sdk._fetch('/directory/contacts', 'POST', params);
167
+ return result;
168
+ }
169
+
170
+ /**
171
+ * Remove a directory contact.
172
+ *
173
+ * @param {string} contactId - Id of the contact to remove (required).
174
+ * @returns {Promise<Object>}
175
+ */
176
+ async removeContact(contactId) {
177
+ this.sdk.validateParams(
178
+ { contactId },
179
+ {
180
+ contactId: { type: 'string', required: true },
181
+ },
182
+ );
183
+
184
+ const result = await this.sdk._fetch(
185
+ `/directory/contacts/${contactId}`,
186
+ 'DELETE',
187
+ );
188
+ return result;
189
+ }
124
190
  }
package/services/inbox.js CHANGED
@@ -9,8 +9,8 @@ export class InboxService {
9
9
  *
10
10
  * @param {Object} params
11
11
  * @param {string|string[]} [params.types] - Source kinds to include
12
- * ('call','voicemail','fax','sms'). Pass an array or a comma-joined
13
- * string; omit to include all kinds.
12
+ * ('call','voicemail','fax','sms','meeting'). Pass an array or a
13
+ * comma-joined string; omit to include all kinds.
14
14
  * @param {number} [params.limit] - Max items to return.
15
15
  * @param {string} [params.before] - UTC cursor 'YYYY-MM-DD HH:mm:ss';
16
16
  * only items strictly older than this are returned.
@@ -82,10 +82,10 @@ export class InboxService {
82
82
  }
83
83
 
84
84
  /**
85
- * Fetch inbox stats (calls/talk time/missed/unread voicemail) for the
86
- * current user.
85
+ * Fetch inbox stats (calls/talk time/missed/unread voicemail/meetings) for
86
+ * the current user.
87
87
  *
88
- * @returns {Promise<{callsToday: number, talkTimeSeconds: number, missedToday: number, unreadVoicemail: number, oldestUnreadVoicemailAt: string|null}>}
88
+ * @returns {Promise<{callsToday: number, talkTimeSeconds: number, missedToday: number, unreadVoicemail: number, oldestUnreadVoicemailAt: string|null, meetingsToday: number, meetingMinutesToday: number}>}
89
89
  */
90
90
  async stats() {
91
91
  const result = await this.sdk._fetch('/inbox/stats', 'GET');
@@ -0,0 +1,302 @@
1
+ export class CCService {
2
+ constructor(sdk) {
3
+ this.sdk = sdk;
4
+ }
5
+
6
+ /**
7
+ * Resolve the caller's Contact Center queue scope + role.
8
+ * Managers see every non-deleted queue; agents see only the queues they
9
+ * belong to (queueUsers).
10
+ *
11
+ * @returns {Promise<Object>} result
12
+ * @returns {boolean} result.isManager - Whether the caller has queue-manager scope
13
+ * @returns {string|null} result.workerId - The caller's own worker id, if any
14
+ * @returns {Array<Object>} result.queues - Accessible queues [{id, name, slaThreshold}]
15
+ *
16
+ * @example
17
+ * const scope = await sdk.taskRouter.cc.getScope();
18
+ * console.log(scope.isManager, scope.queues.length);
19
+ */
20
+ async getScope() {
21
+ const result = await this.sdk._fetch('/taskRouter/cc/scope', 'GET', {});
22
+ return result;
23
+ }
24
+
25
+ /**
26
+ * Get a live Contact Center snapshot (KPIs, per-queue summaries, team roster
27
+ * with active tasks) scoped to a set of queues.
28
+ *
29
+ * @param {Object} [options] - Parameters
30
+ * @param {string[]} [options.queueIds] - Queue ids to scope to (must be a subset of the caller's accessible queues). Omit/empty for full scope.
31
+ * @returns {Promise<Object>} result
32
+ * @returns {Object} result.kpis - Aggregate KPIs (inQueueNow, longestWaitSec, myHandledToday, myAvgHandleSecToday, slaTodayPct, slaTargetPct)
33
+ * @returns {Array<Object>} result.queues - Per-queue summaries (id, name, waiting, longestWaitSec, agentsAvailable, agentsTotal, slaPct, health)
34
+ * @returns {Array<Object>} result.team - Team roster with active tasks
35
+ *
36
+ * @example
37
+ * const snapshot = await sdk.taskRouter.cc.getSnapshot({ queueIds: ['q1', 'q2'] });
38
+ * console.log(snapshot.kpis.inQueueNow);
39
+ */
40
+ async getSnapshot(options = {}) {
41
+ const { queueIds } = options;
42
+
43
+ this.sdk.validateParams(
44
+ { queueIds },
45
+ {
46
+ queueIds: { type: 'array', required: false },
47
+ },
48
+ );
49
+
50
+ const params = {};
51
+ if (queueIds && queueIds.length) {
52
+ params.query = { queueIds: queueIds.join(',') };
53
+ }
54
+
55
+ const result = await this.sdk._fetch(
56
+ '/taskRouter/cc/snapshot',
57
+ 'GET',
58
+ params,
59
+ );
60
+ return result;
61
+ }
62
+
63
+ /**
64
+ * Get on-queue session history for a worker (self allowed; another
65
+ * worker's sessions require the queue-manager scope).
66
+ *
67
+ * @param {Object} [options] - Parameters
68
+ * @param {string} [options.workerId] - Worker id to fetch sessions for (defaults to the caller's own worker)
69
+ * @param {string} [options.from] - ISO-8601 range start
70
+ * @param {string} [options.to] - ISO-8601 range end
71
+ * @returns {Promise<Object>} result
72
+ * @returns {Array<Object>} result.sessions - [{id, startedAt, endedAt, onQueueSec, breakSec, doneCount}]
73
+ *
74
+ * @example
75
+ * const { sessions } = await sdk.taskRouter.cc.getSessions({ workerId: 'w1', from: '2026-08-12T00:00:00Z', to: '2026-08-19T00:00:00Z' });
76
+ */
77
+ async getSessions(options = {}) {
78
+ const { workerId, from, to } = options;
79
+
80
+ this.sdk.validateParams(
81
+ { workerId, from, to },
82
+ {
83
+ workerId: { type: 'string', required: false },
84
+ from: { type: 'string', required: false },
85
+ to: { type: 'string', required: false },
86
+ },
87
+ );
88
+
89
+ const query = {};
90
+ if (workerId) query.workerId = workerId;
91
+ if (from) query.from = from;
92
+ if (to) query.to = to;
93
+
94
+ const result = await this.sdk._fetch('/taskRouter/cc/sessions', 'GET', {
95
+ query,
96
+ });
97
+ return result;
98
+ }
99
+
100
+ /**
101
+ * Get windowed composite agent rankings (top/struggling) per queue + an
102
+ * 'all' rollup, scored from sentiment/SLA/volume/acceptance. `struggling`
103
+ * arrays are only present when the caller has the queue-manager scope
104
+ * (see `strugglingIncluded` on the result).
105
+ *
106
+ * @param {Object} [options] - Parameters
107
+ * @param {string[]} [options.queueIds] - Queue ids to scope to (must be a subset of the caller's accessible queues). Omit/empty for full scope.
108
+ * @param {string} options.from - ISO-8601 window start
109
+ * @param {string} options.to - ISO-8601 window end
110
+ * @returns {Promise<Object>} result
111
+ * @returns {Object} result.queues - Per-queue rankings, keyed by queueId: `{ top: [{workerId,name,score}], struggling?: [...] }`
112
+ * @returns {Object} result.all - Same shape, aggregated across every scoped queue
113
+ * @returns {boolean} result.strugglingIncluded - Whether `struggling` arrays were included
114
+ *
115
+ * @example
116
+ * const { queues, all } = await sdk.taskRouter.cc.getRankings({
117
+ * queueIds: ['q1'],
118
+ * from: '2026-08-12T00:00:00Z',
119
+ * to: '2026-08-19T00:00:00Z',
120
+ * });
121
+ * console.log(all.top[0].name, all.top[0].score);
122
+ */
123
+ async getRankings(options = {}) {
124
+ const { queueIds, from, to } = options;
125
+
126
+ this.sdk.validateParams(
127
+ { queueIds, from, to },
128
+ {
129
+ queueIds: { type: 'array', required: false },
130
+ from: { type: 'string', required: true },
131
+ to: { type: 'string', required: true },
132
+ },
133
+ );
134
+
135
+ const query = { from, to };
136
+ if (queueIds && queueIds.length) {
137
+ query.queueIds = queueIds.join(',');
138
+ }
139
+
140
+ const result = await this.sdk._fetch('/taskRouter/cc/rankings', 'GET', {
141
+ query,
142
+ });
143
+ return result;
144
+ }
145
+
146
+ /**
147
+ * Set a queue's composite ranking-weights override. Manager-only
148
+ * (`taskrouter:queue:manage`). Weights must be integers 0-100 that sum to
149
+ * exactly 100.
150
+ *
151
+ * @param {Object} options - Parameters
152
+ * @param {string} options.queueId - Queue id to update
153
+ * @param {Object} options.weights - `{sentiment, sla, volume, acceptance}` ints summing to 100
154
+ * @returns {Promise<Object>} result
155
+ * @returns {string} result.queueId
156
+ * @returns {Object} result.rankingWeights - The stored weights
157
+ *
158
+ * @example
159
+ * await sdk.taskRouter.cc.setQueueRankingWeights({
160
+ * queueId: 'q1',
161
+ * weights: { sentiment: 35, sla: 25, volume: 20, acceptance: 20 },
162
+ * });
163
+ */
164
+ async setQueueRankingWeights(options = {}) {
165
+ const { queueId, weights } = options;
166
+
167
+ this.sdk.validateParams(
168
+ { queueId, weights },
169
+ {
170
+ queueId: { type: 'string', required: true },
171
+ weights: { type: 'object', required: true },
172
+ },
173
+ );
174
+
175
+ const result = await this.sdk._fetch(
176
+ `/taskRouter/cc/queues/${queueId}/rankingWeights`,
177
+ 'PUT',
178
+ { body: weights },
179
+ );
180
+ return result;
181
+ }
182
+
183
+ /**
184
+ * Get one agent's Contact Center performance summary (KPIs, per-day
185
+ * charts, live tasks, on-queue sessions) over a date/date-range, with a
186
+ * comparison window for deltas. Self allowed; another worker's summary
187
+ * requires the queue-manager scope.
188
+ *
189
+ * @param {Object} options - Parameters
190
+ * @param {string} options.workerId - Worker id to summarize
191
+ * @param {string} options.from - ISO-8601 window start
192
+ * @param {string} options.to - ISO-8601 window end
193
+ * @param {string} [options.compareFrom] - ISO-8601 comparison window start (defaults to the preceding equal-length period)
194
+ * @param {string} [options.compareTo] - ISO-8601 comparison window end
195
+ * @returns {Promise<Object>} result
196
+ * @returns {Object} result.kpis - tasksHandled, offersAccepted, avgHandleSec, slaMet, avgSentiment, composite, onQueue
197
+ * @returns {Array<Object>} result.tasksPerDay - [{date, count}]
198
+ * @returns {Array<Object>} result.trend - [{date, sentiment, slaPct}]
199
+ * @returns {Array<Object>} result.liveNow - Current tasks (only when the window includes today)
200
+ * @returns {Array<Object>} result.sessions - On-queue session log
201
+ *
202
+ * @example
203
+ * const summary = await sdk.taskRouter.cc.getAgentSummary({
204
+ * workerId: 'w1',
205
+ * from: '2026-08-12T00:00:00Z',
206
+ * to: '2026-08-19T00:00:00Z',
207
+ * });
208
+ * console.log(summary.kpis.tasksHandled.value);
209
+ */
210
+ async getAgentSummary(options = {}) {
211
+ const { workerId, from, to, compareFrom, compareTo } = options;
212
+
213
+ this.sdk.validateParams(
214
+ { workerId, from, to, compareFrom, compareTo },
215
+ {
216
+ workerId: { type: 'string', required: true },
217
+ from: { type: 'string', required: true },
218
+ to: { type: 'string', required: true },
219
+ compareFrom: { type: 'string', required: false },
220
+ compareTo: { type: 'string', required: false },
221
+ },
222
+ );
223
+
224
+ const query = { from, to };
225
+ if (compareFrom) query.compareFrom = compareFrom;
226
+ if (compareTo) query.compareTo = compareTo;
227
+
228
+ const result = await this.sdk._fetch(
229
+ `/taskRouter/cc/agents/${workerId}/summary`,
230
+ 'GET',
231
+ { query },
232
+ );
233
+ return result;
234
+ }
235
+
236
+ /**
237
+ * Log a worker into or out of a queue. Manager-only
238
+ * (`taskrouter:queue:manage`). Mutates the worker's queue membership
239
+ * through task-router (audited).
240
+ *
241
+ * @param {Object} options - Parameters
242
+ * @param {string} options.workerId - Worker id to update
243
+ * @param {string} options.queueId - Queue id to log in/out of
244
+ * @param {'login'|'logout'} options.action - Whether to add or remove the queue
245
+ * @returns {Promise<Object>} result
246
+ * @returns {string} result.workerId
247
+ * @returns {string} result.status
248
+ * @returns {Array<string>} result.queues - The worker's updated queue list
249
+ *
250
+ * @example
251
+ * await sdk.taskRouter.cc.setWorkerQueue({ workerId: 'w1', queueId: 'q1', action: 'login' });
252
+ */
253
+ async setWorkerQueue(options = {}) {
254
+ const { workerId, queueId, action } = options;
255
+
256
+ this.sdk.validateParams(
257
+ { workerId, queueId, action },
258
+ {
259
+ workerId: { type: 'string', required: true },
260
+ queueId: { type: 'string', required: true },
261
+ action: { type: 'string', required: true },
262
+ },
263
+ );
264
+
265
+ const result = await this.sdk._fetch(
266
+ `/taskRouter/cc/workers/${workerId}/queues`,
267
+ 'PUT',
268
+ { body: { queueId, action } },
269
+ );
270
+ return result;
271
+ }
272
+
273
+ /**
274
+ * Force a worker offline (out of every queue), closing their on-queue
275
+ * session. Manager-only (`taskrouter:queue:manage`, audited).
276
+ *
277
+ * @param {Object} options - Parameters
278
+ * @param {string} options.workerId - Worker id to log out
279
+ * @returns {Promise<Object>} result
280
+ * @returns {string} result.workerId
281
+ * @returns {string} result.status
282
+ * @returns {Array<string>} result.queues
283
+ *
284
+ * @example
285
+ * await sdk.taskRouter.cc.forceLogoutWorker({ workerId: 'w1' });
286
+ */
287
+ async forceLogoutWorker(options = {}) {
288
+ const { workerId } = options;
289
+
290
+ this.sdk.validateParams(
291
+ { workerId },
292
+ { workerId: { type: 'string', required: true } },
293
+ );
294
+
295
+ const result = await this.sdk._fetch(
296
+ `/taskRouter/cc/workers/${workerId}/forceLogout`,
297
+ 'POST',
298
+ {},
299
+ );
300
+ return result;
301
+ }
302
+ }
@@ -108,4 +108,58 @@ export class MetricsService {
108
108
  );
109
109
  return result;
110
110
  }
111
+
112
+ /**
113
+ * Get windowed queue/company metrics with a compare-window delta, per
114
+ * queue + an 'all' rollup (avg wait, avg handle, service level, longest
115
+ * wait, live depth/workers, and a waiting-count sparkline).
116
+ *
117
+ * @param {Object} params - Parameters
118
+ * @param {string[]} [params.queueIds] - Queue ids to scope to. Omit/empty for all queues.
119
+ * @param {string} params.from - ISO-8601 window start
120
+ * @param {string} params.to - ISO-8601 window end
121
+ * @param {string} params.compareFrom - ISO-8601 compare-window start
122
+ * @param {string} params.compareTo - ISO-8601 compare-window end
123
+ * @returns {Promise<Object>} result
124
+ * @returns {Object} result.window - `{from, to}`
125
+ * @returns {Object} result.compare - `{from, to}`
126
+ * @returns {Object} result.all - Rollup across every scoped queue (avgWaitSec, avgHandleSec, serviceLevelPct, longestWaitSec, depth, workersAvailable, workersTotal, waitingSparkline, deltas)
127
+ * @returns {Object} result.queues - Same shape as `result.all`, keyed by queueId
128
+ *
129
+ * @example
130
+ * const { all, queues } = await sdk.taskRouter.metrics.getWindow({
131
+ * queueIds: ['q1', 'q2'],
132
+ * from: '2026-08-19T22:00:00Z',
133
+ * to: '2026-08-19T23:00:00Z',
134
+ * compareFrom: '2026-08-19T21:00:00Z',
135
+ * compareTo: '2026-08-19T22:00:00Z',
136
+ * });
137
+ * console.log(all.serviceLevelPct, all.deltas.serviceLevelPct);
138
+ */
139
+ async getWindow(params = {}) {
140
+ const { queueIds, from, to, compareFrom, compareTo } = params;
141
+
142
+ this.sdk.validateParams(
143
+ { queueIds, from, to, compareFrom, compareTo },
144
+ {
145
+ queueIds: { type: 'array', required: false },
146
+ from: { type: 'string', required: true },
147
+ to: { type: 'string', required: true },
148
+ compareFrom: { type: 'string', required: false },
149
+ compareTo: { type: 'string', required: false },
150
+ },
151
+ );
152
+
153
+ const query = { from, to };
154
+ if (queueIds && queueIds.length) query.queueIds = queueIds.join(',');
155
+ if (compareFrom) query.compareFrom = compareFrom;
156
+ if (compareTo) query.compareTo = compareTo;
157
+
158
+ const result = await this.sdk._fetch(
159
+ '/taskRouter/metrics/window',
160
+ 'GET',
161
+ { query },
162
+ );
163
+ return result;
164
+ }
111
165
  }
@@ -1,6 +1,7 @@
1
1
  import { WorkerService } from './WorkerService.js';
2
2
  import { TaskService } from './TaskService.js';
3
3
  import { MetricsService } from './MetricsService.js';
4
+ import { CCService } from './CCService.js';
4
5
 
5
6
  export class TaskRouterService {
6
7
  constructor(sdk) {
@@ -8,5 +9,6 @@ export class TaskRouterService {
8
9
  this.worker = new WorkerService(sdk);
9
10
  this.task = new TaskService(sdk);
10
11
  this.metrics = new MetricsService(sdk);
12
+ this.cc = new CCService(sdk);
11
13
  }
12
14
  }
package/services/video.js CHANGED
@@ -230,6 +230,7 @@ export class VideoService {
230
230
  calendarProvider,
231
231
  vocabularyTerms,
232
232
  shareOcrEnabled,
233
+ earlyJoinMinutes,
233
234
  }) {
234
235
  this.sdk.validateParams(
235
236
  {
@@ -259,6 +260,7 @@ export class VideoService {
259
260
  calendarProvider,
260
261
  vocabularyTerms,
261
262
  shareOcrEnabled,
263
+ earlyJoinMinutes,
262
264
  },
263
265
  {
264
266
  name: { type: 'string', required: false },
@@ -287,6 +289,9 @@ export class VideoService {
287
289
  calendarProvider: { type: 'string', required: false },
288
290
  vocabularyTerms: { type: 'array', required: false },
289
291
  shareOcrEnabled: { type: 'boolean', required: false },
292
+ // 0 = strict at startTime, 5 = 5-min early window (api rejects
293
+ // other values); omit for no schedule-window enforcement.
294
+ earlyJoinMinutes: { type: 'number', required: false },
290
295
  },
291
296
  );
292
297
  const params = {
@@ -317,6 +322,7 @@ export class VideoService {
317
322
  calendarProvider,
318
323
  vocabularyTerms,
319
324
  shareOcrEnabled,
325
+ earlyJoinMinutes,
320
326
  },
321
327
  };
322
328
  const result = await this.sdk._fetch(`/video`, 'POST', params);
@@ -362,6 +368,8 @@ export class VideoService {
362
368
  validationSchema.vocabularyTerms = { type: 'array' };
363
369
  if ('shareOcrEnabled' in update)
364
370
  validationSchema.shareOcrEnabled = { type: 'boolean' };
371
+ if ('earlyJoinMinutes' in update)
372
+ validationSchema.earlyJoinMinutes = { type: 'number' };
365
373
 
366
374
  if (Object.keys(validationSchema).length > 0) {
367
375
  this.sdk.validateParams(update, validationSchema);
@@ -1411,4 +1419,140 @@ export class VideoService {
1411
1419
  );
1412
1420
  return result;
1413
1421
  }
1422
+
1423
+ /**
1424
+ * Get (or lazily create) the calling user's personal meeting room for this
1425
+ * account. Every account user has exactly one; the first call provisions
1426
+ * it server-side.
1427
+ *
1428
+ * NOTE: implemented flat (`getPersonalRoom`/`updatePersonalRoom`/
1429
+ * `regeneratePersonalRoomPin`/`resolvePersonalRoom`) rather than the
1430
+ * `sdk.video.personalRoom.{get,update,regeneratePin}` namespace named in
1431
+ * the meet-hub plan — this file has no existing nested-namespace
1432
+ * precedent, so flat matches every other method here.
1433
+ *
1434
+ * @returns {Promise<{personalRoom: {id: string, slug: string, url: string|null, dialInPin: string, guestsCanStart: boolean}}>}
1435
+ */
1436
+ async getPersonalRoom() {
1437
+ const result = await this.sdk._fetch('/video/personal-room', 'GET');
1438
+ return result;
1439
+ }
1440
+
1441
+ /**
1442
+ * Update the calling user's personal meeting room (slug and/or
1443
+ * guests-can-start). Omit a field to leave it unchanged.
1444
+ *
1445
+ * @param {Object} [update]
1446
+ * @param {string} [update.slug] - New slug (3-32 chars, lowercase/numbers/hyphens, not reserved). 409-equivalent BadRequestError on collision.
1447
+ * @param {boolean} [update.guestsCanStart] - Whether guests can start the room without the host present.
1448
+ * @returns {Promise<{personalRoom: {id: string, slug: string, url: string|null, dialInPin: string, guestsCanStart: boolean}}>}
1449
+ */
1450
+ async updatePersonalRoom({ slug, guestsCanStart, password } = {}) {
1451
+ const validationSchema = {};
1452
+ if (slug !== undefined) validationSchema.slug = { type: 'string' };
1453
+ if (guestsCanStart !== undefined)
1454
+ validationSchema.guestsCanStart = { type: 'boolean' };
1455
+ // Static room passcode — 4-6 digits (api-enforced); applied to every
1456
+ // session the room link mints.
1457
+ if (password !== undefined) validationSchema.password = { type: 'string' };
1458
+
1459
+ if (Object.keys(validationSchema).length > 0) {
1460
+ this.sdk.validateParams(
1461
+ { slug, guestsCanStart, password },
1462
+ validationSchema,
1463
+ );
1464
+ }
1465
+
1466
+ const body = {};
1467
+ if (slug !== undefined) body.slug = slug;
1468
+ if (guestsCanStart !== undefined) body.guestsCanStart = guestsCanStart;
1469
+ if (password !== undefined) body.password = password;
1470
+
1471
+ const params = { body };
1472
+ const result = await this.sdk._fetch(
1473
+ '/video/personal-room',
1474
+ 'PUT',
1475
+ params,
1476
+ );
1477
+ return result;
1478
+ }
1479
+
1480
+ /**
1481
+ * Regenerate the dial-in PIN for the calling user's personal meeting room.
1482
+ *
1483
+ * @returns {Promise<{personalRoom: {id: string, dialInPin: string}}>}
1484
+ */
1485
+ async regeneratePersonalRoomPin() {
1486
+ const result = await this.sdk._fetch(
1487
+ '/video/personal-room/regenerate-pin',
1488
+ 'POST',
1489
+ {},
1490
+ );
1491
+ return result;
1492
+ }
1493
+
1494
+ /**
1495
+ * Live availability dry-run for a personal-room slug (settings editor).
1496
+ *
1497
+ * @param {string} slug - Candidate slug.
1498
+ * @returns {Promise<{slug: string, available: boolean, reason?: 'invalid'|'taken'}>}
1499
+ */
1500
+ async checkPersonalRoomSlug(slug) {
1501
+ this.sdk.validateParams(
1502
+ { slug },
1503
+ { slug: { type: 'string', required: true } },
1504
+ );
1505
+ const result = await this.sdk._fetch(
1506
+ `/video/personal-room/slug-available?slug=${encodeURIComponent(slug)}`,
1507
+ 'GET',
1508
+ {},
1509
+ );
1510
+ return result;
1511
+ }
1512
+
1513
+ /**
1514
+ * Regenerate the static web passcode for the calling user's personal
1515
+ * meeting room (a separate secret from the dial-in PIN). Sessions minted
1516
+ * after this use the new value.
1517
+ *
1518
+ * @returns {Promise<{personalRoom: {id: string, password: string}}>}
1519
+ */
1520
+ async regeneratePersonalRoomPassword() {
1521
+ const result = await this.sdk._fetch(
1522
+ '/video/personal-room/regenerate-password',
1523
+ 'POST',
1524
+ {},
1525
+ );
1526
+ return result;
1527
+ }
1528
+
1529
+ /**
1530
+ * Resolve a personal-room slug to a live session (join page). Guest-capable
1531
+ * — mints/claims a fresh meeting if none is live, or returns the already
1532
+ * -claimed session; if the caller is an unauthenticated guest and the room
1533
+ * doesn't allow guests to start, returns `waitingForHost: true` instead.
1534
+ * Guests only receive `password` when they supply the room's static
1535
+ * passcode; otherwise `passwordRequired: true` is returned and the join
1536
+ * page should prompt.
1537
+ *
1538
+ * @param {string} slug - The personal room's slug.
1539
+ * @param {{password?: string}} [options] - The room's static passcode, when known.
1540
+ * @returns {Promise<{claimed: boolean, meetingId?: string, friendlyName?: string, password?: string, passwordRequired?: boolean, waitingForHost?: boolean}>}
1541
+ */
1542
+ async resolvePersonalRoom(slug, { password } = {}) {
1543
+ this.sdk.validateParams(
1544
+ { slug, password },
1545
+ {
1546
+ slug: { type: 'string', required: true },
1547
+ password: { type: 'string', required: false },
1548
+ },
1549
+ );
1550
+
1551
+ const result = await this.sdk._fetch(
1552
+ `/video/personal-room/resolve/${slug}`,
1553
+ 'POST',
1554
+ { body: { password } },
1555
+ );
1556
+ return result;
1557
+ }
1414
1558
  }