Channels
Conversation containers — direct (1-on-1) and group (many members).
A channel is one conversation. Messages live inside channels. Users join channels via memberships. Everything else (typing, presence) is scoped to channels too.
Cariosan has two channel types and is opinionated about keeping it that way.
Direct channels
Exactly two members. Meant for DMs between a buyer and a seller, a patient and a doctor, a rider and a driver. The server can auto-generate a stable external_id or you can supply your own.
Server-side dedup
Calling createChannel a second time with the same two users (in either order) returns the existing channel, not a new one. The chat with Alice is always one thread.
Group channels
Any number of members. Use them for support rooms, project threads, anything not strictly 1-on-1. Group channels require an explicit external_id so your backend can address them later.
Group channels can grow and shrink over their lifetime:
Direct channels are immutable
Attempting to add a third member returns ErrValidation. Convert the conversation to a group channel by creating a new one if you need more participants.
Group avatars
Group channels can carry an optional avatar_url (an icon). Set it when you create the channel, or change it later. Direct channels have no avatar — clients render the other participant instead.
Roles & group admins
Every group member has a role: admin or member (direct channels have neither). Pass created_by on create — that user is added as a member (if missing) and made the admin; everyone else starts as a member.
Management follows a hybrid model:
- Your backend (API key) is god-mode. It created the channel and can rename it, change the avatar, and add/remove members with no role checks — exactly what you want for trusted server-side automation (
PATCH /v1/channels/:id,POST/DELETE /v1/channels/:id/members). - An admin can also manage the group from the app with their user JWT. Cariosan enforces the admin role on these client routes (
/v1/me/channels/:id/...); a non-admin member gets403 FORBIDDEN.
Leaving a group
Removing yourself (channel.removeMember(yourExternalId)) is allowed for any member regardless of role — that's "leave group". Removing someone else still requires admin. The last admin can't be demoted, so a group can't be orphaned.
Membership enforcement
Every client-JWT read (GET /v1/me/channels, GET /v1/channels/:id/messages, GET /v1/channels/:id/presence) filters by membership automatically. Non-members get 403 NOT_A_MEMBER.
Server-side bypass
The workspace API key/secret pair bypasses membership checks. Your backend can read or mutate anything in the workspace, which is what you want for moderation and audit tools. Use the server-side key only on trusted backends — never embed it in client apps.
Deleting a channel
No soft delete
Deletion cascades to memberships and messages, triggers a channel.deleted webhook, and is irrecoverable. If you need a "hide from feed" UX without losing data, store a deleted_at flag in your own DB and filter on read instead.
Tip
Subscribe to channel.deleted webhooks to keep client caches in sync.
Was this page helpful?