@unboundcx/sdk 4.8.6 → 4.8.9

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
@@ -33,6 +33,7 @@ import { TriggersService } from './services/triggers.js';
33
33
  import { InboxService } from './services/inbox.js';
34
34
  import { SearchService } from './services/search.js';
35
35
  import { DirectoryService } from './services/directory.js';
36
+ import { ChatService } from './services/chat.js';
36
37
 
37
38
  class UnboundSDK extends BaseSDK {
38
39
  constructor(options = {}) {
@@ -107,6 +108,7 @@ class UnboundSDK extends BaseSDK {
107
108
  this.inbox = new InboxService(this);
108
109
  this.search = new SearchService(this);
109
110
  this.directory = new DirectoryService(this);
111
+ this.chat = new ChatService(this);
110
112
 
111
113
  // Add additional services that might be missing
112
114
  this._initializeAdditionalServices();
@@ -290,4 +292,5 @@ export { PermissionsService } from './services/permissions.js';
290
292
  export { InboxService } from './services/inbox.js';
291
293
  export { SearchService } from './services/search.js';
292
294
  export { DirectoryService } from './services/directory.js';
295
+ export { ChatService } from './services/chat.js';
293
296
  export { BaseSDK } from './base.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.8.6",
3
+ "version": "4.8.9",
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",
@@ -0,0 +1,851 @@
1
+ /**
2
+ * ChatService — channels, DMs, membership, unreads, DND, messages, search,
3
+ * webhooks, card actions, reports, admin review, admin export, record feeds,
4
+ * channel meet, push devices, and notifyLevel.
5
+ * Backed by /chat/* on app1-api (checkApiAuth). Incoming webhook POST
6
+ * (HMAC) is external and is not an SDK method.
7
+ */
8
+ export class ChatService {
9
+ constructor(sdk) {
10
+ this.sdk = sdk;
11
+ }
12
+
13
+ /**
14
+ * Create a channel.
15
+ * @param {Object} params
16
+ * @param {string} params.name - Channel name (required)
17
+ * @param {string} [params.topic]
18
+ * @param {'public'|'private'|'dm'|'group_dm'|'record'|'meeting'} [params.kind]
19
+ * @param {Object} [params.settings]
20
+ * @param {string[]} [params.groupIds] - Groups whose members are auto-added
21
+ * @returns {Promise<Object>} Created channel
22
+ */
23
+ async createChannel({ name, topic, kind, settings, groupIds }) {
24
+ this.sdk.validateParams(
25
+ { name, topic, kind, settings, groupIds },
26
+ {
27
+ name: { type: 'string', required: true },
28
+ topic: { type: 'string', required: false },
29
+ kind: { type: 'string', required: false },
30
+ settings: { type: 'object', required: false },
31
+ groupIds: { type: 'array', required: false },
32
+ },
33
+ );
34
+
35
+ const body = { name };
36
+ if (topic !== undefined) body.topic = topic;
37
+ if (kind !== undefined) body.kind = kind;
38
+ if (settings !== undefined) body.settings = settings;
39
+ if (groupIds !== undefined) body.groupIds = groupIds;
40
+
41
+ return this.sdk._fetch('/chat/channels', 'POST', { body });
42
+ }
43
+
44
+ /**
45
+ * List channels the current user is a member of.
46
+ * @returns {Promise<Object>}
47
+ */
48
+ async listChannels() {
49
+ return this.sdk._fetch('/chat/channels', 'GET');
50
+ }
51
+
52
+ /**
53
+ * Browse joinable (public) channels.
54
+ * @returns {Promise<Object>}
55
+ */
56
+ async browseChannels() {
57
+ return this.sdk._fetch('/chat/channels/browse', 'GET');
58
+ }
59
+
60
+ /**
61
+ * Get a channel by id.
62
+ * @param {string} id
63
+ * @returns {Promise<Object>}
64
+ */
65
+ async getChannel(id) {
66
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
67
+ return this.sdk._fetch(`/chat/channels/${id}`, 'GET');
68
+ }
69
+
70
+ /**
71
+ * Update channel name/topic/settings.
72
+ * @param {string} id
73
+ * @param {Object} [params]
74
+ * @param {string} [params.name]
75
+ * @param {string} [params.topic]
76
+ * @param {Object} [params.settings]
77
+ * @returns {Promise<Object>}
78
+ */
79
+ async updateChannel(id, { name, topic, settings } = {}) {
80
+ this.sdk.validateParams(
81
+ { id, name, topic, settings },
82
+ {
83
+ id: { type: 'string', required: true },
84
+ name: { type: 'string', required: false },
85
+ topic: { type: 'string', required: false },
86
+ settings: { type: 'object', required: false },
87
+ },
88
+ );
89
+
90
+ const body = {};
91
+ if (name !== undefined) body.name = name;
92
+ if (topic !== undefined) body.topic = topic;
93
+ if (settings !== undefined) body.settings = settings;
94
+
95
+ return this.sdk._fetch(`/chat/channels/${id}`, 'PATCH', { body });
96
+ }
97
+
98
+ /**
99
+ * Archive a channel (reversible; hard delete is not supported).
100
+ * @param {string} id
101
+ * @returns {Promise<Object>}
102
+ */
103
+ async archiveChannel(id) {
104
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
105
+ return this.sdk._fetch(`/chat/channels/${id}/archive`, 'POST', {
106
+ body: {},
107
+ });
108
+ }
109
+
110
+ /**
111
+ * Unarchive a channel.
112
+ * @param {string} id
113
+ * @returns {Promise<Object>}
114
+ */
115
+ async unarchiveChannel(id) {
116
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
117
+ return this.sdk._fetch(`/chat/channels/${id}/unarchive`, 'POST', {
118
+ body: {},
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Join a public channel.
124
+ * @param {string} id
125
+ * @returns {Promise<Object>}
126
+ */
127
+ async joinChannel(id) {
128
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
129
+ return this.sdk._fetch(`/chat/channels/${id}/join`, 'POST', { body: {} });
130
+ }
131
+
132
+ /**
133
+ * Leave a channel.
134
+ * @param {string} id
135
+ * @returns {Promise<Object>}
136
+ */
137
+ async leaveChannel(id) {
138
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
139
+ return this.sdk._fetch(`/chat/channels/${id}/leave`, 'POST', { body: {} });
140
+ }
141
+
142
+ /**
143
+ * List members of a channel.
144
+ * @param {string} id
145
+ * @returns {Promise<Object>}
146
+ */
147
+ async listMembers(id) {
148
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
149
+ return this.sdk._fetch(`/chat/channels/${id}/members`, 'GET');
150
+ }
151
+
152
+ /**
153
+ * Add a member to a channel.
154
+ * @param {string} id
155
+ * @param {Object} params
156
+ * @param {string} params.userId
157
+ * @param {'owner'|'moderator'|'member'} [params.role]
158
+ * @returns {Promise<Object>}
159
+ */
160
+ async addMember(id, { userId, role } = {}) {
161
+ this.sdk.validateParams(
162
+ { id, userId, role },
163
+ {
164
+ id: { type: 'string', required: true },
165
+ userId: { type: 'string', required: true },
166
+ role: { type: 'string', required: false },
167
+ },
168
+ );
169
+
170
+ const body = { userId };
171
+ if (role !== undefined) body.role = role;
172
+
173
+ return this.sdk._fetch(`/chat/channels/${id}/members`, 'POST', { body });
174
+ }
175
+
176
+ /**
177
+ * Remove a member from a channel.
178
+ * @param {string} id
179
+ * @param {string} userId
180
+ * @returns {Promise<Object>}
181
+ */
182
+ async removeMember(id, userId) {
183
+ this.sdk.validateParams(
184
+ { id, userId },
185
+ {
186
+ id: { type: 'string', required: true },
187
+ userId: { type: 'string', required: true },
188
+ },
189
+ );
190
+ return this.sdk._fetch(`/chat/channels/${id}/members/${userId}`, 'DELETE');
191
+ }
192
+
193
+ /**
194
+ * Get group-default membership links for a channel.
195
+ * @param {string} id
196
+ * @returns {Promise<Object>}
197
+ */
198
+ async getGroupDefaults(id) {
199
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
200
+ return this.sdk._fetch(`/chat/channels/${id}/group-defaults`, 'GET');
201
+ }
202
+
203
+ /**
204
+ * Replace group-default membership links for a channel.
205
+ * @param {string} id
206
+ * @param {Object} params
207
+ * @param {string[]} params.groupIds
208
+ * @returns {Promise<Object>}
209
+ */
210
+ async setGroupDefaults(id, { groupIds } = {}) {
211
+ this.sdk.validateParams(
212
+ { id, groupIds },
213
+ {
214
+ id: { type: 'string', required: true },
215
+ groupIds: { type: 'array', required: true },
216
+ },
217
+ );
218
+ return this.sdk._fetch(`/chat/channels/${id}/group-defaults`, 'PUT', {
219
+ body: { groupIds },
220
+ });
221
+ }
222
+
223
+ /**
224
+ * Find-or-create a 1:1 or group DM.
225
+ * @param {Object} params
226
+ * @param {string[]} params.userIds
227
+ * @returns {Promise<Object>}
228
+ */
229
+ async openDm({ userIds }) {
230
+ this.sdk.validateParams(
231
+ { userIds },
232
+ { userIds: { type: 'array', required: true } },
233
+ );
234
+ return this.sdk._fetch('/chat/dms', 'POST', { body: { userIds } });
235
+ }
236
+
237
+ /**
238
+ * Sidebar unread snapshot (channels + previews + counters).
239
+ * @returns {Promise<Object>}
240
+ */
241
+ async getUnreads() {
242
+ return this.sdk._fetch('/chat/unreads', 'GET');
243
+ }
244
+
245
+ /**
246
+ * Get the caller's Do Not Disturb state.
247
+ * @returns {Promise<Object>}
248
+ */
249
+ async getDnd() {
250
+ return this.sdk._fetch('/chat/dnd', 'GET');
251
+ }
252
+
253
+ /**
254
+ * Set the caller's Do Not Disturb state.
255
+ * @param {Object} params
256
+ * @param {boolean} params.enabled
257
+ * @returns {Promise<Object>}
258
+ */
259
+ async setDnd({ enabled } = {}) {
260
+ this.sdk.validateParams(
261
+ { enabled },
262
+ { enabled: { type: 'boolean', required: true } },
263
+ );
264
+ return this.sdk._fetch('/chat/dnd', 'PATCH', { body: { enabled } });
265
+ }
266
+
267
+ /**
268
+ * Advance the read watermark and recompute unread counters.
269
+ * @param {string} channelId
270
+ * @param {string} messageId
271
+ * @returns {Promise<Object>}
272
+ */
273
+ async markRead(channelId, messageId) {
274
+ this.sdk.validateParams(
275
+ { channelId, messageId },
276
+ {
277
+ channelId: { type: 'string', required: true },
278
+ messageId: { type: 'string', required: true },
279
+ },
280
+ );
281
+ return this.sdk._fetch(`/chat/channels/${channelId}/read`, 'POST', {
282
+ body: { messageId },
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Set the watermark to the message before `messageId` (mark as unread).
288
+ * @param {string} channelId
289
+ * @param {string} messageId
290
+ * @returns {Promise<Object>}
291
+ */
292
+ async markUnread(channelId, messageId) {
293
+ this.sdk.validateParams(
294
+ { channelId, messageId },
295
+ {
296
+ channelId: { type: 'string', required: true },
297
+ messageId: { type: 'string', required: true },
298
+ },
299
+ );
300
+ return this.sdk._fetch(`/chat/channels/${channelId}/unread`, 'POST', {
301
+ body: { messageId },
302
+ });
303
+ }
304
+
305
+ /**
306
+ * Channels linked to a group via defaults (remove-from-group prompt).
307
+ * @param {Object} params
308
+ * @param {string} params.groupId
309
+ * @param {string} params.userId
310
+ * @returns {Promise<Object>}
311
+ */
312
+ async getLinkedChannels({ groupId, userId }) {
313
+ this.sdk.validateParams(
314
+ { groupId, userId },
315
+ {
316
+ groupId: { type: 'string', required: true },
317
+ userId: { type: 'string', required: true },
318
+ },
319
+ );
320
+ return this.sdk._fetch(`/chat/groups/${groupId}/linked-channels`, 'GET', {
321
+ query: { userId },
322
+ });
323
+ }
324
+
325
+ /**
326
+ * List messages in a channel (cursor pagination).
327
+ * @param {string} channelId
328
+ * @param {Object} [params]
329
+ * @param {string} [params.before]
330
+ * @param {string} [params.after]
331
+ * @param {number} [params.limit]
332
+ * @returns {Promise<Object>}
333
+ */
334
+ async listMessages(channelId, { before, after, limit } = {}) {
335
+ this.sdk.validateParams(
336
+ { channelId, before, after, limit },
337
+ {
338
+ channelId: { type: 'string', required: true },
339
+ before: { type: 'string', required: false },
340
+ after: { type: 'string', required: false },
341
+ limit: { type: 'number', required: false },
342
+ },
343
+ );
344
+
345
+ const query = {};
346
+ if (before !== undefined) query.before = before;
347
+ if (after !== undefined) query.after = after;
348
+ if (limit !== undefined) query.limit = limit;
349
+
350
+ return this.sdk._fetch(`/chat/channels/${channelId}/messages`, 'GET', {
351
+ query,
352
+ });
353
+ }
354
+
355
+ /**
356
+ * Send a message to a channel.
357
+ * @param {string} channelId
358
+ * @param {Object} params
359
+ * @param {Object} params.message - ProseMirror JSON (required)
360
+ * @param {string} [params.threadRootId]
361
+ * @param {boolean} [params.alsoSendToChannel]
362
+ * @param {string[]} [params.storageIds]
363
+ * @returns {Promise<Object>} Created message
364
+ */
365
+ async sendMessage(
366
+ channelId,
367
+ { message, threadRootId, alsoSendToChannel, storageIds } = {},
368
+ ) {
369
+ this.sdk.validateParams(
370
+ { channelId, message, threadRootId, alsoSendToChannel, storageIds },
371
+ {
372
+ channelId: { type: 'string', required: true },
373
+ message: { type: 'object', required: true },
374
+ threadRootId: { type: 'string', required: false },
375
+ alsoSendToChannel: { type: 'boolean', required: false },
376
+ storageIds: { type: 'array', required: false },
377
+ },
378
+ );
379
+
380
+ const body = { message };
381
+ if (threadRootId !== undefined) body.threadRootId = threadRootId;
382
+ if (alsoSendToChannel !== undefined) {
383
+ body.alsoSendToChannel = alsoSendToChannel;
384
+ }
385
+ if (storageIds !== undefined) body.storageIds = storageIds;
386
+
387
+ return this.sdk._fetch(`/chat/channels/${channelId}/messages`, 'POST', {
388
+ body,
389
+ });
390
+ }
391
+
392
+ /**
393
+ * Edit a message body (ProseMirror JSON).
394
+ * @param {string} id
395
+ * @param {Object} params
396
+ * @param {Object} params.message - ProseMirror JSON (required)
397
+ * @returns {Promise<Object>}
398
+ */
399
+ async editMessage(id, { message } = {}) {
400
+ this.sdk.validateParams(
401
+ { id, message },
402
+ {
403
+ id: { type: 'string', required: true },
404
+ message: { type: 'object', required: true },
405
+ },
406
+ );
407
+ return this.sdk._fetch(`/chat/messages/${id}`, 'PATCH', {
408
+ body: { message },
409
+ });
410
+ }
411
+
412
+ /**
413
+ * Delete a message (tombstone).
414
+ * @param {string} id
415
+ * @returns {Promise<Object>}
416
+ */
417
+ async deleteMessage(id) {
418
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
419
+ return this.sdk._fetch(`/chat/messages/${id}`, 'DELETE');
420
+ }
421
+
422
+ /**
423
+ * Add an emoji reaction to a message.
424
+ * @param {string} id
425
+ * @param {Object} params
426
+ * @param {string} params.emoji
427
+ * @returns {Promise<Object>}
428
+ */
429
+ async addReaction(id, { emoji } = {}) {
430
+ this.sdk.validateParams(
431
+ { id, emoji },
432
+ {
433
+ id: { type: 'string', required: true },
434
+ emoji: { type: 'string', required: true },
435
+ },
436
+ );
437
+ return this.sdk._fetch(`/chat/messages/${id}/reactions`, 'POST', {
438
+ body: { emoji },
439
+ });
440
+ }
441
+
442
+ /**
443
+ * Remove an emoji reaction from a message.
444
+ * @param {string} id
445
+ * @param {Object} params
446
+ * @param {string} params.emoji
447
+ * @returns {Promise<Object>}
448
+ */
449
+ async removeReaction(id, { emoji } = {}) {
450
+ this.sdk.validateParams(
451
+ { id, emoji },
452
+ {
453
+ id: { type: 'string', required: true },
454
+ emoji: { type: 'string', required: true },
455
+ },
456
+ );
457
+ return this.sdk._fetch(`/chat/messages/${id}/reactions`, 'DELETE', {
458
+ body: { emoji },
459
+ });
460
+ }
461
+
462
+ /**
463
+ * Get a thread (root + replies) for a channel message.
464
+ * @param {string} channelId
465
+ * @param {string} rootId
466
+ * @returns {Promise<Object>}
467
+ */
468
+ async getThread(channelId, rootId) {
469
+ this.sdk.validateParams(
470
+ { channelId, rootId },
471
+ {
472
+ channelId: { type: 'string', required: true },
473
+ rootId: { type: 'string', required: true },
474
+ },
475
+ );
476
+ return this.sdk._fetch(
477
+ `/chat/channels/${channelId}/messages/${rootId}/thread`,
478
+ 'GET',
479
+ );
480
+ }
481
+
482
+ /**
483
+ * Search messages the current user can read.
484
+ * @param {Object} params
485
+ * @param {string} params.q - Search term (required)
486
+ * @param {string} [params.channelId]
487
+ * @param {string} [params.fromUserId]
488
+ * @param {string} [params.before]
489
+ * @param {string} [params.after]
490
+ * @param {'public'|'private'|'dm'|'group_dm'|'record'|'meeting'} [params.kind]
491
+ * @param {string} [params.nextId]
492
+ * @param {number} [params.limit]
493
+ * @returns {Promise<Object>}
494
+ */
495
+ async search({
496
+ q,
497
+ channelId,
498
+ fromUserId,
499
+ before,
500
+ after,
501
+ kind,
502
+ nextId,
503
+ limit,
504
+ } = {}) {
505
+ this.sdk.validateParams(
506
+ { q, channelId, fromUserId, before, after, kind, nextId, limit },
507
+ {
508
+ q: { type: 'string', required: true },
509
+ channelId: { type: 'string', required: false },
510
+ fromUserId: { type: 'string', required: false },
511
+ before: { type: 'string', required: false },
512
+ after: { type: 'string', required: false },
513
+ kind: { type: 'string', required: false },
514
+ nextId: { type: 'string', required: false },
515
+ limit: { type: 'number', required: false },
516
+ },
517
+ );
518
+
519
+ const query = { q };
520
+ if (channelId !== undefined) query.channelId = channelId;
521
+ if (fromUserId !== undefined) query.fromUserId = fromUserId;
522
+ if (before !== undefined) query.before = before;
523
+ if (after !== undefined) query.after = after;
524
+ if (kind !== undefined) query.kind = kind;
525
+ if (nextId !== undefined) query.nextId = nextId;
526
+ if (limit !== undefined) query.limit = limit;
527
+
528
+ return this.sdk._fetch('/chat/search', 'GET', { query });
529
+ }
530
+
531
+ /**
532
+ * Create a channel incoming webhook (signing secret returned once).
533
+ * Button callback URLs are registered here — never taken from message payloads.
534
+ * @param {string} channelId
535
+ * @param {Object} params
536
+ * @param {string} params.name - Display name (required)
537
+ * @param {string} [params.avatar]
538
+ * @param {string} [params.callbackUrl] - Admin-registered button callback URL
539
+ * @returns {Promise<Object>} Created webhook (includes signingSecret once)
540
+ */
541
+ async createWebhook(channelId, { name, avatar, callbackUrl } = {}) {
542
+ this.sdk.validateParams(
543
+ { channelId, name, avatar, callbackUrl },
544
+ {
545
+ channelId: { type: 'string', required: true },
546
+ name: { type: 'string', required: true },
547
+ avatar: { type: 'string', required: false },
548
+ callbackUrl: { type: 'string', required: false },
549
+ },
550
+ );
551
+
552
+ const body = { name };
553
+ if (avatar !== undefined) body.avatar = avatar;
554
+ if (callbackUrl !== undefined) body.callbackUrl = callbackUrl;
555
+
556
+ return this.sdk._fetch(`/chat/channels/${channelId}/webhooks`, 'POST', {
557
+ body,
558
+ });
559
+ }
560
+
561
+ /**
562
+ * List webhooks for a channel.
563
+ * @param {string} channelId
564
+ * @returns {Promise<Object>}
565
+ */
566
+ async listWebhooks(channelId) {
567
+ this.sdk.validateParams(
568
+ { channelId },
569
+ { channelId: { type: 'string', required: true } },
570
+ );
571
+ return this.sdk._fetch(`/chat/channels/${channelId}/webhooks`, 'GET');
572
+ }
573
+
574
+ /**
575
+ * Revoke a channel webhook.
576
+ * @param {string} channelId
577
+ * @param {string} webhookId
578
+ * @returns {Promise<Object>}
579
+ */
580
+ async revokeWebhook(channelId, webhookId) {
581
+ this.sdk.validateParams(
582
+ { channelId, webhookId },
583
+ {
584
+ channelId: { type: 'string', required: true },
585
+ webhookId: { type: 'string', required: true },
586
+ },
587
+ );
588
+ return this.sdk._fetch(
589
+ `/chat/channels/${channelId}/webhooks/${webhookId}`,
590
+ 'DELETE',
591
+ );
592
+ }
593
+
594
+ /**
595
+ * Click a card action button on a message. Acting principal is the caller.
596
+ * @param {string} messageId
597
+ * @param {Object} params
598
+ * @param {string} params.actionId
599
+ * @param {string} [params.value]
600
+ * @returns {Promise<Object>}
601
+ */
602
+ async clickAction(messageId, { actionId, value } = {}) {
603
+ this.sdk.validateParams(
604
+ { messageId, actionId, value },
605
+ {
606
+ messageId: { type: 'string', required: true },
607
+ actionId: { type: 'string', required: true },
608
+ value: { type: 'string', required: false },
609
+ },
610
+ );
611
+
612
+ const body = { actionId };
613
+ if (value !== undefined) body.value = value;
614
+
615
+ return this.sdk._fetch(`/chat/messages/${messageId}/actions`, 'POST', {
616
+ body,
617
+ });
618
+ }
619
+
620
+ /**
621
+ * Report a message.
622
+ * @param {string} id
623
+ * @param {Object} params
624
+ * @param {string} params.reason
625
+ * @returns {Promise<Object>}
626
+ */
627
+ async reportMessage(id, { reason } = {}) {
628
+ this.sdk.validateParams(
629
+ { id, reason },
630
+ {
631
+ id: { type: 'string', required: true },
632
+ reason: { type: 'string', required: true },
633
+ },
634
+ );
635
+ return this.sdk._fetch(`/chat/messages/${id}/report`, 'POST', {
636
+ body: { reason },
637
+ });
638
+ }
639
+
640
+ /**
641
+ * Admin: list all channels (optional search/kind filter).
642
+ * @param {Object} [params]
643
+ * @param {string} [params.q]
644
+ * @param {'public'|'private'|'dm'|'group_dm'|'record'|'meeting'} [params.kind]
645
+ * @returns {Promise<Object>}
646
+ */
647
+ async adminListChannels({ q, kind } = {}) {
648
+ this.sdk.validateParams(
649
+ { q, kind },
650
+ {
651
+ q: { type: 'string', required: false },
652
+ kind: { type: 'string', required: false },
653
+ },
654
+ );
655
+
656
+ const query = {};
657
+ if (q !== undefined) query.q = q;
658
+ if (kind !== undefined) query.kind = kind;
659
+
660
+ return this.sdk._fetch('/chat/admin/channels', 'GET', { query });
661
+ }
662
+
663
+ /**
664
+ * Admin: get a channel by id (no membership required).
665
+ * @param {string} id
666
+ * @returns {Promise<Object>}
667
+ */
668
+ async adminGetChannel(id) {
669
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
670
+ return this.sdk._fetch(`/chat/admin/channels/${id}`, 'GET');
671
+ }
672
+
673
+ /**
674
+ * Admin: export a channel (messages + members).
675
+ * @param {string} id
676
+ * @returns {Promise<Object>}
677
+ */
678
+ async adminExportChannel(id) {
679
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
680
+ return this.sdk._fetch(`/chat/admin/channels/${id}/export`, 'GET');
681
+ }
682
+
683
+ /**
684
+ * Admin: list message reports.
685
+ * @param {Object} [params]
686
+ * @param {string} [params.status]
687
+ * @returns {Promise<Object>}
688
+ */
689
+ async adminListReports({ status } = {}) {
690
+ this.sdk.validateParams(
691
+ { status },
692
+ { status: { type: 'string', required: false } },
693
+ );
694
+
695
+ const query = {};
696
+ if (status !== undefined) query.status = status;
697
+
698
+ return this.sdk._fetch('/chat/admin/reports', 'GET', { query });
699
+ }
700
+
701
+ /**
702
+ * Admin: review a message report (set status).
703
+ * @param {string} id
704
+ * @param {Object} params
705
+ * @param {string} params.status
706
+ * @returns {Promise<Object>}
707
+ */
708
+ async adminReviewReport(id, { status } = {}) {
709
+ this.sdk.validateParams(
710
+ { id, status },
711
+ {
712
+ id: { type: 'string', required: true },
713
+ status: { type: 'string', required: true },
714
+ },
715
+ );
716
+ return this.sdk._fetch(`/chat/admin/reports/${id}`, 'POST', {
717
+ body: { status },
718
+ });
719
+ }
720
+
721
+ /**
722
+ * Admin: delete a message (moderation).
723
+ * @param {string} id
724
+ * @returns {Promise<Object>}
725
+ */
726
+ async adminDeleteMessage(id) {
727
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
728
+ return this.sdk._fetch(`/chat/admin/messages/${id}`, 'DELETE');
729
+ }
730
+
731
+ /**
732
+ * Admin: audit log of review actions.
733
+ * @returns {Promise<Object>}
734
+ */
735
+ async adminAudit() {
736
+ return this.sdk._fetch('/chat/admin/audit', 'GET');
737
+ }
738
+
739
+ /**
740
+ * Get (find-or-create) the record-feed channel for a related record.
741
+ * @param {string} relatedId
742
+ * @param {Object} params
743
+ * @param {string} params.recordTypeId
744
+ * @returns {Promise<Object>} Record-kind channel
745
+ */
746
+ async getRecordChannel(relatedId, { recordTypeId } = {}) {
747
+ this.sdk.validateParams(
748
+ { relatedId, recordTypeId },
749
+ {
750
+ relatedId: { type: 'string', required: true },
751
+ recordTypeId: { type: 'string', required: true },
752
+ },
753
+ );
754
+ return this.sdk._fetch(`/chat/records/${relatedId}`, 'GET', {
755
+ query: { recordTypeId },
756
+ });
757
+ }
758
+
759
+ /**
760
+ * Post a message to a record-feed channel (find-or-create).
761
+ * @param {string} relatedId
762
+ * @param {Object} params
763
+ * @param {Object} params.message - ProseMirror JSON (required)
764
+ * @param {string} params.recordTypeId
765
+ * @returns {Promise<Object>} Created message
766
+ */
767
+ async postToRecord(relatedId, { message, recordTypeId } = {}) {
768
+ this.sdk.validateParams(
769
+ { relatedId, message, recordTypeId },
770
+ {
771
+ relatedId: { type: 'string', required: true },
772
+ message: { type: 'object', required: true },
773
+ recordTypeId: { type: 'string', required: true },
774
+ },
775
+ );
776
+ return this.sdk._fetch(`/chat/records/${relatedId}/messages`, 'POST', {
777
+ body: { message, recordTypeId },
778
+ });
779
+ }
780
+
781
+ /**
782
+ * Get the Meet/Call room for a channel.
783
+ * @param {string} channelId
784
+ * @returns {Promise<Object>}
785
+ */
786
+ async getChannelMeet(channelId) {
787
+ this.sdk.validateParams(
788
+ { channelId },
789
+ { channelId: { type: 'string', required: true } },
790
+ );
791
+ return this.sdk._fetch(`/chat/channels/${channelId}/meet`, 'GET');
792
+ }
793
+
794
+ /**
795
+ * Get the VAPID public key for Web Push subscription.
796
+ * @returns {Promise<Object>}
797
+ */
798
+ async getVapidPublicKey() {
799
+ return this.sdk._fetch('/chat/push/vapidPublicKey', 'GET');
800
+ }
801
+
802
+ /**
803
+ * Register a push device (web push subscription or native FCM/APNs token).
804
+ * @param {Object} params
805
+ * @param {'webpush'|'fcm'|'apns'} params.kind
806
+ * @param {Object} params.subscription - Push subscription / token JSON
807
+ * @returns {Promise<Object>}
808
+ */
809
+ async registerPushDevice({ kind, subscription } = {}) {
810
+ this.sdk.validateParams(
811
+ { kind, subscription },
812
+ {
813
+ kind: { type: 'string', required: true },
814
+ subscription: { type: 'object', required: true },
815
+ },
816
+ );
817
+ return this.sdk._fetch('/chat/push/devices', 'POST', {
818
+ body: { kind, subscription },
819
+ });
820
+ }
821
+
822
+ /**
823
+ * Unregister a push device.
824
+ * @param {string} id
825
+ * @returns {Promise<Object>}
826
+ */
827
+ async unregisterPushDevice(id) {
828
+ this.sdk.validateParams({ id }, { id: { type: 'string', required: true } });
829
+ return this.sdk._fetch(`/chat/push/devices/${id}`, 'DELETE');
830
+ }
831
+
832
+ /**
833
+ * Set the caller's notifyLevel on a channel (all | mentions | mute).
834
+ * @param {string} channelId
835
+ * @param {Object} params
836
+ * @param {'all'|'mentions'|'mute'} params.notifyLevel
837
+ * @returns {Promise<Object>}
838
+ */
839
+ async setNotifyLevel(channelId, { notifyLevel } = {}) {
840
+ this.sdk.validateParams(
841
+ { channelId, notifyLevel },
842
+ {
843
+ channelId: { type: 'string', required: true },
844
+ notifyLevel: { type: 'string', required: true },
845
+ },
846
+ );
847
+ return this.sdk._fetch(`/chat/channels/${channelId}/notify`, 'PATCH', {
848
+ body: { notifyLevel },
849
+ });
850
+ }
851
+ }