@ethora/sdk-backend 26.2.3 → 26.3.1

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/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  This guide will walk you through integrating the Ethora SDK into your existing Node.js backend application.
6
6
 
7
+ **Part of the [Ethora SDK ecosystem](https://github.com/dappros/ethora#ecosystem)** — see all SDKs, tools, and sample apps. Follow cross-SDK updates in the [Release Notes](https://github.com/dappros/ethora/blob/main/RELEASE-NOTES.md).
8
+
7
9
  ## Table of Contents
8
10
 
9
11
  - [Prerequisites](#prerequisites)
@@ -36,6 +38,38 @@ Ethora exposes Swagger UI from every running backend instance at:
36
38
 
37
39
  If you are running a separate staging instance, the same pattern applies (e.g. `https://api.asterotoken.com/api-docs/`).
38
40
 
41
+ ## Tenant Admin / B2B Endpoints
42
+
43
+ The SDK also exposes the explicit tenant-admin surface added in the backend:
44
+
45
+ - `listApps()`, `getApp(appId)`, `createApp(appData)`, `deleteApp(appId)`
46
+ - `createUsersInApp(appId, payload)`, `getUsersBatchJob(appId, jobId)`, `deleteUsersInApp(appId, userIds)`
47
+ - `createChatRoomInApp(appId, chatId, roomData)`, `deleteChatRoomInApp(appId, chatId)`
48
+ - `grantUserAccessToChatRoomInApp(appId, chatId, userIds)`, `removeUserAccessFromChatRoomInApp(appId, chatId, userIds)`
49
+ - `getUserChatsInApp(appId, userId, params)`, `updateChatRoomInApp(appId, chatId, updateData)`
50
+
51
+ These helpers target explicit `/v2/apps/{appId}/...` routes so a parent-app / tenant backend can manage child apps without relying on implicit token scope.
52
+
53
+ ## Token Types
54
+
55
+ The Ethora API uses several JWT/token types with different purposes:
56
+
57
+ - `B2B Server JWT`: used by this SDK automatically for server-to-server API calls. The backend accepts it primarily via `x-custom-token`, and many deployments also accept it in `Authorization: Bearer ...`.
58
+ - `Client JWT`: created by `createChatUserJwtToken(userId)` for client/chat credential flows only.
59
+ - `User JWT`: returned by Ethora login endpoints and used for user-session API calls outside this SDK's main server-to-server flow.
60
+ - `App JWT`: legacy app-scoped runtime token. Frontend/bootstrap login and sign-up flows now prefer explicit `appId`, while old app-token auth remains accepted for backward compatibility.
61
+
62
+ If you are using explicit tenant-admin routes like `/v2/apps/{appId}/...`, the intended token for backend integrations is `B2B Server JWT`.
63
+
64
+ ## API Versioning
65
+
66
+ For new integrations, prefer `/v2/...` endpoints.
67
+
68
+ - Main runtime chat/user operations in this SDK use `/v2/...`.
69
+ - Explicit tenant-admin helpers use `/v2/apps/{appId}/...`.
70
+ - A few delete operations still map to legacy `/v1/...` endpoints because that is where backend parity currently exists.
71
+ - Frontend/bootstrap auth flows are moving toward explicit `appId` request context rather than relying on implicit app-token scope.
72
+
39
73
  ## Installation
40
74
 
41
75
  ### Step 1: Install the Package
@@ -141,10 +175,20 @@ export class ChatService {
141
175
  // routes/chat.ts
142
176
  import express, { Request, Response } from 'express';
143
177
  import { getEthoraSDKService } from '@ethora/sdk-backend';
178
+ import type {
179
+ ChatRepository,
180
+ ApiResponse,
181
+ CreateChatRoomRequest,
182
+ CreateUserData,
183
+ UpdateUserData,
184
+ GetUsersQueryParams,
185
+ GetUserChatsQueryParams,
186
+ UUID
187
+ } from '@ethora/sdk-backend';
144
188
  import axios from 'axios';
145
189
 
146
190
  const router = express.Router();
147
- const chatService = getEthoraSDKService();
191
+ const chatService: ChatRepository = getEthoraSDKService();
148
192
 
149
193
  // Create a chat room for a workspace
150
194
  router.post(
@@ -152,9 +196,9 @@ router.post(
152
196
  async (req: Request, res: Response) => {
153
197
  try {
154
198
  const { workspaceId } = req.params;
155
- const roomData = req.body;
199
+ const roomData: Partial<CreateChatRoomRequest> = req.body;
156
200
 
157
- const response = await chatService.createChatRoom(workspaceId, {
201
+ const response: ApiResponse = await chatService.createChatRoom(workspaceId, {
158
202
  title: roomData.title || `Chat Room ${workspaceId}`,
159
203
  uuid: workspaceId,
160
204
  type: roomData.type || 'group',
@@ -179,9 +223,9 @@ router.post(
179
223
  router.post('/users/:userId', async (req: Request, res: Response) => {
180
224
  try {
181
225
  const { userId } = req.params;
182
- const userData = req.body;
226
+ const userData: CreateUserData = req.body;
183
227
 
184
- const response = await chatService.createUser(userId, userData);
228
+ const response: ApiResponse = await chatService.createUser(userId, userData);
185
229
  res.json({ success: true, data: response });
186
230
  } catch (error) {
187
231
  if (axios.isAxiosError(error)) {
@@ -254,11 +298,11 @@ router.get('/users/:userId/chat-token', (req: Request, res: Response) => {
254
298
  router.get('/users', async (req: Request, res: Response) => {
255
299
  try {
256
300
  const { chatName, xmppUsername } = req.query;
257
- const params: any = {};
301
+ const params: GetUsersQueryParams = {};
258
302
  if (chatName) params.chatName = String(chatName);
259
303
  if (xmppUsername) params.xmppUsername = String(xmppUsername);
260
304
 
261
- const response = await chatService.getUsers(
305
+ const response: ApiResponse = await chatService.getUsers(
262
306
  Object.keys(params).length > 0 ? params : undefined,
263
307
  );
264
308
  res.json({ success: true, data: response });
@@ -277,17 +321,12 @@ router.get('/users', async (req: Request, res: Response) => {
277
321
  // Update users (batch)
278
322
  router.patch('/users', async (req: Request, res: Response) => {
279
323
  try {
280
- const { users } = req.body;
324
+ const { users } = req.body as { users: UpdateUserData[] };
281
325
  if (!Array.isArray(users) || users.length === 0) {
282
326
  return res.status(400).json({ error: 'users must be a non-empty array' });
283
327
  }
284
- if (users.length > 100) {
285
- return res
286
- .status(400)
287
- .json({ error: 'Maximum 100 users allowed per request' });
288
- }
289
328
 
290
- const response = await chatService.updateUsers(users);
329
+ const response: ApiResponse = await chatService.updateUsers(users);
291
330
  res.json({ success: true, data: response });
292
331
  } catch (error) {
293
332
  if (axios.isAxiosError(error)) {
@@ -301,6 +340,33 @@ router.patch('/users', async (req: Request, res: Response) => {
301
340
  }
302
341
  });
303
342
 
343
+ // Update chat room
344
+ router.patch(
345
+ '/workspaces/:workspaceId/chat',
346
+ async (req: Request, res: Response) => {
347
+ try {
348
+ const { workspaceId } = req.params;
349
+ const updateData: { title?: string; description?: string } = req.body;
350
+ const response: ApiResponse = await chatService.updateChatRoom(workspaceId, updateData);
351
+ res.json({ success: true, data: response });
352
+ } catch (error) {
353
+ res.status(500).json({ error: 'Failed to update chat room' });
354
+ }
355
+ },
356
+ );
357
+
358
+ // Get user chats
359
+ router.get('/users/:userId/chats', async (req: Request, res: Response) => {
360
+ try {
361
+ const { userId } = req.params;
362
+ const query: GetUserChatsQueryParams = req.query as unknown as GetUserChatsQueryParams;
363
+ const response: ApiResponse = await chatService.getUserChats(userId, query);
364
+ res.json({ success: true, data: response });
365
+ } catch (error) {
366
+ res.status(500).json({ error: 'Failed to get user chats' });
367
+ }
368
+ });
369
+
304
370
  export default router;
305
371
  ```
306
372
 
@@ -314,9 +380,9 @@ import axios from 'axios';
314
380
 
315
381
  @Injectable()
316
382
  export class ChatService {
317
- private readonly ethoraService = getEthoraSDKService();
383
+ private readonly ethoraService: ChatRepository = getEthoraSDKService();
318
384
 
319
- async createChatRoom(workspaceId: string, roomData?: any) {
385
+ async createChatRoom(workspaceId: string, roomData?: Partial<CreateChatRoomRequest>): Promise<ApiResponse> {
320
386
  try {
321
387
  return await this.ethoraService.createChatRoom(workspaceId, roomData);
322
388
  } catch (error) {
@@ -333,7 +399,7 @@ export class ChatService {
333
399
  }
334
400
  }
335
401
 
336
- async createUser(userId: string, userData?: any) {
402
+ async createUser(userId: string, userData?: CreateUserData): Promise<ApiResponse> {
337
403
  try {
338
404
  return await this.ethoraService.createUser(userId, userData);
339
405
  } catch (error) {
@@ -366,13 +432,16 @@ export class ChatController {
366
432
  @Post('workspaces/:workspaceId/rooms')
367
433
  async createChatRoom(
368
434
  @Param('workspaceId') workspaceId: string,
369
- @Body() roomData: any,
370
- ) {
435
+ @Body() roomData: Partial<CreateChatRoomRequest>,
436
+ ): Promise<ApiResponse> {
371
437
  return this.chatService.createChatRoom(workspaceId, roomData);
372
438
  }
373
439
 
374
440
  @Post('users/:userId')
375
- async createUser(@Param('userId') userId: string, @Body() userData: any) {
441
+ async createUser(
442
+ @Param('userId') userId: string,
443
+ @Body() userData: CreateUserData
444
+ ): Promise<ApiResponse> {
376
445
  return this.chatService.createUser(userId, userData);
377
446
  }
378
447
 
@@ -396,12 +465,12 @@ export async function chatRoutes(fastify: FastifyInstance) {
396
465
  // Create chat room
397
466
  fastify.post(
398
467
  '/workspaces/:workspaceId/chat',
399
- async (request: FastifyRequest, reply: FastifyReply) => {
468
+ async (request: FastifyRequest, reply: FastifyReply): Promise<ApiResponse | void> => {
400
469
  const { workspaceId } = request.params as { workspaceId: string };
401
- const roomData = request.body as any;
470
+ const roomData = request.body as Partial<CreateChatRoomRequest>;
402
471
 
403
472
  try {
404
- const response = await chatService.createChatRoom(
473
+ const response: ApiResponse = await chatService.createChatRoom(
405
474
  workspaceId,
406
475
  roomData,
407
476
  );
@@ -426,6 +495,33 @@ export async function chatRoutes(fastify: FastifyInstance) {
426
495
 
427
496
  ## Common Use Cases
428
497
 
498
+ ### Use Case 0: Tenant-admin / child-app management
499
+
500
+ Create and manage a child app through the explicit B2B admin surface:
501
+
502
+ ```typescript
503
+ const sdk = getEthoraSDKService();
504
+
505
+ const app = await sdk.createApp({ displayName: 'Tenant Managed Demo' });
506
+ const childAppId = String((app as any).app?._id || (app as any).result?._id || '');
507
+
508
+ await sdk.createUsersInApp(childAppId, {
509
+ bypassEmailConfirmation: true,
510
+ usersList: [
511
+ { uuid: 'workspace-u1', email: 'workspace-u1@example.com', firstName: 'Workspace', lastName: 'One' },
512
+ { uuid: 'workspace-u2', email: 'workspace-u2@example.com', firstName: 'Workspace', lastName: 'Two' },
513
+ ],
514
+ });
515
+
516
+ await sdk.createChatRoomInApp(childAppId, 'workspace-room', {
517
+ title: 'Workspace Room',
518
+ uuid: 'workspace-room',
519
+ type: 'public',
520
+ });
521
+
522
+ await sdk.grantUserAccessToChatRoomInApp(childAppId, 'workspace-room', ['workspace-u1', 'workspace-u2']);
523
+ ```
524
+
429
525
  ### Use Case 1: Workspace Setup Flow
430
526
 
431
527
  When creating a new workspace, set up the chat room and initial users:
@@ -435,8 +531,8 @@ async function setupWorkspaceChat(
435
531
  workspaceId: string,
436
532
  userIds: string[],
437
533
  adminUserId: string,
438
- ) {
439
- const chatService = getEthoraSDKService();
534
+ ): Promise<{ success: boolean }> {
535
+ const chatService: ChatRepository = getEthoraSDKService();
440
536
 
441
537
  try {
442
538
  // 1. Create chat room
@@ -485,8 +581,8 @@ When a new user joins your platform:
485
581
  async function onboardNewUser(
486
582
  userId: string,
487
583
  userData: { firstName: string; lastName: string; email: string },
488
- ) {
489
- const chatService = getEthoraSDKService();
584
+ ): Promise<{ success: boolean; chatToken: string }> {
585
+ const chatService: ChatRepository = getEthoraSDKService();
490
586
 
491
587
  try {
492
588
  // Create user in chat service
@@ -498,7 +594,7 @@ async function onboardNewUser(
498
594
  });
499
595
 
500
596
  // Generate client token for frontend
501
- const clientToken = chatService.createChatUserJwtToken(userId);
597
+ const clientToken: string = chatService.createChatUserJwtToken(userId);
502
598
 
503
599
  return {
504
600
  success: true,
@@ -516,8 +612,11 @@ async function onboardNewUser(
516
612
  When adding a user to an existing workspace:
517
613
 
518
614
  ```typescript
519
- async function addUserToWorkspace(workspaceId: string, userId: string) {
520
- const chatService = getEthoraSDKService();
615
+ async function addUserToWorkspace(
616
+ workspaceId: string,
617
+ userId: string,
618
+ ): Promise<{ success: boolean }> {
619
+ const chatService: ChatRepository = getEthoraSDKService();
521
620
 
522
621
  try {
523
622
  // Ensure user exists
@@ -543,8 +642,11 @@ async function addUserToWorkspace(workspaceId: string, userId: string) {
543
642
  When removing a user from a workspace:
544
643
 
545
644
  ```typescript
546
- async function removeUserFromWorkspace(workspaceId: string, userId: string) {
547
- const chatService = getEthoraSDKService();
645
+ async function removeUserFromWorkspace(
646
+ workspaceId: string,
647
+ userId: string,
648
+ ): Promise<{ success: boolean }> {
649
+ const chatService: ChatRepository = getEthoraSDKService();
548
650
 
549
651
  try {
550
652
  // Remove access from workspace chat room
@@ -561,8 +663,8 @@ async function removeUserFromWorkspace(workspaceId: string, userId: string) {
561
663
  async function removeMultipleUsersFromWorkspace(
562
664
  workspaceId: string,
563
665
  userIds: string[],
564
- ) {
565
- const chatService = getEthoraSDKService();
666
+ ): Promise<{ success: boolean }> {
667
+ const chatService: ChatRepository = getEthoraSDKService();
566
668
 
567
669
  try {
568
670
  await chatService.removeUserAccessFromChatRoom(workspaceId, userIds);
@@ -580,8 +682,11 @@ async function removeMultipleUsersFromWorkspace(
580
682
  When deleting a workspace:
581
683
 
582
684
  ```typescript
583
- async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
584
- const chatService = getEthoraSDKService();
685
+ async function cleanupWorkspaceChat(
686
+ workspaceId: string,
687
+ userIds: string[],
688
+ ): Promise<{ success: boolean }> {
689
+ const chatService: ChatRepository = getEthoraSDKService();
585
690
 
586
691
  try {
587
692
  // Delete chat room (handles non-existent gracefully)
@@ -609,21 +714,25 @@ async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
609
714
  Retrieve users from the chat service:
610
715
 
611
716
  ```typescript
612
- async function getUsersExample() {
613
- const chatService = getEthoraSDKService();
717
+ async function getUsersExample(): Promise<{
718
+ allUsers: ApiResponse;
719
+ groupChatUsers: ApiResponse;
720
+ oneOnOneUsers: ApiResponse
721
+ }> {
722
+ const chatService: ChatRepository = getEthoraSDKService();
614
723
 
615
724
  try {
616
725
  // Get all users
617
- const allUsers = await chatService.getUsers();
726
+ const allUsers: ApiResponse = await chatService.getUsers();
618
727
  console.log(`Total users: ${allUsers.results?.length || 0}`);
619
728
 
620
729
  // Get users by chat name (group chat)
621
- const groupChatUsers = await chatService.getUsers({
730
+ const groupChatUsers: ApiResponse = await chatService.getUsers({
622
731
  chatName: 'appId_workspaceId',
623
732
  });
624
733
 
625
734
  // Get users by chat name (1-on-1 chat)
626
- const oneOnOneUsers = await chatService.getUsers({
735
+ const oneOnOneUsers: ApiResponse = await chatService.getUsers({
627
736
  chatName: 'userA-userB',
628
737
  });
629
738
 
@@ -640,41 +749,65 @@ async function getUsersExample() {
640
749
  Update multiple users at once:
641
750
 
642
751
  ```typescript
643
- async function updateUsersExample() {
644
- const chatService = getEthoraSDKService();
752
+ async function updateUsersExample(): Promise<ApiResponse> {
753
+ const chatService: ChatRepository = getEthoraSDKService();
645
754
 
646
755
  try {
647
756
  // Update multiple users (1-100 users per request)
648
- const response = await chatService.updateUsers([
757
+ const response: ApiResponse = await chatService.updateUsers([
649
758
  {
650
759
  xmppUsername: 'appId_user1',
651
760
  firstName: 'John',
652
761
  lastName: 'Doe',
653
- username: 'johndoe',
654
- profileImage: 'https://example.com/avatar1.jpg',
655
- },
656
- {
657
- xmppUsername: 'appId_user2',
658
- firstName: 'Jane',
659
- lastName: 'Smith',
660
- username: 'janesmith',
661
- },
762
+ }
662
763
  ]);
663
764
 
664
- // Check results
665
- response.results?.forEach((result: any) => {
666
- if (result.status === 'updated') {
667
- console.log(`User ${result.xmppUsername} updated successfully`);
668
- } else if (result.status === 'not-found') {
669
- console.warn(`User ${result.xmppUsername} not found`);
670
- } else if (result.status === 'skipped') {
671
- console.log(`User ${result.xmppUsername} update skipped`);
672
- }
765
+ return response;
766
+ } catch (error) {
767
+ console.error('Failed to update users:', error);
768
+ throw error;
769
+ }
770
+ }
771
+ ```
772
+
773
+ ### Use Case 8: Updating Chat Room Metadata
774
+
775
+ Update room title or description:
776
+
777
+ ```typescript
778
+ async function updateRoomExample(): Promise<ApiResponse> {
779
+ const chatService: ChatRepository = getEthoraSDKService();
780
+
781
+ try {
782
+ const response: ApiResponse = await chatService.updateChatRoom('workspaceId', {
783
+ title: 'New Room Title',
784
+ description: 'New Description',
673
785
  });
786
+ return response;
787
+ } catch (error) {
788
+ console.error('Failed to update room:', error);
789
+ throw error;
790
+ }
791
+ }
792
+ ```
674
793
 
794
+ ### Use Case 9: Getting User Chats
795
+
796
+ Retrieve all rooms the user has access to:
797
+
798
+ ```typescript
799
+ async function getUserChatsExample(): Promise<ApiResponse> {
800
+ const chatService: ChatRepository = getEthoraSDKService();
801
+
802
+ try {
803
+ const query: GetUserChatsQueryParams = {
804
+ limit: 20,
805
+ includeMembers: true
806
+ };
807
+ const response: ApiResponse = await chatService.getUserChats('userId', query);
675
808
  return response;
676
809
  } catch (error) {
677
- console.error('Failed to update users:', error);
810
+ console.error('Failed to get user chats:', error);
678
811
  throw error;
679
812
  }
680
813
  }
@@ -682,39 +815,80 @@ async function updateUsersExample() {
682
815
 
683
816
  ## API Reference
684
817
 
818
+ ### Tenant-admin methods
819
+
820
+ #### `createApp(appData: CreateAppRequest): Promise<ApiResponse>`
821
+
822
+ Creates a child app through `POST /v2/apps`.
823
+
824
+ #### `createUsersInApp(appId: UUID, payload: BatchCreateUsersRequest): Promise<ApiResponse>`
825
+
826
+ Starts an async user-batch job through `POST /v2/apps/{appId}/users/batch`.
827
+
828
+ #### `createChatRoomInApp(appId: UUID, chatId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>`
829
+
830
+ Creates a chat in a target app through `POST /v2/apps/{appId}/chats`.
831
+
832
+ #### `grantUserAccessToChatRoomInApp(appId: UUID, chatId: UUID, userId: UUID | UUID[]): Promise<ApiResponse>`
833
+
834
+ Adds user access in a target app through `POST /v2/apps/{appId}/chats/users-access`.
835
+
836
+ #### `deleteChatRoomInApp(appId: UUID, chatId: UUID): Promise<ApiResponse>`
837
+
838
+ Deletes a chat in a target app through `DELETE /v2/apps/{appId}/chats`.
839
+
685
840
  ### Core Methods
686
841
 
687
- #### `createUser(userId: UUID, userData?: Record<string, unknown>): Promise<ApiResponse>`
842
+ #### `createUser(userId: UUID, userData?: CreateUserData): Promise<ApiResponse>`
688
843
 
689
844
  Creates a user in the chat service using the `/v2/users/batch` endpoint.
690
845
 
691
- **Parameters:**
692
- - `userId` (UUID): The unique identifier of the user
693
- - `userData` (optional): Additional user data
694
- - `firstName` (string): User's first name
695
- - `lastName` (string): User's last name (minimum 2 characters)
696
- - `email` (string): User's email address
697
- - `password` (string): User's password
698
- - `displayName` (string): Display name (will be split into firstName/lastName if needed)
846
+ **Interface: `CreateUserData`**
847
+ ```typescript
848
+ interface CreateUserData {
849
+ email: string; // string: User's email address
850
+ firstName: string; // string: User's first name
851
+ lastName: string; // string: User's last name (min 2 chars)
852
+ password?: string; // string (optional): User's password
853
+ displayName?: string; // string (optional): Full display name
854
+ }
855
+ ```
699
856
 
700
- **Returns:** Promise resolving to the API response
857
+ **Example Request:**
858
+ ```typescript
859
+ await sdk.createUser("user-uuid-123", {
860
+ email: "john@example.com",
861
+ firstName: "John",
862
+ lastName: "Doe"
863
+ });
864
+ ```
701
865
 
702
866
  **Note:** The API requires `lastName` to be at least 2 characters. If not provided or too short, defaults to "User".
703
867
 
704
868
  ---
705
869
 
706
- #### `createChatRoom(chatId: UUID, roomData?: Record<string, unknown>): Promise<ApiResponse>`
870
+ #### `createChatRoom(chatId: UUID, roomData?: CreateChatRoomRequest): Promise<ApiResponse>`
707
871
 
708
872
  Creates a chat room using the `/v2/chats` endpoint.
709
873
 
710
- **Parameters:**
711
- - `chatId` (UUID): The unique identifier of the chat/workspace
712
- - `roomData` (optional): Room configuration
713
- - `title` (string): Chat room title
714
- - `uuid` (string): Room UUID (defaults to chatId)
715
- - `type` (string): Room type (defaults to "group")
874
+ **Interface: `CreateChatRoomRequest`**
875
+ ```typescript
876
+ interface CreateChatRoomRequest {
877
+ title: string; // string: The display name of the chat room
878
+ uuid: string; // string: The workspace/chat identifier
879
+ type: string; // string: The room type (e.g., "group")
880
+ }
881
+ ```
716
882
 
717
- **Returns:** Promise resolving to the API response
883
+ **Example Request:**
884
+ ```typescript
885
+ const roomData: CreateChatRoomRequest = {
886
+ title: "Engineering",
887
+ uuid: "room-abc-123",
888
+ type: "group"
889
+ };
890
+ await sdk.createChatRoom("room-abc-123", roomData);
891
+ ```
718
892
 
719
893
  ---
720
894
 
@@ -722,11 +896,14 @@ Creates a chat room using the `/v2/chats` endpoint.
722
896
 
723
897
  Grants user(s) access to a chat room using the `/v2/chats/users-access` endpoint.
724
898
 
725
- **Parameters:**
726
- - `chatId` (UUID): The unique identifier of the chat/workspace
727
- - `userId` (UUID | UUID[]): Single user ID or array of user IDs
899
+ **Example Request:**
900
+ ```typescript
901
+ // Single user
902
+ await sdk.grantUserAccessToChatRoom("workspace-123", "user-uuid-456");
728
903
 
729
- **Returns:** Promise resolving to the API response
904
+ // Multiple users
905
+ await sdk.grantUserAccessToChatRoom("workspace-123", ["user-1", "user-2"]);
906
+ ```
730
907
 
731
908
  **Note:** User IDs are automatically prefixed with `{appId}_` if they don't already have the prefix.
732
909
 
@@ -736,11 +913,10 @@ Grants user(s) access to a chat room using the `/v2/chats/users-access` endpoint
736
913
 
737
914
  Removes user(s) access from a chat room using the `/v2/chats/users-access` DELETE endpoint.
738
915
 
739
- **Parameters:**
740
- - `chatId` (UUID): The unique identifier of the chat/workspace
741
- - `userId` (UUID | UUID[]): Single user ID or array of user IDs to remove
742
-
743
- **Returns:** Promise resolving to the API response
916
+ **Example Request:**
917
+ ```typescript
918
+ await sdk.removeUserAccessFromChatRoom("workspace-123", "user-456");
919
+ ```
744
920
 
745
921
  **Note:** User IDs are automatically prefixed with `{appId}_` if they don't already have the prefix.
746
922
 
@@ -783,20 +959,79 @@ Retrieves users from the chat service using the `/v2/chats/users` endpoint.
783
959
 
784
960
  Updates multiple users at once using the `/v2/chats/users` PATCH endpoint.
785
961
 
786
- **Parameters:**
787
- - `users` (UpdateUserData[]): Array of user data to update (1-100 users)
788
- - `xmppUsername` (string, required): XMPP username to identify the user
789
- - `firstName` (string, optional): First name
790
- - `lastName` (string, optional): Last name
791
- - `username` (string, optional): Username
792
- - `profileImage` (string, optional): Profile image URL
962
+ **Interface: `UpdateUserData`**
963
+ ```typescript
964
+ interface UpdateUserData {
965
+ xmppUsername: string; // string: Required (format: {appId}_{userId})
966
+ firstName?: string; // string (optional): New first name
967
+ lastName?: string; // string (optional): New last name
968
+ username?: string; // string (optional): New username
969
+ profileImage?: string; // string (optional): URL to profile image
970
+ description?: string; // string (optional): User bio/description
971
+ email?: string; // string (optional): New email address
972
+ }
973
+ ```
793
974
 
794
- **Returns:** Promise resolving to the API response with results array
975
+ **Example Request:**
976
+ ```typescript
977
+ await sdk.updateUsers([
978
+ { xmppUsername: "appId_user1", firstName: "NewName" }
979
+ ]);
980
+ ```
795
981
 
796
- **Response Status Values:**
797
- - `updated`: User was successfully updated (includes updated user data)
798
- - `not-found`: User was not found
799
- - `skipped`: User update was skipped
982
+ ---
983
+
984
+ #### `getUserChats(userId: UUID, params?: GetUserChatsQueryParams): Promise<ApiResponse>`
985
+
986
+ Retrieves all rooms the user has access to.
987
+
988
+ **Interface: `GetUserChatsQueryParams`**
989
+ ```typescript
990
+ interface GetUserChatsQueryParams {
991
+ limit?: number; // number (optional): Pagination limit
992
+ offset?: number; // number (optional): Pagination offset
993
+ includeMembers?: boolean; // boolean (optional): Whether to return member lists
994
+ }
995
+ ```
996
+
997
+ **Example Request:**
998
+ ```typescript
999
+ const query: GetUserChatsQueryParams = { limit: 50, includeMembers: true };
1000
+ await sdk.getUserChats("user-uuid-123", query);
1001
+ ```
1002
+
1003
+ ---
1004
+
1005
+ #### `updateChatRoom(chatId: UUID, updateData: { title?: string; description?: string }): Promise<ApiResponse>`
1006
+
1007
+ Updates the metadata for a specific chat room.
1008
+
1009
+ **Example Request:**
1010
+ ```typescript
1011
+ await sdk.updateChatRoom("workspace-123", {
1012
+ title: "New Team Title",
1013
+ description: "Updated room description"
1014
+ });
1015
+ ```
1016
+
1017
+ ---
1018
+
1019
+ #### `getUsers(params?: GetUsersQueryParams): Promise<ApiResponse>`
1020
+
1021
+ Retrieves users from the chat service using the `/v2/chats/users` endpoint.
1022
+
1023
+ **Interface: `GetUsersQueryParams`**
1024
+ ```typescript
1025
+ interface GetUsersQueryParams {
1026
+ chatName?: string; // string (optional): Filter by roomId (appId_roomId)
1027
+ xmppUsername?: string; // string (optional): Filter by specific JID
1028
+ }
1029
+ ```
1030
+
1031
+ **Example Request:**
1032
+ ```typescript
1033
+ await sdk.getUsers({ chatName: "appId_workspace-123" });
1034
+ ```
800
1035
 
801
1036
  **Limits:** 1-100 users per request
802
1037
 
@@ -806,10 +1041,10 @@ Updates multiple users at once using the `/v2/chats/users` PATCH endpoint.
806
1041
 
807
1042
  Deletes users from the chat service using the `/v1/users/batch` endpoint.
808
1043
 
809
- **Parameters:**
810
- - `userIds` (UUID[]): Array of user IDs to delete
811
-
812
- **Returns:** Promise resolving to the API response
1044
+ **Example Request:**
1045
+ ```typescript
1046
+ await sdk.deleteUsers(["user-id-1", "user-id-2"]);
1047
+ ```
813
1048
 
814
1049
  **Note:** Gracefully handles non-existent users (422 status with "not found").
815
1050
 
@@ -819,10 +1054,10 @@ Deletes users from the chat service using the `/v1/users/batch` endpoint.
819
1054
 
820
1055
  Deletes a chat room using the `/v1/chats` endpoint.
821
1056
 
822
- **Parameters:**
823
- - `chatId` (UUID): The unique identifier of the chat/workspace
824
-
825
- **Returns:** Promise resolving to the API response
1057
+ **Example Request:**
1058
+ ```typescript
1059
+ await sdk.deleteChatRoom("workspace-uuid-123");
1060
+ ```
826
1061
 
827
1062
  **Note:** Gracefully handles non-existent rooms (422 status with "not found").
828
1063
 
@@ -853,6 +1088,8 @@ Creates a client-side JWT token for user authentication.
853
1088
 
854
1089
  **Returns:** The encoded JWT token for client-side authentication
855
1090
 
1091
+ **Important:** Pass the same canonical user ID/UUID that your backend uses when creating that user in Ethora. Do not switch between different user-id formats when creating the user and then minting the client token.
1092
+
856
1093
  ---
857
1094
 
858
1095
  ## Error Handling