# API Overview Source: https://docs.collab-kit.com/api-reference/overview Base URL, authentication, response format, and error codes for the CollabKit REST API. The CollabKit REST API lets you manage rooms, users, files, webhooks, and workflows. All real-time features (presence, stores, comments, broadcasts) use the [WebSocket protocol](/api-reference/websocket) instead. ## Base URL ``` https://api.collab-kit.com ``` Replace with your deployed CollabKit server URL. ## Authentication Most endpoints require authentication via a Bearer token. See the [Authentication guide](/authentication) for full details. ### Bearer Token Construct the token by Base64-encoding `accountId:apiKey`: ```bash theme={null} TOKEN=$(echo -n "${ACCOUNT_ID}:${API_KEY}" | base64) ``` Include it in the `Authorization` header: ```bash theme={null} curl -H "Authorization: Bearer ${TOKEN}" \ https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms ``` ## Response Format All API responses use a standard envelope: ```json theme={null} { "type": "response", "success": true, "description": "Request completed successfully", "data": { /* payload */ }, "error": null, "requestId": "req_abc123" } ``` | Field | Type | Description | | ------------- | ---------------- | -------------------------------------- | | `type` | `string` | Always `"response"` | | `success` | `boolean` | Whether the request succeeded | | `description` | `string` | Human-readable description | | `data` | `object` | Response payload (empty `{}` on error) | | `error` | `object \| null` | Error details, or `null` on success | | `requestId` | `string` | Optional request identifier | ### Error Format When `success` is `false`, the `error` field contains: ```json theme={null} { "error": { "module": "Rooms", "code": "NOT_FOUND", "message": "Room not found" } } ``` | Field | Type | Description | | --------- | -------- | --------------------------------------- | | `module` | `string` | Which server module generated the error | | `code` | `string` | Machine-readable error code | | `message` | `string` | Human-readable error message | ## Error Codes | Code | HTTP Status | Description | | ------------------ | ----------- | ---------------------------------- | | `UNAUTHORIZED` | 401 | Missing or invalid authentication | | `FORBIDDEN` | 403 | Insufficient permissions | | `NOT_FOUND` | 404 | Resource not found | | `BAD_REQUEST` | 400 | Invalid request body or parameters | | `CONFLICT` | 409 | Resource already exists | | `INTERNAL_ERROR` | 500 | Server-side error | | `VALIDATION_ERROR` | 400 | Request body failed validation | | `RATE_LIMITED` | 429 | Too many requests | ## Pagination List endpoints support pagination with `limit` and `offset` query parameters: | Parameter | Type | Default | Max | Description | | --------- | -------- | ------- | --- | -------------------------- | | `limit` | `number` | 50 | 100 | Number of results per page | | `offset` | `number` | 0 | -- | Number of results to skip | Paginated responses include total count: ```json theme={null} { "data": { "rooms": [ /* ... */ ], "total": 150, "limit": 50, "offset": 0 } } ``` ## Search & Filtering Many list endpoints support a `search` query parameter for case-insensitive partial matching, and `from`/`to` date range filters: | Parameter | Type | Description | | --------- | -------- | --------------------------------------------------------------------- | | `search` | `string` | Case-insensitive partial match on the primary field (name, URL, etc.) | | `from` | `string` | ISO date string. Filters `created_at >= from` | | `to` | `string` | ISO date string. Filters `created_at <= to` | # Create Room Source: https://docs.collab-kit.com/api-reference/rooms/create-room Create a new collaboration room. Create a new room in your organization. Rooms are the core collaboration context -- all users, presence, stores, comments, and files are scoped to a room. ## Endpoint ``` POST /v1/accounts/:accountId/rooms ``` ## Authentication **Bearer token** required. ## Request Body A name for the room. Can be any string (e.g., `"whiteboard/room-one"`, `"project-alpha"`). Optional custom identifier for external system correlation. Must be unique within your organization. ## Response The created room. See [`CollabKitRoom`](/types/core#collabkitroom). ## Example ```bash curl theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"name": "whiteboard/room-one", "customId": "my-room-1"}' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/rooms`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'whiteboard/room-one', customId: 'my-room-1' }), }); const { data } = await res.json(); ``` ```json Response (201) theme={null} { "type": "response", "success": true, "description": "Room created", "data": { "room": { "id": "c3003c93-60cf-4184-b85f-20be14d26dac", "account_id": "a1b2c3d4-5678-...", "name": "whiteboard/room-one", "custom_id": "my-room-1", "created_at": "2026-05-29T10:00:00.000Z", "state": "active", "duration_seconds": 0, "active_participants": 0, "total_users_created": 0 } }, "error": null } ``` # Get Room Source: https://docs.collab-kit.com/api-reference/rooms/get-room Get room details including its users. Retrieve details for a specific room, including a paginated list of its users. ## Endpoint ``` GET /v1/accounts/:accountId/rooms/:roomId ``` ## Authentication **Bearer token** required. ## Path Parameters The room's unique ID. ## Query Parameters These parameters control the users sub-list, not the room itself: Number of users to return. Maximum `100`. Number of users to skip. Case-insensitive partial match on user name or ID. ISO date string. Only return users created at or after this date. ISO date string. Only return users created at or before this date. ## Response The room object with merged analytics. See [`CollabKitRoom`](/types/core#collabkitroom). Paginated list of users in this room. See [`CollabKitUser`](/types/core#collabkituser). Total number of users in the room. The limit used for the users list. The offset used for the users list. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms/c3003c93-60cf-4184-b85f-20be14d26dac?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const roomId = 'c3003c93-60cf-4184-b85f-20be14d26dac'; const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/rooms/${roomId}?limit=10`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Room retrieved", "data": { "room": { "id": "c3003c93-60cf-4184-...", "account_id": "a1b2c3d4-...", "name": "whiteboard/room-one", "custom_id": "my-room-1", "created_at": "2026-05-29T10:00:00.000Z", "state": "active", "duration_seconds": 3600, "active_participants": 2, "total_users_created": 5 }, "users": [ { "id": "f47ac10b-58cc-4372-...", "room_id": "c3003c93-...", "name": "Alice", "profile_picture": "https://example.com/alice.png", "custom_id": "alice-001", "status": "online", "created_at": "2026-05-29T10:05:00.000Z", "joined_at": "2026-05-29T10:06:00.000Z" } ], "usersTotal": 5, "limit": 10, "offset": 0 }, "error": null } ``` # Get Room by Custom ID Source: https://docs.collab-kit.com/api-reference/rooms/get-room-by-custom-id Look up a room using its custom identifier. Retrieve room details by the optional `custom_id` set during room creation, instead of the internal UUID. ## Endpoint ``` GET /v1/accounts/:accountId/rooms/custom/:customId ``` ## Authentication **Bearer token** required. ## Path Parameters The custom identifier assigned when the room was created. ## Query Parameters Supports the same user pagination and filtering parameters as [Get Room](/api-reference/rooms/get-room): Number of users to return. Maximum `100`. Number of users to skip. Case-insensitive partial match on user name or ID. ISO date string. Only return users created at or after this date. ISO date string. Only return users created at or before this date. ## Response Returns the same response format as [Get Room](/api-reference/rooms/get-room). ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms/custom/my-room-1" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/rooms/custom/my-room-1`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); console.log(data.room, data.users); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Room retrieved", "data": { "room": { "id": "c3003c93-60cf-4184-...", "account_id": "a1b2c3d4-...", "name": "whiteboard/room-one", "custom_id": "my-room-1", "created_at": "2026-05-29T10:00:00.000Z", "state": "active", "duration_seconds": 3600, "active_participants": 2, "total_users_created": 5 }, "users": [ ... ], "usersTotal": 5, "limit": 50, "offset": 0 }, "error": null } ``` If no room matches the given `customId` within your organization, the API returns a `404 NOT_FOUND` error. # List Rooms Source: https://docs.collab-kit.com/api-reference/rooms/list-rooms List all rooms in your organization with pagination and search. Retrieve a paginated list of rooms in your organization. ## Endpoint ``` GET /v1/accounts/:accountId/rooms ``` ## Authentication **Bearer token** required. ## Query Parameters Number of rooms to return. Maximum `100`. Number of rooms to skip for pagination. Case-insensitive partial match on room name. ISO date string. Only return rooms created at or after this date. ISO date string. Only return rooms created at or before this date. ## Response Array of room objects. Total number of rooms matching the query (for pagination). The limit used in this request. The offset used in this request. ## Example ```bash curl theme={null} # List first 10 rooms curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms?limit=10" \ -H "Authorization: Bearer ${TOKEN}" # Search by name curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms?search=whiteboard" \ -H "Authorization: Bearer ${TOKEN}" # Filter by date range curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms?from=2026-01-01&to=2026-06-01" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/rooms?limit=10`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); console.log(data.rooms, data.total); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Rooms retrieved", "data": { "rooms": [ { "id": "c3003c93-60cf-4184-...", "account_id": "a1b2c3d4-...", "name": "whiteboard/room-one", "custom_id": "my-room-1", "created_at": "2026-05-29T10:00:00.000Z", "state": "active", "duration_seconds": 3600, "active_participants": 2, "total_users_created": 5 } ], "total": 1, "limit": 10, "offset": 0 }, "error": null } ``` # Reconcile Room Source: https://docs.collab-kit.com/api-reference/rooms/reconcile-room Reconcile the active participants counter with the server's ground truth. Fixes a stale `active_participants` count by querying the server for the actual number of online users and updating the stored counter to match. This is useful when the dashboard analytics show an incorrect active-user count, typically caused by abrupt client disconnections (e.g. a killed process or network failure) that prevented the normal decrement path from completing. ## Endpoint ``` POST /v1/accounts/:accountId/rooms/:roomId/reconcile ``` ## Authentication **Bearer token** required. ## Path Parameters The room's unique ID. ## Response The room object with the corrected `active_participants` value. See [`CollabKitRoom`](/types/core#collabkitroom). The `active_participants` value that was stored before reconciliation. The corrected `active_participants` value, reflecting the actual number of users currently connected to the room. ## Example ```bash curl theme={null} curl -X POST "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms/c3003c93-60cf-4184-b85f-20be14d26dac/reconcile" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const roomId = 'c3003c93-60cf-4184-b85f-20be14d26dac'; const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/rooms/${roomId}/reconcile`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, }, ); const { data } = await res.json(); console.log(`Fixed: ${data.previous_active_participants} -> ${data.reconciled_active_participants}`); ``` ```json Response (200) theme={null} { "type": "response", "success": true, "description": "OK", "data": { "room": { "id": "c3003c93-60cf-4184-...", "account_id": "a1b2c3d4-...", "name": "whiteboard/room-one", "custom_id": "my-room-1", "created_at": "2026-05-29T10:00:00.000Z", "state": "active", "duration_seconds": 3600, "active_participants": 0, "total_users_created": 500 }, "previous_active_participants": 500, "reconciled_active_participants": 0 }, "error": null } ``` This endpoint is safe to call at any time. If the counter is already accurate, the response will show the same value for both `previous_active_participants` and `reconciled_active_participants`. # Delete File Source: https://docs.collab-kit.com/api-reference/storage/delete-file Delete a file from storage. Delete a file from the room's R2 object storage by its key. ## Endpoint ``` DELETE /v1/accounts/:accountId/upload ``` ## Authentication **Bearer token** required. ## Request Body The storage key of the file to delete (returned from the [upload](/api-reference/storage/upload-file) endpoint). ## Response The key of the deleted file. ## Example ```bash curl theme={null} curl -X DELETE https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/upload \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"key": "c3003c93/uploads/screenshot.png"}' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/upload`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ key: 'c3003c93/uploads/screenshot.png' }), }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "File deleted", "data": { "key": "c3003c93/uploads/screenshot.png" }, "error": null } ``` # Get File Source: https://docs.collab-kit.com/api-reference/storage/get-file Serve a stored file by its key. Retrieve the raw contents of a stored file. This serves the file directly from R2 storage with appropriate HTTP headers. ## Endpoint ``` GET /v1/accounts/:accountId/storage/* ``` The file's storage key follows `/v1/accounts/:accountId/storage/` in the URL path. ## Authentication **No authentication required.** Files are served publicly by their storage key. ## Response Returns the raw file body with the following headers: | Header | Description | | ---------------- | ---------------------- | | `Content-Type` | The file's MIME type | | `ETag` | Entity tag for caching | | `Content-Length` | File size in bytes | Returns a `404` response (using the standard error envelope) if the file is not found. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/storage/c3003c93/uploads/screenshot.png" \ --output screenshot.png ``` ```html HTML theme={null} ``` ```typescript JavaScript theme={null} const url = `https://api.collab-kit.com/v1/accounts/${accountId}/storage/c3003c93/uploads/screenshot.png`; const res = await fetch(url); const blob = await res.blob(); ``` # List Files Source: https://docs.collab-kit.com/api-reference/storage/list-files List files in a room with optional filters. Retrieve a list of files stored in a room, with optional filtering by MIME type and user. ## Endpoint ``` GET /v1/accounts/:accountId/files ``` ## Authentication **Bearer token** required. ## Query Parameters The room to list files from. Filter by MIME type. Use a trailing slash for categories (e.g., `image/` matches all image types). Can be specified multiple times for OR matching. Filter by the user who uploaded the files. ## Response Array of file objects. See [`StorageFile`](/types/storage#storagefile). ## Example ```bash curl theme={null} # List all files in a room curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/files?roomId=${ROOM_ID}" \ -H "Authorization: Bearer ${TOKEN}" # Filter by MIME type curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/files?roomId=${ROOM_ID}&mimeType=image/" \ -H "Authorization: Bearer ${TOKEN}" # Filter by user curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/files?roomId=${ROOM_ID}&userId=user-001" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/files?roomId=${roomId}&mimeType=image/`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); console.log(data.files); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Files retrieved", "data": { "files": [ { "key": "c3003c93/uploads/screenshot.png", "url": "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/storage/c3003c93/uploads/screenshot.png", "filename": "screenshot.png", "mimeType": "image/png", "size": 245760, "uploadedAt": "2026-05-29T10:15:00.000Z", "uploadedBy": "user-001" } ] }, "error": null } ``` # Upload File Source: https://docs.collab-kit.com/api-reference/storage/upload-file Upload a file to a room's storage. Upload a file to the room's R2 object storage. ## Endpoint ``` POST /v1/accounts/:accountId/upload ``` ## Authentication **Bearer token** required. ## Request Body `multipart/form-data` with the following fields: The room to associate the file with. The user uploading the file. The file to upload. ## Response The uploaded file's key and URL. See [`UploadResult`](/types/storage#uploadresult). ## Example ```bash curl theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/upload \ -H "Authorization: Bearer ${TOKEN}" \ -F "roomId=c3003c93-60cf-4184-b85f-20be14d26dac" \ -F "userId=user-001" \ -F "file=@./screenshot.png" ``` ```typescript JavaScript theme={null} const formData = new FormData(); formData.append('roomId', roomId); formData.append('userId', userId); formData.append('file', fileInput.files[0]); const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/upload`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }, body: formData, }); const { data } = await res.json(); console.log(data.file.url); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "File uploaded", "data": { "file": { "key": "c3003c93/uploads/screenshot.png", "url": "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/storage/c3003c93/uploads/screenshot.png" } }, "error": null } ``` # Create User Source: https://docs.collab-kit.com/api-reference/users/create-user Create a user in a room and get a JWT token for WebSocket auth. Create a new user in a room. Returns the user details and a JWT token that the client SDK uses for WebSocket authentication. ## Endpoint ``` POST /v1/accounts/:accountId/users ``` ## Authentication **Bearer token** required. ## Request Body Display name for the user. The room to add the user to. URL of the user's avatar image. Optional custom identifier for external system correlation. Must be unique within the room. ## Response The created user. See [`CollabKitUser`](/types/core#collabkituser). JWT token for WebSocket authentication. Valid for 90 days. Pass this to the `CollabKitClient` constructor. ## Example ```bash curl theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "Alice", "roomId": "c3003c93-60cf-4184-b85f-20be14d26dac", "profilePicture": "https://example.com/alice.png", "customId": "alice-001" }' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/users`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Alice', roomId: 'c3003c93-60cf-4184-b85f-20be14d26dac', profilePicture: 'https://example.com/alice.png', customId: 'alice-001', }), }); const { data } = await res.json(); // data.token is the JWT for the client SDK ``` ```json Response (201) theme={null} { "type": "response", "success": true, "description": "User created", "data": { "user": { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "room_id": "c3003c93-60cf-4184-...", "name": "Alice", "profile_picture": "https://example.com/alice.png", "custom_id": "alice-001", "status": "offline", "created_at": "2026-05-29T10:05:00.000Z" }, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "error": null } ``` The user starts with `"offline"` status. They become `"online"` when the client SDK calls `join()`. ## Using the JWT Token Pass the returned `token` to the client SDK: ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: data.token, // JWT from POST /v1/accounts/:accountId/users }); await client.join(); // User is now online as an editor by default // await client.join({ role: 'viewer' }); ``` The JWT contains: * `accountId` -- Your organization ID * `userId` -- The user's ID * `roomId` -- The room ID * `exp` -- Expiration (90 days from creation) # Get User Source: https://docs.collab-kit.com/api-reference/users/get-user Get user details and session history. Retrieve a user's details and their session history (join/leave timestamps). ## Endpoint ``` GET /v1/accounts/:accountId/users/:userId ``` ## Authentication **Bearer token** required. ## Path Parameters The user's unique ID. ## Query Parameters The room ID the user belongs to. Required to route the request to the correct Durable Object. ## Response The user object. See [`CollabKitUser`](/types/core#collabkituser). The user's session history. See [`CollabKitUserSession`](/types/core#collabkitusersession). ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users/user-001?roomId=c3003c93-60cf-4184-b85f-20be14d26dac" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/users/user-001?roomId=${roomId}`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "User retrieved", "data": { "user": { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "room_id": "c3003c93-...", "name": "Alice", "profile_picture": "https://example.com/alice.png", "custom_id": "alice-001", "status": "online", "created_at": "2026-05-29T10:05:00.000Z", "joined_at": "2026-05-29T10:06:00.000Z", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "sessions": [ { "id": "sess_001", "user_id": "user-001", "room_id": "c3003c93-...", "joined_at": "2026-05-29T10:06:00.000Z", "left_at": null }, { "id": "sess_000", "user_id": "user-001", "room_id": "c3003c93-...", "joined_at": "2026-05-28T14:00:00.000Z", "left_at": "2026-05-28T15:30:00.000Z" } ] }, "error": null } ``` # Get User by Custom ID Source: https://docs.collab-kit.com/api-reference/users/get-user-by-custom-id Look up a user using their custom identifier. Retrieve user details by the optional `custom_id` set during user creation, instead of the internal UUID. ## Endpoint ``` GET /v1/accounts/:accountId/users/custom/:customId ``` ## Authentication **Bearer token** required. ## Path Parameters The custom identifier assigned when the user was created. ## Query Parameters The room ID the user belongs to. Required to route the request to the correct Durable Object. ## Response Returns the same response format as [Get User](/api-reference/users/get-user). The user object. See [`CollabKitUser`](/types/core#collabkituser). The user's session history. See [`CollabKitUserSession`](/types/core#collabkitusersession). ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users/custom/alice-001?roomId=c3003c93-60cf-4184-b85f-20be14d26dac" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/users/custom/alice-001?roomId=${roomId}`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); console.log(data.user, data.sessions); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "User retrieved", "data": { "user": { "id": "f47ac10b-58cc-4372-...", "room_id": "c3003c93-...", "name": "Alice", "profile_picture": "https://example.com/alice.png", "custom_id": "alice-001", "status": "online", "created_at": "2026-05-29T10:05:00.000Z", "joined_at": "2026-05-29T10:06:00.000Z", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "sessions": [ { "id": "sess_001", "user_id": "f47ac10b-58cc-4372-...", "room_id": "c3003c93-...", "joined_at": "2026-05-29T10:06:00.000Z", "left_at": null } ] }, "error": null } ``` If no user matches the given `customId` within the specified room, the API returns a `404 NOT_FOUND` error. # List Users Source: https://docs.collab-kit.com/api-reference/users/list-users List all users in a room with pagination and search. Retrieve a paginated list of users in a specific room. ## Endpoint ``` GET /v1/accounts/:accountId/users ``` ## Authentication **Bearer token** required. This endpoint accepts either an account API token or a user JWT. The client SDK uses this endpoint internally for `client.users.list(...)`. ## Query Parameters The room ID to list users for. Number of users to return. Maximum `100`. Number of users to skip for pagination. Case-insensitive partial match on user name or ID. ## Response Array of user objects. See [`CollabKitUser`](/types/core#collabkituser). Total number of users matching the query. The limit used in this request. The offset used in this request. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users?roomId=c3003c93-60cf-4184-b85f-20be14d26dac&limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/users?roomId=${roomId}&limit=10`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); console.log(data.users, data.total); // SDK equivalent. This auto-populates client.users.all and excludes self. const page = await client.users.list({ pageSize: 100, offset: 0 }); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Users retrieved", "data": { "users": [ { "id": "f47ac10b-58cc-4372-...", "room_id": "c3003c93-...", "name": "Alice", "profile_picture": "https://example.com/alice.png", "custom_id": "alice-001", "status": "online", "created_at": "2026-05-29T10:05:00.000Z", "joined_at": "2026-05-29T10:06:00.000Z", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ], "total": 5, "limit": 10, "offset": 0 }, "error": null } ``` # Create Webhook Source: https://docs.collab-kit.com/api-reference/webhooks/create-webhook Register a webhook to receive event notifications. Register a new webhook endpoint that receives HTTP callbacks when events occur in your organization. ## Endpoint ``` POST /v1/accounts/:accountId/webhooks ``` ## Authentication **Bearer token** required. ## Request Body The URL to send webhook payloads to. Must be a valid, reachable URL. List of events to subscribe to. At least one event is required. Scope the webhook to a specific room. If omitted, the webhook fires for events in all rooms. ### Available Events All payloads are wrapped in a base envelope: `{ id, event, timestamp, ...payload }`. | Event | Description | Payload | | -------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | | `participant.joined` | A user came online in a room | `{ room: `[`CollabKitRoom`](/types/core#collabkitroom)`, user: `[`CollabKitUser`](/types/core#collabkituser)` }` | | `participant.left` | A user went offline | `{ room: `[`CollabKitRoom`](/types/core#collabkitroom)`, user: `[`CollabKitUser`](/types/core#collabkituser)` }` | | `session.started` | A new session started (first user joined) | `{ room: `[`CollabKitRoom`](/types/core#collabkitroom)` }` | | `session.closed` | A session ended (last user left) | `{ room: `[`CollabKitRoom`](/types/core#collabkitroom)`, users: `[`CollabKitUser`](/types/core#collabkituser)`[] }` | | `user.created` | A new user was added to a room | `{ room: { id }, user: `[`CollabKitUser`](/types/core#collabkituser)` }` | | `user.updated` | A user's fields changed | `{ room: { id }, user: `[`CollabKitUser`](/types/core#collabkituser)` }` | | `user.deleted` | A user was removed | `{ room: { id }, user: `[`CollabKitUser`](/types/core#collabkituser)` }` | ## Response The created webhook, **including the signing secret** (only returned at creation time). See [`WebhookRegistration`](/types/webhooks#webhookregistration). The `secret` is only returned when the webhook is created. Store it securely -- you cannot retrieve it later. ## Example ```bash curl theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.example.com/collab-webhook", "events": ["participant.joined", "participant.left"], "roomId": "c3003c93-60cf-4184-b85f-20be14d26dac" }' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/webhooks`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ url: 'https://api.example.com/collab-webhook', events: ['participant.joined', 'participant.left'], }), }); const { data } = await res.json(); // SAVE data.webhook.secret securely! ``` ```json Response (201) theme={null} { "type": "response", "success": true, "description": "Webhook created", "data": { "webhook": { "id": "wh_abc123", "organization_id": "a1b2c3d4-...", "url": "https://api.example.com/collab-webhook", "secret": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1", "events": ["participant.joined", "participant.left"], "room_id": "c3003c93-60cf-4184-...", "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } }, "error": null } ``` ## Verifying Webhook Signatures Each webhook delivery includes an HMAC-SHA256 signature in the headers. Verify it using the `secret`: ```typescript theme={null} import { createHmac } from 'crypto'; function verifyWebhook(body: string, signature: string, secret: string): boolean { const expected = createHmac('sha256', secret).update(body).digest('hex'); return expected === signature; } ``` # Delete Webhook Source: https://docs.collab-kit.com/api-reference/webhooks/delete-webhook Delete a webhook registration. Permanently delete a webhook. All pending deliveries for this webhook will be cancelled. ## Endpoint ``` DELETE /v1/accounts/:accountId/webhooks/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The webhook's unique ID. ## Response Returns an empty data object on success. ## Example ```bash curl theme={null} curl -X DELETE https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks/wh_abc123 \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/webhooks/wh_abc123`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` }, }); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Webhook deleted", "data": {}, "error": null } ``` # Get Webhook Source: https://docs.collab-kit.com/api-reference/webhooks/get-webhook Get details for a specific webhook. Retrieve details for a specific webhook by ID. ## Endpoint ``` GET /v1/accounts/:accountId/webhooks/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The webhook's unique ID. ## Response The webhook object (without the `secret` field). See [`WebhookRegistrationPublic`](/types/webhooks#webhookregistrationpublic). ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks/wh_abc123" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/webhooks/wh_abc123`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Webhook retrieved", "data": { "webhook": { "id": "wh_abc123", "organization_id": "a1b2c3d4-...", "url": "https://api.example.com/collab-webhook", "events": ["participant.joined", "participant.left"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } }, "error": null } ``` # List Deliveries Source: https://docs.collab-kit.com/api-reference/webhooks/list-deliveries View the delivery log for a webhook. Retrieve the delivery history for a specific webhook, including payload, status, and retry information. ## Endpoint ``` GET /v1/accounts/:accountId/webhooks/:id/deliveries ``` ## Authentication **Bearer token** required. ## Path Parameters The webhook's unique ID. ## Query Parameters Number of deliveries to return. Maximum `100`. Number of deliveries to skip. ## Response Array of delivery log entries. See [`WebhookDelivery`](/types/webhooks#webhookdelivery). Total number of deliveries. The limit used. The offset used. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks/wh_abc123/deliveries?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/webhooks/wh_abc123/deliveries?limit=10`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Deliveries retrieved", "data": { "deliveries": [ { "id": "del_001", "webhook_id": "wh_abc123", "event": "participant.joined", "payload": "{\"event\":\"participant.joined\",\"data\":{\"userId\":\"user-001\",\"roomId\":\"c3003c93-...\"}}", "status": "success", "attempts": 1, "last_attempt_at": "2026-05-29T10:06:00.000Z", "next_retry_at": null, "status_code": 200, "created_at": "2026-05-29T10:06:00.000Z" } ], "total": 1, "limit": 10, "offset": 0 }, "error": null } ``` # List Webhooks Source: https://docs.collab-kit.com/api-reference/webhooks/list-webhooks List all webhooks in your organization. Retrieve a paginated list of webhooks registered for your organization. ## Endpoint ``` GET /v1/accounts/:accountId/webhooks ``` ## Authentication **Bearer token** required. ## Query Parameters Number of webhooks to return. Maximum `100`. Number of webhooks to skip. Case-insensitive partial match on the webhook URL. ## Response Array of webhook objects. The `secret` field is **not** included. See [`WebhookRegistrationPublic`](/types/webhooks#webhookregistrationpublic). Total number of webhooks. The limit used. The offset used. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/webhooks?limit=10`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Webhooks retrieved", "data": { "webhooks": [ { "id": "wh_abc123", "organization_id": "a1b2c3d4-...", "url": "https://api.example.com/collab-webhook", "events": ["participant.joined", "participant.left"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } ], "total": 1, "limit": 10, "offset": 0 }, "error": null } ``` The `secret` field is never included in list or get responses. It is only returned when [creating a webhook](/api-reference/webhooks/create-webhook). # Update Webhook Source: https://docs.collab-kit.com/api-reference/webhooks/update-webhook Update an existing webhook's configuration. Update a webhook's URL, events, room scope, or enabled state. At least one field must be provided. ## Endpoint ``` PATCH /v1/accounts/:accountId/webhooks/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The webhook's unique ID. ## Request Body All fields are optional, but at least one must be provided. New delivery URL. New list of subscribed events. New room scope. Set to `null` to subscribe to events from all rooms. Enable or disable the webhook. ## Response The updated webhook object (without the `secret` field). See [`WebhookRegistrationPublic`](/types/webhooks#webhookregistrationpublic). ## Example ```bash curl theme={null} curl -X PATCH https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks/wh_abc123 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "events": ["participant.joined", "participant.left", "user.created"], "enabled": true }' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/webhooks/wh_abc123`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ events: ['participant.joined', 'participant.left', 'user.created'], enabled: true, }), }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Webhook updated", "data": { "webhook": { "id": "wh_abc123", "organization_id": "a1b2c3d4-...", "url": "https://api.example.com/collab-webhook", "events": ["participant.joined", "participant.left", "user.created"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T11:00:00.000Z" } }, "error": null } ``` # Create Workflow Source: https://docs.collab-kit.com/api-reference/workflows/create-workflow Create a workflow that runs custom code on events. Create a new workflow that executes custom JavaScript/TypeScript code in response to events. Workflows run in Cloudflare Workers with access to room data, stores, and file storage. ## Endpoint ``` POST /v1/accounts/:accountId/workflows ``` ## Authentication **Bearer token** required. ## Request Body A descriptive name for the workflow. JavaScript or TypeScript source code to execute when triggered. List of events that trigger this workflow. At least one event is required. Same event types as [webhooks](/api-reference/webhooks/create-webhook#available-events). Scope the workflow to a specific room. If omitted, the workflow triggers for events in all rooms. ### Available Events | Event | Description | | -------------------- | ---------------------------- | | `participant.joined` | A user came online in a room | | `participant.left` | A user went offline | | `session.started` | A new session started | | `session.closed` | A session ended | | `user.created` | A new user was added | | `user.updated` | A user's fields changed | | `user.deleted` | A user was removed | ### Worker Bindings Your workflow code has access to these bindings: | Binding | Type | Description | | ------- | -------------- | ------------------------------------- | | `store` | `StoreBinding` | Read-only access to KV stores | | `room` | `RoomBinding` | Read-only room and user metadata | | `files` | `FilesBinding` | Scoped R2 file operations | | `event` | `EventBinding` | The event that triggered the workflow | ## Response The created workflow. See [`WorkflowRegistration`](/types/workflows#workflowregistration). ## Example ```bash curl theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "Welcome message", "code": "export default async function(event, { store, room }) {\n console.log(`User ${event.data.userId} joined room ${event.data.roomId}`);\n return { success: true };\n}", "events": ["participant.joined"] }' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/workflows`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Welcome message', code: `export default async function(event, { store, room }) { console.log(\`User \${event.data.userId} joined\`); return { success: true }; }`, events: ['participant.joined'], }), }); const { data } = await res.json(); ``` ```json Response (201) theme={null} { "type": "response", "success": true, "description": "Workflow created", "data": { "workflow": { "id": "wf_abc123", "organization_id": "a1b2c3d4-...", "name": "Welcome message", "code": "export default async function(event, { store, room }) { ... }", "events": ["participant.joined"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } }, "error": null } ``` # Delete Workflow Source: https://docs.collab-kit.com/api-reference/workflows/delete-workflow Delete a workflow. Permanently delete a workflow. Any in-progress executions will complete, but no new executions will be triggered. ## Endpoint ``` DELETE /v1/accounts/:accountId/workflows/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The workflow's unique ID. ## Response Returns an empty data object on success. ## Example ```bash curl theme={null} curl -X DELETE https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123 \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/workflows/wf_abc123`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` }, }); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Workflow deleted", "data": {}, "error": null } ``` # Get Workflow Source: https://docs.collab-kit.com/api-reference/workflows/get-workflow Get details for a specific workflow. Retrieve details for a specific workflow by ID. ## Endpoint ``` GET /v1/accounts/:accountId/workflows/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The workflow's unique ID. ## Response The workflow object. See [`WorkflowRegistration`](/types/workflows#workflowregistration). ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/workflows/wf_abc123`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Workflow retrieved", "data": { "workflow": { "id": "wf_abc123", "organization_id": "a1b2c3d4-...", "name": "Welcome message", "code": "export default async function(event, { store, room }) {\n console.log(`User ${event.data.userId} joined`);\n return { success: true };\n}", "events": ["participant.joined"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } }, "error": null } ``` # List Executions Source: https://docs.collab-kit.com/api-reference/workflows/list-executions View the execution log for a workflow. Retrieve the execution history for a specific workflow, including status, duration, and results. ## Endpoint ``` GET /v1/accounts/:accountId/workflows/:id/executions ``` ## Authentication **Bearer token** required. ## Path Parameters The workflow's unique ID. ## Query Parameters Number of executions to return. Maximum `100`. Number of executions to skip. ## Response Array of execution log entries. See [`WorkflowExecution`](/types/workflows#workflowexecution). Total number of executions. The limit used. The offset used. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123/executions?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch( `https://api.collab-kit.com/v1/accounts/${accountId}/workflows/wf_abc123/executions?limit=10`, { headers: { 'Authorization': `Bearer ${token}` } } ); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Executions retrieved", "data": { "executions": [ { "id": "exec_001", "workflow_id": "wf_abc123", "event": "participant.joined", "status": "success", "result": "{\"success\":true}", "duration_ms": 42, "created_at": "2026-05-29T10:06:00.000Z" }, { "id": "exec_002", "workflow_id": "wf_abc123", "event": "participant.joined", "status": "failed", "result": "TypeError: Cannot read property 'name' of undefined", "duration_ms": 15, "created_at": "2026-05-29T10:08:00.000Z" } ], "total": 2, "limit": 10, "offset": 0 }, "error": null } ``` # List Workflows Source: https://docs.collab-kit.com/api-reference/workflows/list-workflows List all workflows in your organization. Retrieve a paginated list of workflows registered for your organization. ## Endpoint ``` GET /v1/accounts/:accountId/workflows ``` ## Authentication **Bearer token** required. ## Query Parameters Number of workflows to return. Maximum `100`. Number of workflows to skip. Case-insensitive partial match on workflow name. ## Response Array of workflow objects. Total number of workflows. The limit used. The offset used. ## Example ```bash curl theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/workflows?limit=10`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Workflows retrieved", "data": { "workflows": [ { "id": "wf_abc123", "organization_id": "a1b2c3d4-...", "name": "Welcome message", "code": "export default async function(event, { store, room }) { ... }", "events": ["participant.joined"], "room_id": null, "enabled": true, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T10:00:00.000Z" } ], "total": 1, "limit": 10, "offset": 0 }, "error": null } ``` # Update Workflow Source: https://docs.collab-kit.com/api-reference/workflows/update-workflow Update an existing workflow's configuration or code. Update a workflow's name, code, events, room scope, or enabled state. At least one field must be provided. ## Endpoint ``` PATCH /v1/accounts/:accountId/workflows/:id ``` ## Authentication **Bearer token** required. ## Path Parameters The workflow's unique ID. ## Request Body All fields are optional, but at least one must be provided. New workflow name. New source code. New list of trigger events. New room scope. Set to `null` to trigger for all rooms. Enable or disable the workflow. ## Response The updated workflow object. See [`WorkflowRegistration`](/types/workflows#workflowregistration). ## Example ```bash curl theme={null} curl -X PATCH https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "Updated welcome message", "enabled": false }' ``` ```typescript JavaScript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/workflows/wf_abc123`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Updated welcome message', enabled: false, }), }); const { data } = await res.json(); ``` ```json Response theme={null} { "type": "response", "success": true, "description": "Workflow updated", "data": { "workflow": { "id": "wf_abc123", "organization_id": "a1b2c3d4-...", "name": "Updated welcome message", "code": "export default async function(event, { store, room }) { ... }", "events": ["participant.joined"], "room_id": null, "enabled": false, "created_at": "2026-05-29T10:00:00.000Z", "updated_at": "2026-05-29T11:00:00.000Z" } }, "error": null } ``` # Authentication Source: https://docs.collab-kit.com/authentication Understand how authentication works across the CollabKit platform. Sign up at the [CollabKit dashboard](https://dash.collab-kit.com) to create your organization. Once logged in, go to **Settings** to find your **Account ID** and **API Key**. CollabKit uses two authentication mechanisms: | Mechanism | Used For | Format | | ---------------- | --------------------------------------------------------------- | -------------------------------------------------- | | **Bearer Token** | All REST API calls (rooms, users, storage, webhooks, workflows) | `Authorization: Bearer ` | | **JWT Token** | Client SDK WebSocket connections | Passed to the Client SDK manullay | ## Bearer Authentication All REST API endpoints require Bearer authentication. ### Constructing the Token The Bearer token is a Base64-encoded string combining your `accountId` and `apiKey`: ```bash theme={null} TOKEN=$(echo -n "${ACCOUNT_ID}:${API_KEY}" | base64) ``` ```typescript theme={null} const token = btoa(`${accountId}:${apiKey}`); ``` ### Using the Token Include it in the `Authorization` header: ```bash theme={null} curl -X GET https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms \ -H "Authorization: Bearer ${TOKEN}" ``` ```typescript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/rooms`, { headers: { 'Authorization': `Bearer ${token}` }, }); const { data } = await res.json(); ``` ## JWT Authentication (Client SDK) The client SDK authenticates over WebSocket using a JWT token. You get this token when you [create a user](/api-reference/users/create-user) via the REST API. ### Getting a JWT ```bash theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "Alice", "roomId": "room-id-here" }' ``` ```typescript theme={null} const res = await fetch(`https://api.collab-kit.com/v1/accounts/${accountId}/users`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Alice', roomId: 'room-id-here', }), }); const { data } = await res.json(); ``` The response includes a `token` field containing the JWT: ```json theme={null} { "success": true, "data": { "user": { "id": "f47ac10b-58cc-4372-...", "name": "Alice", "status": "offline" }, "token": "eyJhbGciOiJIUzI1NiIs..." } } ``` ### Using the JWT Pass the JWT when constructing the client: ```typescript theme={null} const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: 'eyJhbGciOiJIUzI1NiIs...', }); await client.join(); // defaults to { role: 'editor' } // await client.join({ role: 'viewer' }); ``` The SDK sends the JWT with the `JOIN_ROOM` message, which performs both authentication and room join in a single step. If the token is invalid or expired, the `authFailed` socket event fires: ```typescript theme={null} client.socket.on('authFailed', () => { console.error('Authentication failed - token may be expired'); // Redirect to login or refresh the token }); ``` ### Token Lifecycle * Tokens are valid for **90 days** from creation * Each call to `POST /users` generates a new token for that user * Each call to `POST /users` creates a new user with a server-generated UUID * There is no refresh endpoint -- create a new user entry to get a new token ## Error Responses Authentication failures return a standard error response: ```json theme={null} { "type": "response", "success": false, "description": "Unauthorized", "data": {}, "error": { "module": "Auth", "code": "UNAUTHORIZED", "message": "Invalid or missing authentication credentials" } } ``` | HTTP Status | Cause | | ----------- | ------------------------------------------------------------ | | `401` | Missing, malformed, or invalid Bearer token / session cookie | | `403` | Valid credentials but insufficient permissions | # Collaborative Editor Source: https://docs.collab-kit.com/guides/collaborative-editor Build a real-time collaborative text editor with Yjs and TipTap. This guide shows you how to build a Google Docs-style collaborative text editor using CollabKit's Yjs provider with TipTap. ## What You'll Build * A rich text editor where multiple users can type simultaneously * Conflict-free collaborative editing via CRDTs * Persistent document state (survives page refreshes) * User cursors and presence indicators ## Prerequisites * A CollabKit server running with a room and users created * `@collab-kit/client` installed ## Step 1: Install Dependencies ```bash theme={null} npm install @collab-kit/client @collab-kit/utils yjs \ @tiptap/core @tiptap/starter-kit @tiptap/extension-collaboration ``` ## Step 2: Set Up CollabKit ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); ``` Yjs writes require an editor session. A viewer can load and receive document updates, but outbound Yjs changes are blocked. ## Step 3: Create the Yjs Document and Provider ```typescript theme={null} import { CollabKitYjsProvider } from '@collab-kit/client/yjs'; import * as Y from 'yjs'; const ydoc = new Y.Doc(); const provider = new CollabKitYjsProvider(client, ydoc, { documentId: 'main-editor', // Unique per document in the room }); ``` ## Step 4: Initialize TipTap ```typescript theme={null} import { Editor } from '@tiptap/core'; import StarterKit from '@tiptap/starter-kit'; import Collaboration from '@tiptap/extension-collaboration'; const editor = new Editor({ element: document.getElementById('editor'), extensions: [ StarterKit.configure({ history: false, // Disable built-in undo -- Yjs handles this }), Collaboration.configure({ document: ydoc, }), ], editable: false, // Start disabled until synced }); // Enable editing once the initial document state loads provider.on('synced', () => { editor.setEditable(true); console.log('Document ready for editing'); }); ``` Always disable TipTap's built-in `history` extension when using Collaboration. Yjs provides its own undo/redo management that understands collaborative edits. ## Step 5: Add a Loading State Show a loading indicator until the document syncs: ```typescript theme={null} const loadingEl = document.getElementById('loading'); provider.on('synced', () => { loadingEl.style.display = 'none'; editor.setEditable(true); }); ``` ## Step 6: Add User Presence (Optional) Show cursor positions of other users alongside the editor: ```typescript theme={null} // Share cursor/selection position editor.on('selectionUpdate', ({ editor }) => { const { from, to } = editor.state.selection; client.presence.update({ cursor: { from, to }, user: { name: client.currentUser?.name }, }); }); // Display other users' selections client.presence.sync('*', ({ userId, state }) => { if (userId === client.userId || !state) return; // Use state.cursor.from and state.cursor.to to highlight // their selection in the editor (implementation depends on your UI) console.log(`${state.user.name} is at position ${state.cursor.from}-${state.cursor.to}`); }); ``` ## Step 7: Clean Up ```typescript theme={null} window.addEventListener('beforeunload', () => { provider.destroy(); editor.destroy(); void client.disconnect(); }); ``` ## Multiple Documents Per Room You can have multiple collaborative documents in the same room by using different `documentId` values: ```typescript theme={null} const notesDoc = new Y.Doc(); const notesProvider = new CollabKitYjsProvider(client, notesDoc, { documentId: 'meeting-notes', }); const agendaDoc = new Y.Doc(); const agendaProvider = new CollabKitYjsProvider(client, agendaDoc, { documentId: 'meeting-agenda', }); ``` ## Next Steps * Add [cursor tracking](/guides/cursor-tracking) alongside the editor * Use [stores](/guides/real-time-stores) to save editor metadata (last editor, word count) * Add [comments](/guides/comments-system) anchored to text selections # Comments System Source: https://docs.collab-kit.com/guides/comments-system Add threaded comments with reactions and user tagging to your app. This guide shows you how to build a complete commenting system with threaded replies, emoji reactions, and user mentions -- all synced in real time. ## What You'll Build * A threaded comment feed with replies * Emoji reactions on comments and replies * User tagging with notifications * Real-time sync across all participants ## Prerequisites * A CollabKit server running with a room and users created * `@collab-kit/client` and `@collab-kit/utils` installed ## Step 1: Set Up the Client ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); ``` Comment writes require an editor session. Viewers receive comment updates, but cannot create comments, replies, reactions, or tags. ## Step 2: Load Existing Comments ```typescript theme={null} const comments = await client.comments.getAll(); comments.forEach(renderComment); ``` ## Step 3: Add a Comment ```typescript theme={null} async function addComment(text: string, taggedUserIds?: string[]) { const comment = await client.comments.add(text, { tags: taggedUserIds, }); // Set up per-comment listener comment.on('update', (updated) => { rerenderComment(updated); }); renderComment(comment); return comment; } ``` ## Step 4: Reply to a Comment Comments support one level of nesting: ```typescript theme={null} async function replyToComment(comment, text: string) { const reply = await comment.reply(text); renderReply(comment.id, reply); return reply; } ``` ## Step 5: Add Reactions ```typescript theme={null} async function addReaction(comment, emoji: string) { await comment.addReaction(emoji); } async function removeReaction(comment, emoji: string) { await comment.deleteReaction(emoji); } ``` ## Step 6: Tag Users ```typescript theme={null} async function tagUser(comment, userId: string) { await comment.addTag(userId); } // Listen for being tagged client.currentUser?.on('commentTagged', (comment) => { showNotification(`You were tagged in: "${comment.text}"`); }); ``` ## Step 7: Listen for Real-Time Updates ```typescript theme={null} // New comment from another user client.comments.on('add', (comment) => { renderComment(comment); comment.on('update', (updated) => rerenderComment(updated)); }); // Comment deleted by another user client.comments.on('delete', (commentId) => { removeCommentFromUI(commentId); }); // Comment updated (new reaction, reply, or tag) client.comments.on('update', (comment) => { rerenderComment(comment); }); ``` ## Next Steps * Anchor comments to specific UI elements or text selections * Add user @mention autocomplete using `client.users.list(...)` and `client.users.all` * Combine with [stores](/guides/real-time-stores) to attach comments to specific entities * Use [webhooks](/guides/webhooks-workflows) to send email notifications for tagged users # Live Cursor Tracking Source: https://docs.collab-kit.com/guides/cursor-tracking Build a real-time cursor tracking experience with the Presence module. This guide walks you through building live cursor tracking where each user's mouse position is displayed to all other participants in the room. ## What You'll Build * Real-time cursor positions for all connected users * User name labels on each cursor * Automatic cleanup when users disconnect * Different cursor colors per user ## Prerequisites * A CollabKit server running and a room + user created ([Quickstart](/quickstart)) * `@collab-kit/client` and `@collab-kit/utils` installed ## Step 1: Set Up the Client ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); ``` Presence writes require an editor session. Viewers can receive cursor updates and follow users, but cannot call `presence.update(...)`. ## Step 2: Broadcast Cursor Position Use the [Presence module](/sdk/presence) to share cursor coordinates. Updates are automatically throttled to 50ms: ```typescript theme={null} document.addEventListener('mousemove', (e) => { client.presence.update({ cursor: { x: e.clientX, y: e.clientY, }, }); }); ``` ## Step 3: Render Remote Cursors Subscribe to all users' presence and create/update DOM elements for each cursor: ```typescript theme={null} const cursors = new Map(); // Generate a consistent color from a string function stringToColor(str: string): string { let hash = 0; for (let i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } const hue = hash % 360; return `hsl(${hue}, 70%, 50%)`; } client.presence.sync('*', ({ userId, state }) => { // Skip our own cursor if (userId === client.userId) return; // User disconnected -- remove their cursor if (state === null) { cursors.get(userId)?.remove(); cursors.delete(userId); return; } // Create or get the cursor element let el = cursors.get(userId); if (!el) { el = document.createElement('div'); el.className = 'remote-cursor'; el.style.position = 'fixed'; el.style.pointerEvents = 'none'; el.style.zIndex = '9999'; el.style.transition = 'transform 0.1s ease-out'; // Cursor dot const dot = document.createElement('div'); dot.style.width = '8px'; dot.style.height = '8px'; dot.style.borderRadius = '50%'; dot.style.backgroundColor = stringToColor(userId); el.appendChild(dot); // Name label const label = document.createElement('span'); const user = client.users.active.get(userId) ?? client.users.all.get(userId); label.textContent = user?.name ?? userId; label.style.fontSize = '12px'; label.style.marginLeft = '8px'; label.style.backgroundColor = stringToColor(userId); label.style.color = 'white'; label.style.padding = '2px 6px'; label.style.borderRadius = '4px'; label.style.whiteSpace = 'nowrap'; el.appendChild(label); document.body.appendChild(el); cursors.set(userId, el); } // Update position el.style.transform = `translate(${state.cursor.x}px, ${state.cursor.y}px)`; }); ``` ## Step 4: Clean Up on Disconnect Remove all cursor elements when the user leaves: ```typescript theme={null} window.addEventListener('beforeunload', () => { void client.disconnect(); }); ``` Other clients will receive a `null` state in the presence callback, which triggers cursor removal (handled in Step 3). ## Step 5: Add Typing Indicators (Optional) Extend the presence state to include a cursor mode: ```typescript theme={null} // Track typing state const input = document.querySelector('input'); input?.addEventListener('focus', () => { client.presence.update({ cursor: { x: 0, y: 0, mode: 'typing' }, }); }); input?.addEventListener('blur', () => { client.presence.update({ cursor: { x: 0, y: 0, mode: 'idle' }, }); }); // In the presence sync callback, check the mode: client.presence.sync('*', ({ userId, state }) => { if (!state || userId === client.userId) return; if (state.cursor.mode === 'typing') { showTypingIndicator(userId); } else { hideTypingIndicator(userId); } }); ``` ## Next Steps * Combine cursor tracking with [Follow Mode](/sdk/presence#example-follow-mode) to let users follow each other * Add cursor click animations using [Broadcasts](/sdk/broadcasts) * Track scroll position to sync viewports across users # File Sharing Source: https://docs.collab-kit.com/guides/file-sharing Upload, share, and manage files within collaborative rooms. This guide shows you how to build a file sharing feature within your collaborative application using CollabKit's Storage module. ## What You'll Build * File upload with drag-and-drop support * Filterable file gallery (by type, by user) * File deletion * Image preview for uploaded images ## Prerequisites * A CollabKit server running with a room and users created * `@collab-kit/client` installed ## Step 1: Set Up the Client ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); ``` Storage uploads and deletes are editor actions. Viewers can list and open files, but cannot upload or delete files. ## Step 2: Upload Files The Storage module uploads files via HTTP (not WebSocket): ```typescript theme={null} const fileInput = document.getElementById('file-input') as HTMLInputElement; fileInput.addEventListener('change', async () => { const file = fileInput.files?.[0]; if (!file) return; try { const result = await client.storage.upload({ file }); console.log('Uploaded:', result.key, result.url); renderFiles(); // Refresh the file list } catch (err) { console.error('Upload failed:', err); } }); ``` ## Step 3: Add Drag-and-Drop ```typescript theme={null} const dropZone = document.getElementById('drop-zone'); dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('drag-over'); }); dropZone.addEventListener('dragleave', () => { dropZone.classList.remove('drag-over'); }); dropZone.addEventListener('drop', async (e) => { e.preventDefault(); dropZone.classList.remove('drag-over'); const files = e.dataTransfer?.files; if (!files?.length) return; for (const file of files) { const result = await client.storage.upload({ file }); console.log('Uploaded:', result.url); } renderFiles(); }); ``` ## Step 4: List and Filter Files ```typescript theme={null} // All files in the room const allFiles = await client.storage.getAll(); // Only images const images = await client.storage.getAll({ mimeType: 'image/' }); // Only PDFs const pdfs = await client.storage.getAll({ mimeType: 'application/pdf' }); // Only my files const myFiles = await client.storage.getAll({ userId: client.userId }); // Combine filters const myImages = await client.storage.getAll({ mimeType: 'image/', userId: client.userId, }); ``` ## Step 5: Render a File Gallery ```typescript theme={null} async function renderFiles(filter?: { mimeType?: string; userId?: string }) { const files = await client.storage.getAll(filter); const gallery = document.getElementById('file-gallery'); gallery.innerHTML = files.map((file) => { const isImage = file.mimeType?.startsWith('image/'); const sizeKB = (file.size / 1024).toFixed(1); const uploader = file.uploadedBy ? client.users.active.get(file.uploadedBy)?.name ?? client.users.all.get(file.uploadedBy)?.name ?? 'Unknown' : 'Unknown'; return `
${isImage ? `${file.filename}` : `
${getFileIcon(file.mimeType)}
` }
${file.filename} ${sizeKB} KB By ${uploader}
`; }).join(''); } function getFileIcon(mimeType: string | null): string { if (!mimeType) return '📄'; if (mimeType.startsWith('image/')) return '🖼️'; if (mimeType.startsWith('video/')) return '🎬'; if (mimeType.startsWith('audio/')) return '🎵'; if (mimeType === 'application/pdf') return '📑'; return '📄'; } ``` ## Step 6: Delete Files ```typescript theme={null} async function deleteFile(key: string) { if (!confirm('Delete this file?')) return; await client.storage.delete({ key }); renderFiles(); // Refresh } ``` ## Step 7: Filter Buttons ```typescript theme={null} const filterBtns = document.querySelectorAll('[data-filter]'); filterBtns.forEach((btn) => { btn.addEventListener('click', () => { const filter = btn.getAttribute('data-filter'); switch (filter) { case 'all': renderFiles(); break; case 'images': renderFiles({ mimeType: 'image/' }); break; case 'documents': renderFiles({ mimeType: 'application/pdf' }); break; case 'mine': renderFiles({ userId: client.userId }); break; } }); }); ``` ## Notify Others with Broadcasts Since file operations use HTTP (not WebSocket), other users don't automatically see new uploads. Use broadcasts to notify them: ```typescript theme={null} // After uploading const result = await client.storage.upload({ file }); client.notifications.broadcast('file-uploaded', { key: result.key, url: result.url, filename: file.name, uploadedBy: client.userId, }); // Listen for new uploads from others client.notifications.on('file-uploaded', ({ filename, uploadedBy }) => { const user = client.users.active.get(uploadedBy) ?? client.users.all.get(uploadedBy); showNotification(`${user?.name} uploaded ${filename}`); renderFiles(); // Refresh the gallery }); ``` ## Next Steps * Add file preview modals for images and PDFs * Attach files to [comments](/guides/comments-system) * Use [stores](/guides/real-time-stores) to track file metadata (tags, descriptions) * Set up [webhooks](/guides/webhooks-workflows) to process uploads server-side # Real-Time Stores Source: https://docs.collab-kit.com/guides/real-time-stores Sync application state across users with schema-driven KV stores. This guide shows you how to use CollabKit's Stores module to build a shared task board where all changes are synced in real time across connected clients. ## What You'll Build * A shared task list with real-time sync * Type-safe store operations with schema validation * Live update rendering when other users make changes ## Prerequisites * A CollabKit server running with a room and users created * `@collab-kit/client` and `@collab-kit/utils` installed ## Step 1: Define Your Schema Create a schema using `defineStores()`. This gives you full type safety and validation: ```typescript theme={null} import { defineStores } from '@collab-kit/utils'; const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, assignee: { type: 'string' }, priority: { type: 'number', default: 0 }, }, }); ``` ### Schema Rules * `required: true` -- field must be present when calling `set()` (unless it has a `default`) * `default: value` -- applied automatically when the field is missing on `set()` * Fields without `required: true` become optional in TypeScript types ## Step 2: Initialize the Client with Stores ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', stores, }); await client.join({ role: 'editor' }); ``` Store writes require an editor session. Viewers can read stores and receive realtime store updates, but cannot call `set`, `update`, or `delete`. ## Step 3: Create, Read, Update, Delete ```typescript theme={null} // CREATE -- 'completed' defaults to false, 'priority' defaults to 0 const task = await client.stores.tasks.set({ key: 'task-1', value: { title: 'Design mockups', assignee: 'user-001' }, }); // READ one const fetched = await client.stores.tasks.get({ key: 'task-1' }); // { title: 'Design mockups', completed: false, assignee: 'user-001', priority: 0 } // READ all const allTasks = await client.stores.tasks.getAll(); // [{ key: 'task-1', value: { ... } }, ...] // UPDATE (partial -- only changes specified fields) await client.stores.tasks.update({ key: 'task-1', value: { completed: true }, }); // DELETE await client.stores.tasks.delete({ key: 'task-1' }); ``` ## Step 4: Listen for Real-Time Changes Store events fire when **other clients** make changes: ```typescript theme={null} // Listen for any change in the tasks store client.stores.tasks.on('changed', ({ key, action, value }) => { console.log(`[${action}] ${key}:`, value); // action is 'set', 'update', or 'delete' renderTaskList(); }); // Listen for changes to a specific key client.stores.tasks.on('task-1', (value) => { console.log('task-1 changed:', value); updateTaskUI('task-1', value); }); ``` ## Step 5: Build the Task Board UI ```typescript theme={null} const listEl = document.getElementById('task-list'); const formEl = document.getElementById('task-form'); const inputEl = document.getElementById('task-input') as HTMLInputElement; // Render all tasks async function renderTaskList() { const tasks = await client.stores.tasks.getAll(); listEl.innerHTML = tasks.map(({ key, value }) => `
${value.title} ${value.assignee ? `Assigned to: ${value.assignee}` : ''}
`).join(''); } // Add a new task formEl.addEventListener('submit', async (e) => { e.preventDefault(); const title = inputEl.value.trim(); if (!title) return; const key = `task-${Date.now()}`; await client.stores.tasks.set({ key, value: { title, assignee: client.userId }, }); inputEl.value = ''; renderTaskList(); }); // Toggle completion window.toggleTask = async (key: string, completed: boolean) => { await client.stores.tasks.update({ key, value: { completed } }); renderTaskList(); }; // Delete a task window.deleteTask = async (key: string) => { await client.stores.tasks.delete({ key }); renderTaskList(); }; // Re-render when other clients make changes client.stores.tasks.on('changed', () => renderTaskList()); // Initial render renderTaskList(); ``` ## Multiple Stores You can define multiple stores for different data types: ```typescript theme={null} const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, }, settings: { theme: { type: 'string', default: 'light' }, sortBy: { type: 'string', default: 'created' }, }, labels: { name: { type: 'string', required: true }, color: { type: 'string', required: true }, }, }); // Each store is accessed independently await client.stores.tasks.set({ key: 'task-1', value: { title: 'Ship it' } }); await client.stores.settings.set({ key: 'user-prefs', value: { theme: 'dark' } }); await client.stores.labels.set({ key: 'label-1', value: { name: 'Bug', color: '#ef4444' } }); ``` ## Syncing on Reconnect When the client reconnects after a network interruption, call `sync()` to reload the latest state: ```typescript theme={null} client.socket.on('reconnected', async () => { await client.stores.tasks.sync(); renderTaskList(); }); ``` ## Next Steps * Add [comments](/guides/comments-system) to individual tasks * Use [presence](/guides/cursor-tracking) to show which task a user is viewing * Set up [webhooks](/guides/webhooks-workflows) to notify external services when tasks change # Webhooks & Workflows Source: https://docs.collab-kit.com/guides/webhooks-workflows Automate server-side actions in response to collaboration events. This guide covers setting up webhooks for external notifications and workflows for running custom code when events occur in your rooms. ## Webhooks vs Workflows | Feature | Webhooks | Workflows | | ------------ | ------------------------------------------ | ---------------------------------------------------- | | **What** | HTTP POST to your server | Custom JS/TS code executed in Cloudflare Workers | | **Where** | Your own infrastructure | CollabKit's infrastructure | | **Use case** | Notify external services, update databases | Process events, transform data, trigger side effects | | **Setup** | Provide a URL | Provide source code | | **Auth** | HMAC-SHA256 signature verification | N/A (runs in a secure sandbox) | ## Part 1: Webhooks ### Create a Webhook Register a URL to receive HTTP callbacks when events occur: ```bash theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.example.com/collab-webhook", "events": ["participant.joined", "participant.left", "user.created"] }' ``` Save the `secret` from the response -- you need it to verify webhook signatures. ### Available Events | Event | When It Fires | | -------------------- | ----------------------------------------------- | | `participant.joined` | A user comes online in a room | | `participant.left` | A user goes offline | | `session.started` | First user joins a room (new session) | | `session.closed` | Last user leaves a room (session ends) | | `user.created` | A new user is added to a room via `POST /users` | | `user.updated` | A user's fields are changed | | `user.deleted` | A user is removed from a room | ### Webhook Payload Each delivery sends a POST request with a JSON body: ```json theme={null} { "event": "participant.joined", "data": { "userId": "user-001", "roomId": "c3003c93-60cf-4184-..." }, "timestamp": "2026-05-29T10:06:00.000Z" } ``` ### Verify Webhook Signatures Each delivery includes an HMAC-SHA256 signature. Verify it to ensure the payload is authentic: ```typescript theme={null} import { createHmac, timingSafeEqual } from 'crypto'; function verifyWebhookSignature( body: string, signature: string, secret: string ): boolean { const expected = createHmac('sha256', secret) .update(body) .digest('hex'); return timingSafeEqual( Buffer.from(expected), Buffer.from(signature) ); } // In your webhook handler (Express example) app.post('/collab-webhook', (req, res) => { const signature = req.headers['x-webhook-signature']; const body = JSON.stringify(req.body); if (!verifyWebhookSignature(body, signature, WEBHOOK_SECRET)) { return res.status(401).send('Invalid signature'); } // Process the event const { event, data } = req.body; switch (event) { case 'participant.joined': console.log(`User ${data.userId} joined room ${data.roomId}`); break; case 'session.closed': console.log(`Session ended in room ${data.roomId}`); break; } res.status(200).send('OK'); }); ``` ### Scope to a Room Limit webhooks to a specific room: ```bash theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "url": "https://api.example.com/room-webhook", "events": ["participant.joined"], "roomId": "c3003c93-60cf-4184-b85f-20be14d26dac" }' ``` ### Monitor Deliveries Check the delivery log to debug issues: ```bash theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/webhooks/wh_abc123/deliveries?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` Each delivery shows the status (`pending`, `success`, `failed`), HTTP status code, attempt count, and next retry time. ## Part 2: Workflows Workflows let you run custom JavaScript in response to events, directly on CollabKit's infrastructure. ### Create a Workflow ```bash theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "Log new participants", "events": ["participant.joined", "participant.left"], "code": "export default async function(event, { room }) {\n const roomData = await room.get();\n console.log(`[${event.event}] User ${event.data.userId} in room ${roomData.name}`);\n return { logged: true };\n}" }' ``` ### Workflow Bindings Your workflow code receives the event and a set of bindings: ```typescript theme={null} export default async function(event, bindings) { // event.event -- "participant.joined", etc. // event.data -- event-specific payload const { store, room, files } = bindings; // Read from KV stores const value = await store.get('tasks', 'task-1'); const all = await store.getAll('tasks'); // Read room and user data const roomData = await room.get(); const users = await room.getUsers(); // Work with files const fileList = await files.list(); return { success: true }; } ``` | Binding | Methods | Description | | ------- | ---------------------------------- | -------------------------------- | | `store` | `get(store, key)`, `getAll(store)` | Read-only KV store access | | `room` | `get()`, `getUsers()` | Read-only room and user metadata | | `files` | `list()` | Scoped R2 file listing | | `event` | (passed as first arg) | The triggering event data | ### Example: Notify Slack on Session Start ```typescript theme={null} export default async function(event, { room }) { if (event.event !== 'session.started') return; const roomData = await room.get(); await fetch('https://hooks.slack.com/services/YOUR/WEBHOOK/URL', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `Collaboration session started in "${roomData.name}"`, }), }); return { notified: true }; } ``` ### Example: Auto-Create Welcome Store Entry ```typescript theme={null} export default async function(event, { store }) { if (event.event !== 'user.created') return; // Note: workflow store access is read-only // Use this to check existing state, then use fetch() // to call external APIs with the data const existing = await store.get('settings', event.data.userId); console.log('User settings:', existing); return { checked: true }; } ``` ### Monitor Executions Check the execution log: ```bash theme={null} curl "https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123/executions?limit=10" \ -H "Authorization: Bearer ${TOKEN}" ``` Each execution shows status, duration, and the result or error message. ### Enable/Disable Workflows ```bash theme={null} # Disable curl -X PATCH https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"enabled": false}' # Re-enable curl -X PATCH https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/workflows/wf_abc123 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"enabled": true}' ``` ## Next Steps * Combine webhooks with workflows for a complete event pipeline * Use the [dashboard](https://dash.collab-kit.com) to manage webhooks and workflows visually * Review the [Webhook API reference](/api-reference/webhooks/create-webhook) and [Workflow API reference](/api-reference/workflows/create-workflow) for full details # Introduction Source: https://docs.collab-kit.com/index Build real-time collaborative apps and agents in minutes. # What is CollabKit? CollabKit is a real-time collaboration platform that helps you build multiplayer experiences. It provides a **server** and a **client SDK** (for the browser) that handle the hard parts of real-time collaboration so you can focus on the product. Get up and running in under 5 minutes. Explore the browser SDK modules. Full HTTP API documentation. Practical tutorials and examples. Review operational limits, SLAs, and benchmark targets. ## Key Features | Feature | Description | | -------------------- | ------------------------------------------------------------------------------------ | | **Users Management** | Manage participants, track online/offline status in real-time. | | **Follow Users** | Let users follow each other's cursors and actions in real-time. | | **Presence** | Share ephemeral state (cursors, selections, scroll positions) with 50ms throttling. | | **Stores** | Schema-driven, type-safe KV stores synced across all connected clients in real time. | | **Version Control** | Track changes per user and roll back to previous versions. | | **CRDT (Yjs)** | Optional Yjs provider for conflict-free collaborative editing. | | **Comments** | Threaded comments with replies, reactions, and user tagging. | | **Broadcasts** | Send custom events to all participants or specific users. Fire-and-forget messaging. | | **File Storage** | Upload, list, and serve files scoped to rooms via R2 object storage. | | **Webhooks** | HTTP callbacks for server-side events (user joined, session started, etc.). | | **Workflows** | Run custom JavaScript in response to events via Cloudflare Workers. | ## Architecture CollabKit is split into three npm packages: ``` @collab-kit/client -- Browser SDK (WebSocket + HTTP) @collab-kit/utils -- Shared types, enums, and schema utilities @collab-kit/server -- Cloudflare Worker (Durable Objects, D1, R2, KV) ``` The **client** connects to the **server** over a WebSocket for real-time features (presence, stores, comments, broadcasts, CRDT) and uses HTTP for file storage and user creation. The **utils** package provides shared TypeScript types and the `defineStores()` schema utility used by both client and server. ## Packages Browser SDK for connecting to rooms and using collaboration features. Shared types, enums, and the `defineStores()` schema utility. Cloudflare Worker server. Self-hosted or managed. ## Next Steps Create an account on the [dashboard](https://dash.collab-kit.com) and find your API key in **Settings**. Use the REST API to [create a room](/api-reference/rooms/create-room) and [create users](/api-reference/users/create-user) to get a JWT token. Alternatively, you can also do this through the [dashboard](https://dash.collab-kit.com). Install `@collab-kit/client`, pass the JWT token, and call `connect()` + `join()`. Use [presence](/sdk/presence), [stores](/sdk/stores), [comments](/sdk/comments), and more. # Limits and Benchmarks Source: https://docs.collab-kit.com/limits-and-benchmarks Operational limits, delivery SLAs, and benchmark targets for CollabKit. This page lists CollabKit limits and benchmark targets for the latest version. ## Rate Limits for APIs When rate limits are exceeded, APIs may return `429 Too Many Requests`. | Limit | Value | | --------------------- | ---------------------------------------------------------------------- | | General REST API | 600 requests/minute per account, burst 120 requests/10 seconds | | Create user | 120 requests/minute per account, burst 30 requests/10 seconds per room | | Create room | 60 requests/minute per account | | List endpoints | 300 requests/minute per account | | Storage upload | 20 uploads/minute per user, 100 uploads/minute per room | | Storage delete | 60 deletes/minute per user | | Storage list files | 60 requests/minute per user | | Webhook/workflow CRUD | 60 requests/minute per account | | Auth/login routes | 10 requests/minute per IP | | List users page size | 100 users per page | ## CollabKit Store Limits | Limit | Value | | --------------- | ----------------- | | Stores per room | 10 | | Writes to store | 1 write per 250ms | | Store size | `TBD` | Each named store counts toward the per-room store limit. Store entries, document shape, and payload-size limits are `TBD`. ## Comment Limits | Limit | Value | | --------------------------- | ----- | | Top-level comments per room | 1,000 | | Comment payload size | `TBD` | Replies are limited to one level of nesting. A top-level comment can have replies, but replies cannot have nested replies. ## File Storage Limits | Limit | Value | | ---------------------- | ------------------------------------------------------- | | File size | 1 MB per file | | Per-room storage quota | 5 GB | | Files per room | 1,000 | | Upload rate | 20 uploads/minute per user, 100 uploads/minute per room | | Delete rate | 60 deletes/minute per user | | List files rate | 60 requests/minute per user | Files are scoped to rooms. Storage rate limits are enforced per Worker isolate for now. ## Room Limits Users can join a room with two roles: `editor` | `viewer`. | Scenario | Limit or target | | ----------------------------------- | ----------------------------------------- | | Maximum users in one room | 1,000 users with up to 200 active editors | | Maximum users in an all-editor room | 500 active users, all editors | | Concurrent joins and time SLA | 1.5sec (P50), 3sec (P95) | * **Editors:** * Allowed to mutate stores, files, add comments and more. * Use for users who actively mutate room state, stores, comments, storage, broadcasts, or CRDT documents. * **Viewers:** * Allowed to subscribe to all the changes in a room, including presence, stores, comments, files and more. * Use for users who need real-time updates but do not need to write collaborative state ## Webhook/Workflow Delivery SLAs | System | SLA | | ------------------------ | ---------------------------- | | WebSocket event delivery | 5-30 seconds after the event | | Analytics availability | 30 seconds after the event | WebSocket SLAs describe when connected clients should receive room events after the server processes the event. Analytics SLAs describe when event data should be available for querying or display. # Quickstart Source: https://docs.collab-kit.com/quickstart Get real-time collaboration running in your apps and agents in under 5 minutes. This guide walks you through the complete setup: creating an account, setting up a room, adding users, and connecting the client SDK. ## Prerequisites * Node.js 18+ installed * A running CollabKit server (self-hosted or managed) ## 1. Create an Account Sign up at the [CollabKit dashboard](https://dash.collab-kit.com). This creates your organization and generates an API key. ## 2. Get Your API Key Once logged in, go to **Settings** in the dashboard to find your **Account ID** and **API Key**. Save both the `accountId` and `apiKey`. You'll need them to construct the Bearer token for all API calls. ## 3. Create a Bearer Token All API requests (except auth) use Bearer authentication. The token is a Base64-encoded string of `accountId:apiKey`: ```bash theme={null} ACCOUNT_ID="a1b2c3d4-5678-..." API_KEY="7f3e8a2b9c1d4e5f..." TOKEN=$(echo -n "${ACCOUNT_ID}:${API_KEY}" | base64) ``` ## 4. Create a Room ```bash theme={null} curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/rooms \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"name": "my-first-room"}' ``` ```json Response theme={null} { "success": true, "data": { "room": { "id": "c3003c93-60cf-4184-...", "account_id": "a1b2c3d4-...", "name": "my-first-room", "state": "active", "created_at": "2026-05-29T..." } } } ``` ## 5. Create a User Create a user in the room. This returns a JWT token that the client SDK uses for authentication: ```bash theme={null} ROOM_ID="c3003c93-60cf-4184-..." curl -X POST https://api.collab-kit.com/v1/accounts/${ACCOUNT_ID}/users \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d "{ \"name\": \"Alice\", \"roomId\": \"${ROOM_ID}\", \"profilePicture\": \"https://example.com/alice.png\" }" ``` ```json Response theme={null} { "success": true, "data": { "user": { "id": "f47ac10b-58cc-4372-...", "room_id": "c3003c93-...", "name": "Alice", "status": "offline" }, "token": "eyJhbGciOiJIUzI1NiIs..." } } ``` The `token` in the response is a JWT valid for 90 days. Pass it to the client SDK to authenticate WebSocket connections. ## 6. Install the Client SDK and Connect ```bash theme={null} npm install @collab-kit/client @collab-kit/utils ``` ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: 'eyJhbGciOiJIUzI1NiIs...', // JWT from step 5 }); // join() opens the WebSocket, authenticates with the JWT, and sets the user online. // It defaults to { role: 'editor' }. await client.join({ role: 'editor' }); // Or join as a viewer. Viewers receive updates and can follow users, // but cannot write collaboration state. // await client.join({ role: 'viewer' }); console.log('Connected as:', client.currentUser?.name); console.log('Room:', client.currentRoom?.name); console.log('Active editors:', client.users.active.size); ``` ## 7. Add Real-Time Features Now that you're connected, add collaboration features: ```typescript Presence (Cursors) theme={null} // Share cursor position document.addEventListener('mousemove', (e) => { client.presence.update({ cursor: { x: e.clientX, y: e.clientY }, }); }); // Listen to other users' cursors client.presence.sync('*', ({ userId, state }) => { if (state) { renderCursor(userId, state.cursor); } }); ``` ```typescript Broadcasts theme={null} // Send a custom event client.notifications.broadcast('emoji-reaction', { emoji: '🎉', x: 500, y: 300, }); // Listen for events client.notifications.on('emoji-reaction', (data) => { showFloatingEmoji(data.emoji, data.x, data.y); }); ``` ```typescript Stores theme={null} import { defineStores } from '@collab-kit/utils'; const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, }, }); const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', stores, }); await client.join(); // Set a value (synced to all clients) await client.stores.tasks.set({ key: 'task-1', value: { title: 'Ship v1' }, }); ``` ## Next Steps Full API reference for all SDK modules. Build a cursor tracking experience and more. Detailed HTTP API documentation. Automate server-side actions on events. # Broadcasts Source: https://docs.collab-kit.com/sdk/broadcasts Send custom events to other participants in real time. The Broadcasts module lets you send fire-and-forget custom events to other participants in the room. Use it for emoji reactions, pings, notifications, cursor clicks, or any ephemeral communication. Access it via `client.notifications`. Broadcasts are fire-and-forget. They are not persisted and cannot be replayed. If a user is offline when an event is sent, they won't receive it. Only editors can send broadcasts. Viewers can receive broadcasts, but `broadcast(...)` is blocked for viewer sessions. ## Methods ### Send Event **`broadcast(event, data, userIds?)`** Send a custom event to other users in the room: ```typescript theme={null} // Send to everyone in the room client.notifications.broadcast('cursor-click', { x: 100, y: 200 }); // Send to specific users client.notifications.broadcast('ping', { message: 'hello' }, ['user-002']); ``` | Parameter | Type | Required | Description | | --------- | ---------- | -------- | --------------------------------------------------------------- | | `event` | `string` | Yes | Custom event name. Can be any string. | | `data` | `object` | Yes | Payload to send. Must be JSON-serializable. | | `userIds` | `string[]` | No | User IDs to target. If omitted, sent to all other participants. | ### Listen for Events **`on(event, callback)`** Listen for a specific event type: ```typescript theme={null} client.notifications.on('cursor-click', (data) => { console.log('Click at:', data.x, data.y); }); ``` ### Listen Once **`once(event, callback)`** Listen for an event, but only fire the callback once: ```typescript theme={null} client.notifications.once('welcome', (data) => { showWelcomeMessage(data.message); }); ``` ### Remove Listener **`off(event, callback)`** Remove a specific listener: ```typescript theme={null} const handler = (data) => console.log(data); const offHandler = client.notifications.on('cursor-click', handler); // Later, remove the listener client.notifications.off('cursor-click', handler); // Alternatively offHandler(); ``` ## Examples ### Emoji Reactions ```typescript theme={null} // Send a floating emoji function sendEmoji(emoji: string, x: number, y: number) { client.notifications.broadcast('emoji', { emoji, x, y }); } // Render floating emojis from other users client.notifications.on('emoji', ({ emoji, x, y }) => { const el = document.createElement('span'); el.textContent = emoji; el.className = 'floating-emoji'; el.style.left = `${x}px`; el.style.top = `${y}px`; document.body.appendChild(el); setTimeout(() => el.remove(), 2000); }); ``` ### User Notifications ```typescript theme={null} // Notify a specific user client.notifications.broadcast( 'mention', { from: client.userId, message: 'Check this out!' }, ['user-002'] ); // Listen for mentions client.notifications.on('mention', ({ from, message }) => { const sender = client.users.active.get(from) ?? client.users.all.get(from); showNotification(`${sender?.name}: ${message}`); }); ``` # Changelog Source: https://docs.collab-kit.com/sdk/changelog # Comments Source: https://docs.collab-kit.com/sdk/comments Add threaded comments with replies, reactions, and user tagging. ## Comments Collection The Comments module provides room-scoped threaded comments with support for replies, emoji reactions, and user tagging. All changes are synced in real time to other participants. Access it via `client.comments`. Editors can create and mutate comments. Viewers receive comment updates, but comment writes, reactions, tags, and replies are blocked for viewer sessions. ### Methods #### Add Comment **`add(text, opts?)`** Add a top-level comment to the room. Optionally tag users at creation: ```typescript theme={null} const comment = await client.comments.add('Hello from the comments feature!'); // With tags const comment = await client.comments.add('Check this out!', { tags: ['user-002', 'user-003'], }); ``` The returned `comment` is a `CollabKitCommentInstance` with methods for replies, reactions, and tags. #### Get Comments **`getAll()`** Fetch all comments in the room: ```typescript theme={null} const comments = await client.comments.getAll(); ``` #### Sync Comments **`sync()`** Force a full sync of comments from the server: ```typescript theme={null} await client.comments.sync(); ``` #### Delete Comment **`delete(commentId)`** Delete a top-level comment. This cascades and removes all its replies: ```typescript theme={null} await client.comments.delete(comment.id); ``` ### Events Listen for comment changes across the room: ```typescript theme={null} client.comments.on('add', (comment) => { console.log('New comment:', comment.text); }); ``` | Event | Payload | Description | | -------- | -------------------------- | ------------------------------------------------------- | | `add` | `CollabKitCommentInstance` | A new comment was added by another client | | `delete` | `string` (comment ID) | A comment was deleted by another client | | `update` | `CollabKitCommentInstance` | A comment was updated (reaction, tag, or reply changed) | ## Comment Instance Each comment returned by `add()` or `getAll()` is a `CollabKitCommentInstance` with its own properties and methods. ### Properties | Property | Type | Description | | ----------- | ---------------------------- | -------------------------------------- | | `id` | `string` | Unique comment ID | | `text` | `string` | Comment body | | `userId` | `string` | ID of the user who created the comment | | `parentId` | `string \| null` | Parent comment ID (null for top-level) | | `reactions` | `Reaction[]` | List of reactions on this comment | | `tags` | `string[]` | List of tagged user IDs | | `replies` | `CollabKitCommentInstance[]` | Child replies (one level deep) | | `createdAt` | `string` | ISO timestamp | ### Methods #### Reply to Comment **`reply(text, opts?)`** Add a reply to this comment (one level of nesting): ```typescript theme={null} const reply = await comment.reply('Great point!'); // With tags const reply = await comment.reply('Tagging you on this', { tags: ['user-002'], }); ``` #### Delete Reply **`deleteReply(replyId)`** Remove a reply: ```typescript theme={null} await comment.deleteReply(reply.id); ``` #### Add Reaction **`addReaction(reaction)`** Add an emoji reaction to the comment: ```typescript theme={null} await comment.addReaction('👍'); ``` Reactions on replies work the same way: ```typescript theme={null} const reply = await comment.reply('Nice!'); await reply.addReaction('🎉'); ``` #### Delete Reaction **`deleteReaction(reaction)`** Remove a reaction: ```typescript theme={null} await comment.deleteReaction('👍'); ``` #### Add Tag **`addTag(userId)`** Tag a user on this comment: ```typescript theme={null} await comment.addTag('user-002'); ``` #### Delete Tag **`deleteTag(userId)`** Remove a user tag: ```typescript theme={null} await comment.deleteTag('user-002'); ``` Tagged users receive a `commentTagged` event on their user instance. See [User Events](/sdk/users#events-1). ### Events Listen for updates to a specific comment: ```typescript theme={null} comment.on('update', (updatedComment) => { console.log('Comment updated:', updatedComment); // Fires when reactions, tags, or replies change }); ``` ## Examples ### Threaded Comments ```typescript theme={null} // Listen for new comments from others client.comments.on('add', (comment) => { renderComment(comment); }); // Listen for being tagged client.currentUser?.on('commentTagged', (comment) => { showNotification(`You were tagged in: "${comment.text}"`); }); // Add a comment with tags const comment = await client.comments.add('Design review needed', { tags: [client.userId], }); // Add reactions await comment.addReaction('👍'); // Reply const reply = await comment.reply('Looks good to me!'); await reply.addReaction('🎉'); // Listen for changes to this comment comment.on('update', (updated) => { console.log('Reactions:', updated.reactions); console.log('Replies:', updated.replies.length); }); // Clean up await comment.deleteReply(reply.id); await client.comments.delete(comment.id); ``` # SDK Overview Source: https://docs.collab-kit.com/sdk/overview Install and initialize the CollabKit browser SDK. The `@collab-kit/client` package is a browser SDK. It connects to your server over WebSocket and provides modules for users, rooms, presence, stores, comments, broadcasts, file storage, and CRDT collaboration. ## Installation ```bash npm theme={null} npm install @collab-kit/client @collab-kit/utils ``` ```bash pnpm theme={null} pnpm add @collab-kit/client @collab-kit/utils ``` ```bash yarn theme={null} yarn add @collab-kit/client @collab-kit/utils ``` `@collab-kit/utils` is a peer dependency that provides shared types and the `defineStores()` utility. Install it alongside the client. ## Initialization ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', // from POST /users }); await client.join(); // defaults to { role: 'editor' } console.log(client.currentUser); // { id, name, status: 'online', ... } console.log(client.currentRoom); // { id, name, state: 'active', ... } ``` ## Constructor Options ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; import { defineStores } from '@collab-kit/utils'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', stores: defineStores({ /* optional store schemas */ }), }); ``` Base HTTP(S) URL of your CollabKit server. The SDK automatically derives the WebSocket URL from this. JWT token returned when [creating a user](/api-reference/users/create-user) via `POST /users`. Contains the `accountId`, `userId`, and `roomId`. (Optional) You must pre-define store schemas with [`defineStores()`](/sdk/stores#schema-definition). during initialization. Each client instance has access to the stores that are passed to it during initialization. ## User Lifecycle ### Step 1: `join()` ```typescript theme={null} await client.join(); // Socket opens, the JWT authenticates, the user is set online, // active editors, comments, and stores are loaded // Other clients receive a 'userJoined' event only for editors ``` `join()` is the single entry point. It opens the WebSocket (if not already open), authenticates with the server using the JWT token, sets the current user as `online`, loads active editors, comments, and stores, and notifies other participants when this session is an editor.
After `join()` resolves: * `client.users.active` is populated with active editors, excluding self * `client.users.all` remains empty until you call `client.users.list(...)` * `client.currentUser` and `client.currentRoom` are set * Comments and stores are hydrated * Editors can write collaboration state; viewers receive updates and can follow users Join as a viewer when the user should not collaborate directly: ```typescript theme={null} await client.join({ role: 'viewer' }); await client.updateRole('editor'); ``` ### Step 2: `disconnect()` ```typescript theme={null} await client.disconnect(); // Other clients receive a 'userLeft' event ``` Gracefully disconnects. Notifies other participants before closing the socket. Call `disconnect()` in the `beforeunload` event to notify peers when the tab closes: ```typescript theme={null} window.addEventListener('beforeunload', () => { void client.disconnect(); }); ``` ## Modules The client provides access to collaboration features through modules: | Module | Property | Description | | ----------------------------- | ---------------------- | ----------------------------------------------- | | [Users](/sdk/users) | `client.users` | Track active editors and page through all users | | [Room](/sdk/rooms) | `client.room` | Room operations and connection lifecycle | | [Presence](/sdk/presence) | `client.presence` | Ephemeral state (cursors, selections) | | [Broadcasts](/sdk/broadcasts) | `client.notifications` | Custom event broadcasting | | [Stores](/sdk/stores) | `client.stores` | Schema-driven KV stores | | [Comments](/sdk/comments) | `client.comments` | Threaded comments | | [Storage](/sdk/storage) | `client.storage` | File upload and management | ## Next Steps Full API reference for all SDK modules. Build a cursor tracking experience and more. # Presence Source: https://docs.collab-kit.com/sdk/presence Share ephemeral state like cursors, selections, and scroll positions in real time. The Presence module lets users share short-lived, ephemeral state with other participants. Common use cases include live cursor tracking, text selections, scroll positions, and typing indicators. Access it via `client.presence`. Presence data is **not persisted** -- it exists only while users are connected. Only editors can send presence updates. Viewers can receive presence updates and can follow users, but `presence.update(...)` is blocked for viewer sessions. ## Methods ### Update Presence **`update()`** Broadcast your current presence state to other participants. The input is a freeform object -- you can include any data you want. ```typescript theme={null} client.presence.update({ cursor: { x: 100, y: 200 }, screen: { width: 1920, height: 1080 }, }); ``` Updates are **throttled at 50ms** to prevent flooding the WebSocket. If you call `update()` more frequently, the latest state is sent after the throttle window. ```typescript theme={null} // Track cursor movement -- throttled automatically document.addEventListener('mousemove', (e) => { client.presence.update({ cursor: { x: e.clientX, y: e.clientY, state: 'idle' }, }); }); // Track typing state document.querySelector('input')?.addEventListener('keydown', () => { client.presence.update({ cursor: { x: 0, y: 0, state: 'typing' }, }); }); ``` ### Subscribe to Presence Updates **`sync(target, callback)`** Subscribe to presence changes from other users. The `target` parameter controls which users you receive updates from: | Target | Payload | Description | | ----------------- | ----------------------------------------------------------------------------- | ---------------------------- | | `'*'` | `{userId: string, state: `[`PresenceState`](/types/presence#presencestate)`}` | All users in the room | | `'following'` | `{userId: string, state: `[`PresenceState`](/types/presence#presencestate)`}` | Only users you are following | | `userId` (string) | `{userId: string, state: `[`PresenceState`](/types/presence#presencestate)`}` | A specific user by ID | ```typescript theme={null} // Subscribe to all users client.presence.sync('*', ({ userId, state }) => { if (state === null) { // User left or presence cleared, do something return; } // Update cursor position... }); ``` ### Unsubscribe from Presence Updates **`unsync()`** Unsubscribe from presence updates: ```typescript theme={null} client.presence.unsync(); ``` ### Get All States **`getStates()`** Get all current presence states as a map: ```typescript theme={null} const states = client.presence.getStates(); // Map for (const [userId, state] of states) { console.log(`${userId}:`, state); } ``` ### Get State **`getState(userId?: string)`** Get a specific user's presence state, or the current user's state if no ID is provided: ```typescript theme={null} // Get my own presence state const myState = client.presence.getState(); // Get another user's presence state const theirState = client.presence.getState('user-002'); ``` ## Examples ### Live Cursors ```typescript theme={null} const cursors = new Map(); // Create or update a cursor element for each user client.presence.sync('*', ({ userId, state }) => { if (userId === client.userId) return; // Skip self if (state === null) { // User left -- remove their cursor cursors.get(userId)?.remove(); cursors.delete(userId); return; } let el = cursors.get(userId); if (!el) { el = document.createElement('div'); el.className = 'remote-cursor'; document.body.appendChild(el); cursors.set(userId, el); } el.style.transform = `translate(${state.cursor.x}px, ${state.cursor.y}px)`; }); // Broadcast local cursor document.addEventListener('mousemove', (e) => { client.presence.update({ cursor: { x: e.clientX, y: e.clientY }, }); }); ``` ### Follow Users Use the `'following'` sync target combined with the [follow/unfollow](/sdk/users#follow) user methods to build a "follow mode" where one user's viewport mirrors another's: ```typescript theme={null} // Follow a user const targetUser = client.users.active.get('user-002') ?? client.users.all.get('user-002'); await targetUser.follow(); // Subscribe to followed user's presence const cursorEl = document.createElement('div'); cursorEl.className = 'follow-cursor'; document.body.appendChild(cursorEl); client.presence.sync('following', ({ userId, state }) => { if (state === null) { cursorEl.style.display = 'none'; return; } const user = client.users.active.get(userId) ?? client.users.all.get(userId); cursorEl.style.display = 'block'; cursorEl.style.left = `${state.cursor.x}px`; cursorEl.style.top = `${state.cursor.y}px`; cursorEl.title = user?.name ?? userId; }); // Unfollow when done await targetUser.unfollow(); ``` # Rooms Source: https://docs.collab-kit.com/sdk/rooms Access room details, manage room state, and handle connection lifecycle. A room is the virtual container. All users, presence, stores, comments, and files are scoped to a room. The Room module provides access to the current room's details. ## Room Lifecycle Rooms are created via the [REST API](/api-reference/rooms/create-room) and cannot be created from the client SDK. The typical flow is: 1. **Your backend** creates a room via `POST /rooms` 2. **Your backend** adds a user via `POST /users`, gets a JWT token 3. **The client SDK** connects and joins the room using the JWT token ```typescript theme={null} // After your backend creates a room and user: const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: jwtFromBackend, }); await client.join(); // defaults to { role: 'editor' } // currentUser.status is now 'online' // Other users receive a 'userJoined' event only when this session is an editor ``` 4. **The room** tracks participants, presence, and all collaboration state Users can join as an editor or viewer: ```typescript theme={null} await client.join({ role: 'viewer' }); console.log(client.role); // 'viewer' await client.updateRole('editor'); ``` Roles are session-scoped. Editors are active collaborators and appear in `client.users.active`; viewers receive realtime updates and can follow users, but cannot write collaboration state. After connecting and joining, [room information](/types/core#collabkitroom) is available using: ```typescript theme={null} console.log(client.currentRoom); // { id, name, state, active_participants, duration_seconds, ... } ``` You can also fetch the current room's details from the server: This method returns room details. Use `client.users.list(...)` for paginated users and `client.users.active` for active editors. ```typescript theme={null} const room = await client.room.get(); console.log(room); // Optionally pass a room ID to fetch a different room: const otherRoom = await client.room.get({ roomId: 'other-room-id' }); ``` ## Connection Lifecycle The client exposes low-level connection events via `client.socket`. Use these to monitor connection state, handle disconnections, and implement custom reconnection logic like so: ```typescript theme={null} client.socket.on('connected', () => { console.log('Initial WebSocket connection established'); }); ``` ### Lifecycle Events | Event | Payload | Description | | -------------- | --------------------- | ------------------------------------------------------------------------ | | `connected` | none | Initial WebSocket connection opened (authentication happens at `join()`) | | `disconnected` | none | WebSocket connection closed | | `reconnecting` | `{ attempt: number }` | A reconnection attempt is starting | | `reconnected` | none | Successfully reconnected and state has been rehydrated | | `failed` | none | All reconnection attempts have been exhausted | | `authFailed` | none | The JWT token was rejected by the server | ### Auto-Reconnection The SDK automatically reconnects when the WebSocket connection drops unexpectedly. It uses **exponential backoff** with configurable parameters: * Reconnection starts immediately after disconnection * Each subsequent attempt waits longer (exponential backoff) * After all attempts are exhausted, the `failed` event fires ### Pre-Reconnect Hooks (Optional) Register a handler that runs before each reconnection attempt. Use this to refresh tokens or perform setup before reconnecting: ```typescript theme={null} client.socket.onReconnect(async () => { // Refresh the JWT token before reconnecting const newToken = await fetchNewToken(); client.authToken = newToken; }); ``` ### Connection States The connection transitions through these states: ``` disconnected -> connecting -> connected -> disconnected | v reconnecting -> reconnected | v failed ``` ## Examples ### Offline Queue Buffer actions while offline and replay them on reconnect: ```typescript theme={null} const offlineQueue: (() => Promise)[] = []; let isOnline = true; client.socket.on('disconnected', () => { isOnline = false; }); client.socket.on('reconnected', async () => { isOnline = true; // Replay queued actions while (offlineQueue.length > 0) { const action = offlineQueue.shift(); await action?.(); } }); // Wrapper for store operations async function safeStoreSet(key: string, value: any) { const action = () => client.stores.tasks.set({ key, value }); if (isOnline) { await action(); } else { offlineQueue.push(action); } } ``` # Storage Source: https://docs.collab-kit.com/sdk/storage Upload, list, and manage files scoped to rooms. The Storage module provides file upload and management capabilities scoped to the current room. Files are stored in Cloudflare R2 and served via HTTP. Access it via `client.storage`. Unlike other modules, Storage operations use HTTP requests rather than WebSocket messages. Editors can upload and delete files. Viewers can list and read files, but upload and delete are blocked client-side for viewer sessions. ## Methods ### Upload File **`upload({ file })`** Upload a file to the current room: ```typescript theme={null} const fileInput = document.querySelector('input[type="file"]'); const file = fileInput.files[0]; const { key, url } = await client.storage.upload({ file }); console.log('Uploaded:', url); ``` Returns an [`UploadResult`](/types/storage#uploadresult). ### Get File URL **`getUrl({ key })`** Get the URL for a previously uploaded file: ```typescript theme={null} const url = await client.storage.getUrl({ key: 'uploads/image.png' }); ``` ### List Files **`getAll(opts?)`** List all files in the current room, optionally filtered: ```typescript theme={null} // All files const files = await client.storage.getAll(); // Filter by MIME type const images = await client.storage.getAll({ mimeType: 'image/' }); // Filter by user const myFiles = await client.storage.getAll({ userId: client.userId }); // Combine filters const myImages = await client.storage.getAll({ mimeType: 'image/', userId: client.userId, }); ``` | Option | Type | Description | | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `mimeType` | `string \| string[]` | Filter by MIME type. Use a trailing slash for categories (e.g., `'image/'`). Can be an array for OR matching. | | `userId` | `string` | Filter by the user who uploaded the files. | Returns an array of [`StorageFile`](/types/storage#storagefile) objects. ### Delete File **`delete({ key })`** Delete a file by its storage key: ```typescript theme={null} await client.storage.delete({ key: 'uploads/image.png' }); ``` ## Examples ### Image Gallery ```typescript theme={null} async function renderGallery() { const images = await client.storage.getAll({ mimeType: 'image/' }); const gallery = document.getElementById('gallery'); gallery.innerHTML = images.map((img) => ` `).join(''); } ``` # Stores Source: https://docs.collab-kit.com/sdk/stores Schema-driven, type-safe KV stores synced in real time. Stores are schema-driven key-value stores that sync across all connected clients in real time. Define a schema, and the SDK gives you fully typed CRUD operations with automatic validation and real-time change events. Access stores via `client.stores.`. Editors can write stores. Viewers receive store updates and can read stores, but `set`, `update`, and `delete` are blocked for viewer sessions. ## Schema Definition Define your store schemas using `defineStores()` from `@collab-kit/utils` and pass to the client constructor: ```typescript theme={null} import { defineStores } from '@collab-kit/utils'; const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, assignee: { type: 'string' }, }, settings: { theme: { type: 'string', required: true, default: 'light' }, fontSize: { type: 'number', required: true, default: 14 }, notifications: { type: 'boolean', default: true }, }, }); const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', stores, }); ``` | Option | Type | Description | | ---------- | ----------------------------------- | ------------------------------------------------------------------ | | `type` | `'string' \| 'number' \| 'boolean'` | The primitive type of the field | | `required` | `boolean` | If `true`, the field must be present on `set`. Defaults to `false` | | `default` | `string \| number \| boolean` | Default value applied when the field is missing on `set` | ## Methods ### Add Entry **`set({ key, value })`** Create or overwrite an entry. Required fields (without defaults) must be provided: ```typescript theme={null} await client.stores.tasks.set({ key: 'task-1', value: { title: 'Build stores feature', assignee: 'user-001' }, }); // 'completed' defaults to false (from schema) ``` ### Get Entry **`get({ key })`** Fetch a single entry by key. Returns `null` if the key doesn't exist: ```typescript theme={null} const task = await client.stores.tasks.get({ key: 'task-1' }); // { title: 'Build stores feature', completed: false, assignee: 'user-001' } | null ``` ### Get All Entries **`getAll()`** Fetch all entries in the store: ```typescript theme={null} const all = await client.stores.tasks.getAll(); // [{ key: 'task-1', value: { title: '...', completed: false, assignee: '...' } }] ``` ### Update Entry **`update({ key, value })`** Partially update an existing entry. Only the provided fields are validated and merged: ```typescript theme={null} await client.stores.tasks.update({ key: 'task-1', value: { completed: true }, }); // Only 'completed' is updated; 'title' and 'assignee' remain unchanged ``` ### Delete Entry **`delete({ key })`** Delete an entry by key: ```typescript theme={null} await client.stores.tasks.delete({ key: 'task-1' }); ``` ### Sync Store **`sync()`** Force a full sync of the store from the server: ```typescript theme={null} await client.stores.tasks.sync(); ``` ## Events You can listen to changes made to a store like so: ```typescript theme={null} client.stores.tasks.on('changed', (event) => { console.log(event.key); // 'task-1' console.log(event.action); // 'set' | 'update' | 'delete' console.log(event.value); // the new value (or null on delete) }); // Alternatively, subscribe to a specific key client.stores.tasks.on('task-1', (value) => { console.log('task-1 changed:', value); }); ``` | Event | Payload | Description | | ------------- | -------------------------------------------------------------------------- | ------------------------------------ | | `changes` | `{ key: string, action: 'set' \| 'update' \| 'delete', value: T \| null }` | Store changes along with the action | | `` | `T` | Value as defined in the store schema | Store events fire for changes made by **other clients**. Your own `set`/`update`/`delete` calls resolve with the new value directly. ## Validation The client validates values against the schema before sending them to the server: * **`set`**: All `required` fields (without `default`) must be provided. Default values are applied for missing fields that have defaults. * **`update`**: Only provided fields are validated against their schema types. * **Type checking**: Values must match the declared `type` (`string`, `number`, or `boolean`). If validation fails, the operation throws an error before making a network request. ## Examples ### Task Store ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; import { defineStores } from '@collab-kit/utils'; const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, assignee: { type: 'string' }, }, }); // Listen for changes from other clients client.stores.tasks.on('changed', ({ key, action, value }) => { console.log(`[${action}] ${key}:`, value); renderTaskList(); }); // Create a task await client.stores.tasks.set({ key: 'task-1', value: { title: 'Ship v1' }, }); // Mark complete await client.stores.tasks.update({ key: 'task-1', value: { completed: true }, }); ``` # Users Source: https://docs.collab-kit.com/sdk/users Track active editors and page through all users in a room. The Users module exposes two user maps with different guarantees: * `users.active` is the complete realtime set of active editors in the room, excluding the current user. * `users.all` is a local cache populated only by explicit `users.list(...)` calls, excluding the current user. Access the collection via `client.users`, or use `client.currentUser` for the local user. ## Collections | Property | Type | Description | | -------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- | | `active` | `Map` | Active editor collaborators, excluding self. Populated from `join()` and maintained by editor join/leave broadcasts. | | `all` | `Map` | Explicit paginated cache, excluding self. Only populated by `users.list(...)`. | Viewers are connected users, but they are not active collaborators. Viewer joins and leaves do not update `users.active`. ## Methods ### `get({ userId? })` Fetch one user from the server. This returns the user in the response, but does not populate `users.all`. ```typescript theme={null} const me = await client.users.get(); const user = await client.users.get({ userId: 'user-002' }); ``` ### `list({ pageSize, offset, search })` Fetch a page of users over HTTP and populate `users.all` with the returned users, excluding self. ```typescript theme={null} const page = await client.users.list({ pageSize: 100, offset: 0, search: 'ishita', }); console.log(page.users); console.log(page.total, page.hasMore); console.log(client.users.all); ``` Return shape: ```typescript theme={null} { users: CollabKitUser[]; total: number; pageSize: number; offset: number; hasMore: boolean; } ``` `pageSize` must be at most `100`; the SDK throws before making a network request if it is larger. ## Events User lifecycle events refer to active editors, not all connected viewers. ```typescript theme={null} client.users.on('userJoined', (user) => { console.log(`${user.name} became active`); }); client.users.on('userLeft', (user) => { console.log(`${user.name} is no longer active`); }); ``` | Event | Payload | Description | | ------------- | --------------- | ---------------------------------------- | | `userJoined` | `CollabKitUser` | An editor became active. | | `userLeft` | `CollabKitUser` | An editor stopped being active. | | `userCreated` | `CollabKitUser` | A new user record was added to the room. | | `userUpdated` | `CollabKitUser` | A tracked user's fields changed. | | `userDeleted` | `CollabKitUser` | A tracked user was removed. | ## Individual User Each user in `users.active`, `users.all`, or `client.currentUser` is a `CollabKitUser` instance. ### Properties | Property | Type | Description | | | ----------------- | ---------- | ------------------------------- | ------------------------------- | | `id` | `string` | Unique user ID | | | `name` | `string` | Display name | | | `status` | \`'online' | 'offline'\` | Current connection status | | `profile_picture` | \`string | undefined\` | Avatar URL | | `joined_at` | \`string | undefined\` | Last time the user came online | | `left_at` | \`string | undefined\` | Last time the user went offline | | `following` | `string[]` | User IDs this user is following | | | `followers` | `string[]` | User IDs following this user | | ### `update({ name, profilePicture })` Update your own user profile. Users cannot update another user's profile. ```typescript theme={null} await client.currentUser?.update({ name: 'Updated Name' }); ``` ### `delete()` Delete your own user record. ```typescript theme={null} await client.currentUser?.delete(); ``` ### `follow()` Follow another user. Viewers and editors can both follow users. ```typescript theme={null} const targetUser = client.users.active.get('user-002') ?? client.users.all.get('user-002'); await targetUser?.follow(); ``` Following is limited to one user at a time. If you're already following someone and call `follow()` on a different user, call `unfollow()` first. ### `unfollow()` Stop following a user: ```typescript theme={null} await targetUser?.unfollow(); ``` ## Example ```typescript theme={null} await client.join({ role: 'viewer' }); // Active editors are available immediately after join. renderActiveEditors([...client.users.active.values()]); // Load all users only when the UI needs a browsable list. const firstPage = await client.users.list({ pageSize: 100, offset: 0 }); renderAllUsers(firstPage.users); client.users.on('userJoined', () => renderActiveEditors([...client.users.active.values()])); client.users.on('userLeft', () => renderActiveEditors([...client.users.active.values()])); ``` # CRDT (Yjs) Source: https://docs.collab-kit.com/sdk/yjs Conflict-free collaborative editing with Yjs integration. CollabKit provides a Yjs provider that bridges a `Y.Doc` to the server via the existing WebSocket connection. This enables real-time collaborative editing with conflict-free resolution -- no additional infrastructure required. Editors can send Yjs document updates. Viewers can receive and apply Yjs updates, but outbound Yjs writes are blocked for viewer sessions. ## Installation The Yjs provider is exported from a separate entry point. You also need `yjs` as a peer dependency: ```bash theme={null} npm install yjs @collab-kit/client ``` ## Import ```typescript theme={null} import { CollabKitYjsProvider } from '@collab-kit/client/yjs'; import * as Y from 'yjs'; ``` ## Basic Usage ```typescript theme={null} import CollabKitClient from '@collab-kit/client'; import { CollabKitYjsProvider } from '@collab-kit/client/yjs'; import * as Y from 'yjs'; const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); // Create a Yjs document const ydoc = new Y.Doc(); // Bind it to CollabKit const provider = new CollabKitYjsProvider(client, ydoc, { documentId: 'main-editor', }); // Wait for initial sync from server provider.on('synced', () => { console.log('Document loaded from server'); }); ``` ## Constructor ```typescript theme={null} new CollabKitYjsProvider(client, ydoc, options); ``` An initialized and connected `CollabKitClient` instance. A Yjs document instance. Unique identifier for the CRDT document within the room. Multiple documents can coexist in the same room by using different IDs. ## Properties | Property | Type | Description | | -------- | --------- | ---------------------------------------------------- | | `synced` | `boolean` | Whether the initial sync from the server is complete | ## Events ### `synced` Fires when the initial document state has been loaded from the server: ```typescript theme={null} provider.on('synced', () => { console.log('Document is ready'); }); ``` ## Methods ### `destroy()` Disconnect the provider and clean up resources: ```typescript theme={null} provider.destroy(); ``` ## TipTap Integration CollabKit's Yjs provider works with [TipTap](https://tiptap.dev/) and its collaboration extension: ```typescript theme={null} import { Editor } from '@tiptap/core'; import StarterKit from '@tiptap/starter-kit'; import Collaboration from '@tiptap/extension-collaboration'; import CollabKitClient from '@collab-kit/client'; import { CollabKitYjsProvider } from '@collab-kit/client/yjs'; import * as Y from 'yjs'; // Set up CollabKit const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', }); await client.join({ role: 'editor' }); // Set up Yjs const ydoc = new Y.Doc(); const provider = new CollabKitYjsProvider(client, ydoc, { documentId: 'main-editor', }); // Create the editor const editor = new Editor({ element: document.getElementById('editor'), extensions: [ StarterKit.configure({ history: false }), // Disable built-in undo Collaboration.configure({ document: ydoc }), ], }); // Wait for sync before allowing edits provider.on('synced', () => { editor.setEditable(true); }); ``` Disable TipTap's built-in `history` extension when using Collaboration. Yjs has its own undo/redo management. ## Multiple Documents You can create multiple providers for different documents within the same room: ```typescript theme={null} const ydocEditor = new Y.Doc(); const ydocCanvas = new Y.Doc(); const editorProvider = new CollabKitYjsProvider(client, ydocEditor, { documentId: 'editor', }); const canvasProvider = new CollabKitYjsProvider(client, ydocCanvas, { documentId: 'canvas', }); ``` ## How It Works The Yjs provider uses CollabKit's WebSocket connection to synchronize document state: 1. **Initial sync**: On creation, the provider sends a `crdtSyncRequest` to the server, which returns the full document state. 2. **Local changes**: When the local `Y.Doc` is modified, the provider sends the delta as a `crdtUpdate` message. 3. **Remote changes**: The server broadcasts `crdtUpdated` messages to all other clients, which the provider applies to the local `Y.Doc`. 4. **Persistence**: The server stores the document state in its SQLite database. Documents survive server restarts. No additional server configuration is needed -- CRDT support is built into the CollabKit server. # Enums & Constants Source: https://docs.collab-kit.com/types/constants MessageType, ResponseCode, Module enums and exported constants. ```typescript theme={null} import { MessageType, ResponseCode, Module, WEBHOOK_EVENT_NAMES, RESERVED_STORE_PREFIX, COMMENTS_STORE_NAME } from '@collab-kit/utils'; ``` ## MessageType All WebSocket message types for the real-time protocol. ```typescript theme={null} enum MessageType { // Client -> Server // Note: JOIN_ROOM performs BOTH authentication (via authToken) and join — // there is no separate authentication handshake message. UPDATE_USER = 'updateUser', DELETE_USER = 'deleteUser', GET_USER = 'getUser', GET_USERS = 'getUsers', JOIN_ROOM = 'joinRoom', GET_ROOM = 'getRoom', BROADCAST_MESSAGE = 'broadcastMessage', STORE_GET = 'storeGet', STORE_GET_ALL = 'storeGetAll', STORE_SET = 'storeSet', STORE_UPDATE = 'storeUpdate', STORE_DELETE = 'storeDelete', COMMENT_ADD = 'commentAdd', COMMENT_DELETE = 'commentDelete', COMMENT_GET_ALL = 'commentGetAll', COMMENT_ADD_REACTION = 'commentAddReaction', COMMENT_DELETE_REACTION = 'commentDeleteReaction', COMMENT_ADD_TAG = 'commentAddTag', COMMENT_DELETE_TAG = 'commentDeleteTag', PRESENCE_UPDATE = 'presenceUpdate', CRDT_SYNC_REQUEST = 'crdtSyncRequest', CRDT_UPDATE = 'crdtUpdate', FOLLOW_USER = 'followUser', UNFOLLOW_USER = 'unfollowUser', // Server -> Client (responses) UPDATE_USER_RESPONSE = 'updateUserResponse', DELETE_USER_RESPONSE = 'deleteUserResponse', GET_USER_RESPONSE = 'getUserResponse', GET_USERS_RESPONSE = 'getUsersResponse', JOIN_ROOM_RESPONSE = 'joinRoomResponse', GET_ROOM_RESPONSE = 'getRoomResponse', BROADCAST_MESSAGE_RESPONSE = 'broadcastMessageResponse', STORE_GET_RESPONSE = 'storeGetResponse', STORE_GET_ALL_RESPONSE = 'storeGetAllResponse', STORE_SET_RESPONSE = 'storeSetResponse', STORE_UPDATE_RESPONSE = 'storeUpdateResponse', STORE_DELETE_RESPONSE = 'storeDeleteResponse', COMMENT_ADD_RESPONSE = 'commentAddResponse', COMMENT_DELETE_RESPONSE = 'commentDeleteResponse', COMMENT_GET_ALL_RESPONSE = 'commentGetAllResponse', COMMENT_ADD_REACTION_RESPONSE = 'commentAddReactionResponse', COMMENT_DELETE_REACTION_RESPONSE = 'commentDeleteReactionResponse', COMMENT_ADD_TAG_RESPONSE = 'commentAddTagResponse', COMMENT_DELETE_TAG_RESPONSE = 'commentDeleteTagResponse', CRDT_SYNC_REQUEST_RESPONSE = 'crdtSyncRequestResponse', FOLLOW_USER_RESPONSE = 'followUserResponse', UNFOLLOW_USER_RESPONSE = 'unfollowUserResponse', // Server -> Client (broadcasts) USER_JOINED = 'userJoined', USER_JOINED_BATCH = 'userJoinedBatch', USER_LEFT = 'userLeft', USER_LEFT_BATCH = 'userLeftBatch', STORE_UPDATED = 'storeUpdated', COMMENT_ADDED = 'commentAdded', COMMENT_DELETED = 'commentDeleted', COMMENT_UPDATED = 'commentUpdated', PRESENCE_UPDATED = 'presenceUpdated', CRDT_UPDATED = 'crdtUpdated', } ``` ## ResponseCode Machine-readable error codes returned in `ServerResponseError`. ```typescript theme={null} enum ResponseCode { OK = 'OK', CREATED = 'CREATED', ACCEPTED = 'ACCEPTED', NO_CONTENT = 'NO_CONTENT', NOT_MODIFIED = 'NOT_MODIFIED', BAD_REQUEST = 'BAD_REQUEST', UNAUTHORIZED = 'UNAUTHORIZED', FORBIDDEN = 'FORBIDDEN', NOT_FOUND = 'NOT_FOUND', METHOD_NOT_ALLOWED = 'METHOD_NOT_ALLOWED', INVALID_PAYLOAD = 'INVALID_PAYLOAD', TIMEOUT = 'TIMEOUT', CONFLICT = 'CONFLICT', FILE_TOO_LARGE = 'FILE_TOO_LARGE', UNPROCESSABLE_ENTITY = 'UNPROCESSABLE_ENTITY', TOO_MANY_REQUESTS = 'TOO_MANY_REQUESTS', ACCESS_DENIED = 'ACCESS_DENIED', ALREADY_EXISTS = 'ALREADY_EXISTS', UNKNOWN_MESSAGE_TYPE = 'UNKNOWN_MESSAGE_TYPE', INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', NOT_IMPLEMENTED = 'NOT_IMPLEMENTED', BAD_GATEWAY = 'BAD_GATEWAY', SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE', INTERNAL_ERROR = 'INTERNAL_ERROR', CREATION_FAILED = 'CREATION_FAILED', UPDATE_FAILED = 'UPDATE_FAILED', DELETION_FAILED = 'DELETION_FAILED', UPLOAD_FAILED = 'UPLOAD_FAILED', } ``` ## Module Server module identifiers used in error responses. ```typescript theme={null} enum Module { AUTH = 'AUTH', USER = 'USER', ROOM = 'ROOM', STORAGE = 'STORAGE', BROADCAST = 'BROADCAST', STORE = 'STORE', COMMENT = 'COMMENT', ORGANIZATION = 'ORGANIZATION', GENERAL = 'GENERAL', UNKNOWN = 'UNKNOWN', PRESENCE = 'PRESENCE', CRDT = 'CRDT', WEBSOCKET = 'WEBSOCKET', WEBHOOK = 'WEBHOOK', WORKFLOW = 'WORKFLOW', } ``` ## WEBHOOK\_EVENT\_NAMES Array of all valid webhook event name strings. Useful for validation or building UI selectors. ```typescript theme={null} const WEBHOOK_EVENT_NAMES: WebhookEventName[] = [ 'participant.joined', 'participant.left', 'session.started', 'session.closed', 'user.created', 'user.updated', 'user.deleted', ]; ``` ## RESERVED\_STORE\_PREFIX The prefix reserved for internal store names. User-defined stores must not start with this prefix. ```typescript theme={null} const RESERVED_STORE_PREFIX = '__collabkit-'; ``` ## COMMENTS\_STORE\_NAME The internal store name used by the comments system. This store is managed automatically and should not be written to directly. ```typescript theme={null} const COMMENTS_STORE_NAME = '__collabkit-comments'; ``` # Core Types Source: https://docs.collab-kit.com/types/core User, Room, Organization, Comment, and Session types. ```typescript theme={null} import type { CollabKitRole, CollabKitUser, CollabKitRoom, CollabKitOrganization, CollabKitComment, CollabKitUserSession } from '@collab-kit/utils'; ``` ## CollabKitRole ```typescript theme={null} type CollabKitRole = 'editor' | 'viewer'; ``` Roles are session-scoped. Editors are active collaborators and can write collaboration state. Viewers receive updates and can follow users, but cannot write stores, comments, presence, Yjs updates, storage, or broadcasts. ## CollabKitUser Represents a user in a room. ```typescript theme={null} interface CollabKitUser { id: string; room_id?: string; // present in REST API responses, stripped from WebSocket responses name: string; profile_picture?: string; custom_id?: string; // optional external identifier created_at?: string; // present in REST API responses, stripped from WebSocket responses joined_at?: string; left_at?: string; status: 'online' | 'offline'; token?: string; // present only in the create-user REST API response following?: string[]; followers?: string[]; } ``` > **Note:** `room_id`, `created_at`, and `token` are stripped from all WebSocket responses (broadcasts, join responses, user queries) for payload efficiency and security. They are still included in REST API responses (e.g., `GET /users/:id`, `POST /users`). ## CollabKitRoom Represents a collaboration room. ```typescript theme={null} interface CollabKitRoom { id: string; account_id: string; name: string; custom_id?: string; // optional external identifier created_at: string; state: 'active' | 'disabled'; duration_seconds: number; active_participants: number; total_users_created: number; } ``` ## CollabKitOrganization Represents an organization (account). ```typescript theme={null} interface CollabKitOrganization { id: string; user_id: string; email: string; name: string; description?: string; created_at: string; status: 'active' | 'disabled' | 'blocked'; } ``` ## CollabKitClientOptions Options passed to the `CollabKitClient` constructor. ```typescript theme={null} interface CollabKitClientOptions { serverUrl: string; authToken: string; stores?: T; } ``` ## CollabKitComment Represents a comment (top-level or reply). ```typescript theme={null} interface CollabKitComment { id: string; userId: string; text: string; reactions: Record; tags: string[]; parentId: string | null; replies: CollabKitComment[]; createdAt: string; } ``` ## CollabKitUserSession A user's session record (join/leave timestamps). ```typescript theme={null} interface CollabKitUserSession { id: string; user_id: string; room_id: string; joined_at: string; left_at?: string; } ``` # Presence Types Source: https://docs.collab-kit.com/types/presence Types for ephemeral presence state. ```typescript theme={null} import type { PresenceState, PresenceUpdateInput } from '@collab-kit/utils'; ``` ## PresenceState The full presence state for a user. ```typescript theme={null} interface PresenceState { cursor: { x: number; y: number; state: 'typing' | 'idle' }; screen: { width: number; height: number }; scroll: { x: number; y: number }; zoom?: number; color: string; userId: string; meta?: Record; } ``` ## PresenceUpdateInput The input shape for `client.presence.update()`. All fields are optional. ```typescript theme={null} type PresenceUpdateInput = { cursor?: { x: number; y: number; state: 'typing' | 'idle' }; screen?: { width: number; height: number }; scroll?: { x: number; y: number }; zoom?: number; meta?: Record; }; ``` # Response Types Source: https://docs.collab-kit.com/types/responses Standard response envelope and error types used by all API endpoints. ```typescript theme={null} import type { ServerResponse, ServerResponseError } from '@collab-kit/utils'; ``` ## ServerResponse Standard response envelope used by all API endpoints. ```typescript theme={null} interface ServerResponse> { type: string; success: boolean; description: string; data: T; error: ServerResponseError | null; requestId?: string; } ``` ## ServerResponseError Error details returned when `success` is `false`. ```typescript theme={null} interface ServerResponseError { module: Module; code: ResponseCode; message: string; } ``` # Socket Types Source: https://docs.collab-kit.com/types/socket Types for WebSocket connection state and lifecycle events. ```typescript theme={null} import type { CollabKitRole, SocketClientEventMap, SocketState, SocketClientOptions, SocketMessageResponseMap, InferResponseData, } from '@collab-kit/utils'; ``` ## CollabKitRole Session-scoped role chosen at join time or changed with `client.updateRole(...)`. ```typescript theme={null} type CollabKitRole = 'editor' | 'viewer'; ``` ## SocketClientEventMap Event map for `client.socket.on()` listeners. ```typescript theme={null} interface SocketClientEventMap { connected: void; disconnected: { code: number; reason: string }; reconnecting: { attempt: number }; reconnected: void; failed: void; authFailed: Error; } ``` ## SocketState Possible connection states. ```typescript theme={null} type SocketState = | 'connected' | 'connecting' | 'disconnected' | 'reconnected' | 'errored' | 'reconnecting' | 'reconnectFailure' | 'failed' | 'idle'; ``` ## SocketClientOptions Options for the low-level socket client. ```typescript theme={null} interface SocketClientOptions { url: string; authToken: string; reconnection?: { enabled: boolean; maxAttempts: number; initialDelay: number; maxDelay: number; backoffFactor: number; }; } ``` ## SocketMessageResponseMap Maps each client request `MessageType` to the `data` shape of its corresponding server response. Used by `sendMessagePromise` to infer the response type from the request type at compile time. > **Note:** `CollabKitUser` objects in WebSocket responses have `token`, `room_id`, and `created_at` stripped for payload efficiency and security. These fields are only present in REST API responses. ```typescript theme={null} interface SocketMessageResponseMap { [MessageType.UPDATE_USER]: { user: CollabKitUser }; [MessageType.DELETE_USER]: { user: CollabKitUser }; [MessageType.GET_USER]: { user: CollabKitUser }; [MessageType.GET_USERS]: { users: CollabKitUser[] }; [MessageType.JOIN_ROOM]: { room: CollabKitRoom; currentUser: CollabKitUser; activeUsers: CollabKitUser[]; role: CollabKitRole; }; [MessageType.UPDATE_ROLE]: { role: CollabKitRole }; [MessageType.GET_ROOM]: { room: CollabKitRoom; users: CollabKitUser[] }; [MessageType.BROADCAST_MESSAGE]: BroadcastPayload; [MessageType.STORE_GET]: { key: string; value: Record | null }; [MessageType.STORE_GET_ALL]: { entries: Array<{ key: string; value: Record }> }; [MessageType.STORE_SET]: { key: string; value: Record }; [MessageType.STORE_UPDATE]: { key: string; value: Record }; [MessageType.STORE_DELETE]: { key: string }; [MessageType.COMMENT_ADD]: { comment: CollabKitComment }; [MessageType.COMMENT_DELETE]: { commentId: string }; [MessageType.COMMENT_GET_ALL]: { comments: CollabKitComment[] }; // ... and more for reactions, tags, CRDT, follow/unfollow } ``` ## Broadcast Message Types The server sends batched broadcast messages to connected clients when editors become active or inactive: ```typescript theme={null} interface UserJoinedBatchBroadcast { type: MessageType.USER_JOINED_BATCH; // 'userJoinedBatch' data: { users: CollabKitUser[] }; } interface UserLeftBatchBroadcast { type: MessageType.USER_LEFT_BATCH; // 'userLeftBatch' data: { users: CollabKitUser[] }; } ``` Join and leave events are batched over a short window to reduce message volume under high concurrency. Viewer joins/leaves are not broadcast. The client SDK handles these transparently and emits individual `userJoined` / `userLeft` events per active editor. ## InferResponseData Helper type that infers the response `data` type for a given client message. Falls back to `Record` for unrecognised message types. ```typescript theme={null} type InferResponseData = T extends { type: infer M extends keyof SocketMessageResponseMap } ? SocketMessageResponseMap[M] : Record; ``` This enables type-safe responses from `sendMessagePromise` without manual casts: ```typescript theme={null} // The response data type is automatically inferred from the message type const response = await socket.sendMessagePromise({ type: MessageType.COMMENT_ADD, text: 'Hello', }); // response.data is typed as { comment: CollabKitComment } ``` # Storage Types Source: https://docs.collab-kit.com/types/storage Types for file upload, listing, and filtering. ```typescript theme={null} import type { UploadResult, StorageFile, StorageGetAllOptions } from '@collab-kit/utils'; ``` ## UploadResult Returned by `client.storage.upload()`. ```typescript theme={null} interface UploadResult { key: string; url: string; } ``` ## StorageFile Represents a stored file returned by `client.storage.getAll()`. ```typescript theme={null} interface StorageFile { key: string; url: string; filename: string; mimeType: string | null; size: number; uploadedAt: string; uploadedBy: string | null; } ``` ## StorageGetAllOptions Filter options for `client.storage.getAll()`. ```typescript theme={null} interface StorageGetAllOptions { mimeType?: string | string[]; userId?: string; } ``` # Store Types Source: https://docs.collab-kit.com/types/stores Types for schema-driven KV store definitions and type inference. ```typescript theme={null} import type { StoreFieldDefinition, StoreSchema, StoresConfig, InferDocument } from '@collab-kit/utils'; ``` ## StoreFieldDefinition Defines a single field in a store schema. ```typescript theme={null} interface StoreFieldDefinition { type: 'string' | 'number' | 'boolean'; required?: boolean; default?: string | number | boolean; } ``` ## StoreSchema A record of field names to their definitions. ```typescript theme={null} type StoreSchema = Record; ``` ## StoresConfig A record of store names to their schemas. Passed to the client constructor. ```typescript theme={null} type StoresConfig = Record; ``` ## InferDocument Infers a TypeScript type from a store schema. Required fields become mandatory keys; optional fields become optional keys. ```typescript theme={null} type InferDocument = { [K in keyof S as S[K] extends { required: true } ? K : never]: FieldTypeMap[S[K]['type']]; } & { [K in keyof S as S[K] extends { required: true } ? never : K]?: FieldTypeMap[S[K]['type']]; }; ``` **Example:** ```typescript theme={null} const schema = { theme: { type: 'string' as const, required: true as const }, fontSize: { type: 'number' as const, required: true as const }, notifications: { type: 'boolean' as const }, }; // InferDocument resolves to: // { theme: string; fontSize: number; notifications?: boolean } ``` # Utility Methods Source: https://docs.collab-kit.com/types/utilities Exported functions from @collab-kit/utils. ```typescript theme={null} import { defineStores, codes } from '@collab-kit/utils'; ``` ## defineStores() Identity function that preserves store schema types for full TypeScript inference. Use it to define your store schemas when initializing the client. ```typescript theme={null} function defineStores(config: T): T; ``` ### Usage ```typescript theme={null} import { defineStores } from '@collab-kit/utils'; const stores = defineStores({ tasks: { title: { type: 'string', required: true }, completed: { type: 'boolean', default: false }, assignee: { type: 'string' }, }, settings: { theme: { type: 'string', required: true, default: 'light' }, fontSize: { type: 'number', required: true, default: 14 }, }, }); ``` The returned value is the same object you pass in, but TypeScript preserves the exact literal types of your schema. This enables full type inference when calling `client.stores..set()`, `get()`, etc. Pass the result to the `CollabKitClient` constructor: ```typescript theme={null} const client = new CollabKitClient({ serverUrl: 'https://api.collab-kit.com', authToken: '', stores, }); // Fully typed: value is { title: string; completed?: boolean; assignee?: string } await client.stores.tasks.set({ key: 'task-1', value: { title: 'Ship v1' }, }); ``` ## codes Mapping of [`ResponseCode`](/types/constants#responsecode) enum values to their HTTP status codes, descriptions, and messages. Useful for interpreting error responses. ```typescript theme={null} const codes: Record; ``` ### Usage ```typescript theme={null} import { codes, ResponseCode } from '@collab-kit/utils'; const info = codes[ResponseCode.NOT_FOUND]; // { description: 'NOT_FOUND', code: 404, message: 'Not found' } ``` # Webhook Types Source: https://docs.collab-kit.com/types/webhooks Types for webhook registrations, payloads, and delivery logs. ```typescript theme={null} import type { WebhookEventName, WebhookRegistration, WebhookRegistrationPublic, WebhookPayload, WebhookUser, } from '@collab-kit/utils'; ``` ## WebhookEventName Union of all webhook event types. ```typescript theme={null} type WebhookEventName = | 'participant.joined' | 'participant.left' | 'session.started' | 'session.closed' | 'user.created' | 'user.updated' | 'user.deleted'; ``` ## WebhookRegistration A webhook registration including the signing secret. Only returned at creation time. ```typescript theme={null} interface WebhookRegistration { id: string; organization_id: string; url: string; secret: string; events: WebhookEventName[]; room_id: string | null; enabled: boolean; created_at: string; updated_at: string; } ``` ## WebhookRegistrationPublic A webhook registration without the secret. Returned by list, get, and update endpoints. ```typescript theme={null} type WebhookRegistrationPublic = Omit; ``` ## WebhookUser A user object with the `token` field stripped. Used in webhook payloads. ```typescript theme={null} type WebhookUser = Omit; ``` ## WebhookPayload Union of all webhook payload types. Every payload includes `id`, `event`, and `timestamp` fields. ```typescript theme={null} type WebhookPayload = | ParticipantJoinedPayload // { event, room: CollabKitRoom, user: WebhookUser } | ParticipantLeftPayload // { event, room: CollabKitRoom, user: WebhookUser } | SessionStartedPayload // { event, room: CollabKitRoom } | SessionClosedPayload // { event, room: CollabKitRoom, users: WebhookUser[] } | UserCreatedPayload // { event, room: CollabKitRoom, user: WebhookUser } | UserUpdatedPayload // { event, room: CollabKitRoom, user: WebhookUser } | UserDeletedPayload; // { event, room: CollabKitRoom, user: WebhookUser } ``` ## WebhookDelivery A webhook delivery log entry. This type is defined on the server and not exported from `@collab-kit/utils`. ```typescript theme={null} interface WebhookDelivery { id: string; webhook_id: string; event: string; payload: string; // JSON-serialized webhook payload status: 'pending' | 'success' | 'failed'; attempts: number; last_attempt_at: string | null; next_retry_at: string | null; status_code: number | null; created_at: string; } ``` # Workflow Types Source: https://docs.collab-kit.com/types/workflows Types for workflow registrations and execution logs. ```typescript theme={null} import type { WorkflowRegistration, WorkflowExecution } from '@collab-kit/utils'; ``` ## WorkflowRegistration A workflow registration. ```typescript theme={null} interface WorkflowRegistration { id: string; organization_id: string; name: string; code: string; events: WebhookEventName[]; room_id: string | null; enabled: boolean; created_at: string; updated_at: string; } ``` ## WorkflowExecution A workflow execution log entry. ```typescript theme={null} interface WorkflowExecution { id: string; workflow_id: string; event: string; status: 'pending' | 'success' | 'failed'; result: string | null; duration_ms: number | null; created_at: string; } ```