ccs-chat-user-client
@central-code-station/ccs-chat-user-client makes the signed-in user’s own Chat calls from your web or mobile app. It lists conversations, reads and sends messages, edits and deletes them, reacts, marks read, signals typing and reports presence. It is promise-based, framework-agnostic and has no runtime dependencies.
An embedded Chat integration has three parts. Your backend creates conversations with ccs-chat-client. Your app reads and writes in them with this package, using the user’s token with scope chat:access. Live updates and push come from ccs-chat-notifications-client.
Install
Section titled “Install”The package is private on GitHub Packages. Point the scope at it in your .npmrc, then install. See SDKs overview for how to get access.
@central-code-station:registry=https://npm.pkg.github.com//npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}npm install @central-code-station/ccs-chat-user-clientGITHUB_PACKAGES_TOKEN is a GitHub token with read:packages that can read the organization’s packages. The package uses only the global fetch, so it runs in browsers and Node.js 20 or later. The Angular and React adapters need @angular/core and rxjs, or react. These are optional peer dependencies: install only what your app uses.
Construct
Section titled “Construct”You need the Chat API URL, the tenant id, the application id and a getAccessToken function. Chat accepts a user token only when its audience is chat-api and its scope contains chat:access. The library never signs a user in. Your backend swaps the user’s token for a Chat token and your app returns it from getAccessToken. See Get the user’s Chat token.
The token lasts at most 10 minutes and has no refresh token, so return a cached token and fetch a new one shortly before it expires. A token works only in your application’s context. Any other context answers 403 client_context_mismatch. To switch tenant or application, create a new client.
chat:accessimport { ChatUserClient, ChatUnprocessableError } from '@central-code-station/ccs-chat-user-client';import { getChatAccessToken } from './fe-guide-chat-token';
const chat = new ChatUserClient({ chatApiUrl: 'https://chat.centralcodestation.com/api/v1', tenantId: 'fitness', applicationId: 'fitness-app', getAccessToken: getChatAccessToken, // sync or async, called before every request});
export async function morning(conversationId: string): Promise<void> { const { capabilities } = await chat.capabilities(); console.log('can start chats:', capabilities.includes('conversations.create'));
const { items, next_cursor } = await chat.conversations.list({ limit: 30 }); // most recently active first console.log(items.length, next_cursor);
const page = await chat.messages.list(conversationId, { limit: 50 }); // newest first try { await chat.messages.send(conversationId, { body: 'See you at 9!' }); } catch (error) { if (error instanceof ChatUnprocessableError && error.code === 'conversation_archived') return; throw error; }
await chat.messages.markRead(conversationId, page.items[0]?.message_id); await chat.presence.heartbeat(); console.log(await chat.presence.get(['coach-ana', 'client-john']));}With ccs-chat-notifications-client on the same token, append each message.created event’s message to the open thread. It has the same ChatMessage shape as the items of messages.list.
Who may start a conversation
Section titled “Who may start a conversation”When your backend owns who may talk to whom, give your roles only the permissions they need inside existing conversations: conversations.read, messages.read, messages.send and messages.update. Leave out conversations.create, groups.create and users.lookup, because a permission applies to every holder of the role. See Grant permissions to roles.
Chat checks every call against the user’s permissions and membership. Types are the API’s snake_case wire format, the same as ccs-chat-client and the realtime events. See the generated reference for every type.
| Resource | Method | Calls | Needs |
|---|---|---|---|
me() |
Get my profile | Cached for the life of the client. | |
capabilities() |
Application capabilities | Returns { roles, capabilities }. |
|
conversations |
list({ type?, before?, limit? }) |
List conversations | Members’ conversations plus PUBLIC channels. |
get(id) |
Get conversation | ||
create(input) |
Create conversation | conversations.create, and groups.create or channels.create. |
|
update(id, patch) |
Update conversation | GROUP or CHANNEL owner or admin, or groups.manage or channels.manage. |
|
delete(id) |
Delete conversation | conversations.delete. |
|
listMembers(id, options?) |
List members | ||
addMember(id, userId) / removeMember(id, userId) |
Add member, Remove member | Per the GROUP and CHANNEL rules. | |
join(id) / leave(id) |
Add member, Remove member, for the user’s own id | join a PUBLIC channel needs conversations.read. leave is always allowed. |
|
messages |
list(id, options?) |
List messages | Newest first. |
send(id, input) |
Send a message | messages.send. |
|
edit(id, messageId, body) / delete(id, messageId) |
Edit, Delete | The author with messages.update, or a moderator with messages.delete. |
|
addReaction / removeReaction |
Add, Remove | Users only. | |
markRead(id, messageId?) / typing(id) |
Mark read, Signal typing | Users only. | |
users |
lookup(query) |
Look up users | users.lookup. |
presence |
heartbeat() / offline() / get(userIds) |
Heartbeat, Go offline, Presence | Tenant-wide. |
Lists return { items, next_cursor }. A page holds 50 items by default and 100 items at most.
Configuration reference
Section titled “Configuration reference”ChatUserClientOptions:
| Field | Required | Description |
|---|---|---|
chatApiUrl |
yes | The Chat API base URL, such as https://chat.centralcodestation.com/api/v1. |
tenantId |
yes | The tenant the user is acting in. |
applicationId |
yes | Your tenant application, the one whose client issued the token. |
getAccessToken |
yes | Returns the user’s Chat token, sync or async. Called before every request. |
fetch |
no | Override fetch for tests or runtimes without one. |
Errors
Section titled “Errors”Every failure is a ChatApiError with status, code and body. The subclasses are:
| Class | Status | Typical codes |
|---|---|---|
ChatUnauthorizedError |
401 | invalid_token, or no_access_token when getAccessToken returned nothing. |
ChatForbiddenError |
403 | missing_capability, not_conversation_member, client_context_mismatch. |
ChatNotFoundError |
404 | not_found. |
ChatUnprocessableError |
422 | conversation_archived, invalid_reply. |
ChatRateLimitedError |
429 | rate_limited, with retryAfterSeconds. |
See Troubleshooting.
Module formats
Section titled “Module formats”The package ships ES modules and CommonJS with type declarations for each. import loads dist/esm, require() loads dist/cjs. It works with any bundler and with the node16, nodenext, bundler and legacy node module resolution. Load it one way per app: the two builds are separate copies, so instanceof checks do not cross them.