@unboundcx/sdk 4.8.5 → 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 +1 -1
- package/services/ai.js +26 -0
- package/services/directory.js +73 -7
- package/services/inbox.js +5 -5
- package/services/taskRouter/CCService.js +302 -0
- package/services/taskRouter/MetricsService.js +54 -0
- package/services/taskRouter/TaskRouterService.js +2 -0
- package/services/video.js +144 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unboundcx/sdk",
|
|
3
|
-
"version": "4.8.
|
|
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",
|
package/services/ai.js
CHANGED
|
@@ -642,6 +642,32 @@ export class SpeechToTextService {
|
|
|
642
642
|
return result;
|
|
643
643
|
}
|
|
644
644
|
|
|
645
|
+
/**
|
|
646
|
+
* Synchronously transcribe a stored audio file (batch STT).
|
|
647
|
+
* Counterpart to stream(): give it a storage file id, get the transcript
|
|
648
|
+
* back in the response (typically ~1-3s for voicemail-length audio).
|
|
649
|
+
*
|
|
650
|
+
* @param {Object} options
|
|
651
|
+
* @param {string} options.storageId - Storage file id of the audio (WAV by default).
|
|
652
|
+
* @param {string} [options.language] - Language hint (default 'en').
|
|
653
|
+
* @param {string} [options.encoding] - Audio encoding (default 'WAV').
|
|
654
|
+
* @returns {Promise<{transcription: string|null, language: string|null, duration: number|null}>}
|
|
655
|
+
*/
|
|
656
|
+
async file({ storageId, language, encoding } = {}) {
|
|
657
|
+
this.sdk.validateParams(
|
|
658
|
+
{ storageId, language, encoding },
|
|
659
|
+
{
|
|
660
|
+
storageId: { type: 'string', required: true },
|
|
661
|
+
language: { type: 'string', required: false },
|
|
662
|
+
encoding: { type: 'string', required: false },
|
|
663
|
+
},
|
|
664
|
+
);
|
|
665
|
+
const body = { storageId };
|
|
666
|
+
if (language !== undefined) body.language = language;
|
|
667
|
+
if (encoding !== undefined) body.encoding = encoding;
|
|
668
|
+
return await this.sdk._fetch('/ai/stt/file', 'POST', { body });
|
|
669
|
+
}
|
|
670
|
+
|
|
645
671
|
/**
|
|
646
672
|
* Create a real-time streaming transcription session
|
|
647
673
|
* Returns an EventEmitter-based stream for sending audio and receiving transcripts
|
package/services/directory.js
CHANGED
|
@@ -15,27 +15,33 @@ export class DirectoryService {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Add a person or
|
|
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 '
|
|
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
|
|
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
|
|
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
|
}
|