Server API reference
Your backend drives Komozu with the secret key: members, channels (one per event or group), members of private
channels, channel admins, pinned messages, moderation, reported messages. The @komozu/node SDK wraps every route below; this page
is the HTTP reference.
Authentication and limits
Section titled “Authentication and limits”Authorization: Bearer kz_sec_…- Missing or unknown key →
401 INVALID_SECRET_KEY. - Any request carrying an
Originheader (i.e. from a browser) →403 SECRET_KEY_IN_BROWSER. The secret key never goes to a browser or a mobile app. - Rate limit per project:
RATE_LIMIT_SERVER_PER_MINUTE(600 requests per minute by default). Over it →429 RATE_LIMIT_EXCEEDEDwithRateLimit-Limit,RateLimit-Remaining,RateLimit-ResetandRetry-After. - Allowed domains don’t apply to the server API.
- Errors are JSON
{ code, error, message }with a stablecode. Invalid input →400 VALIDATION_ERROR.
Identifiers
Section titled “Identifiers”- Members are identified by their external id: the user id in your own database (1–128 characters).
- Channels (
:channelsegment) by slug or UUID, or by theirexternalIdwhen the request carries?by=externalId(never guessed: an external id could look like another channel’s slug). With@komozu/node, pass{ externalId: 'evt_1102' }. - The slug is generated from the name at creation (
Tournament – Nov 2→tournament-nov-2, then-2,-3… if taken) and never changes.externalIdis optional, unique in the project and immutable.
Members
Section titled “Members”| Method & path | Body / query | Response | Errors |
|---|---|---|---|
PUT /v1/server/members/:externalId | { name (1–64), avatarUrl?: https:// URL | null } | 201 created / 200 updated, { member, token } | 400 |
PATCH /v1/server/members/:externalId | { name?, avatarUrl? } | 200 { member } | 400, 404 MEMBER_NOT_FOUND |
GET /v1/server/members/:externalId | — | 200 { member } | 404 |
GET /v1/server/members | ?limit=1..100 (50)&cursor=&search= | 200 { members, nextCursor } | 400 |
DELETE /v1/server/members/:externalId | ?deleteMessages=true|false (false) | 200 { success } | 404 |
POST /v1/server/members/:externalId/ban | { reason?: string ≤ 500 } | 201 { ban } (200 if already banned) | 404 |
DELETE /v1/server/members/:externalId/ban | — | 200 { success } (idempotent) | 404 member |
POST /v1/server/members/:externalId/revoke-tokens | — | 200 { token } (new token) | 404 MEMBER_NOT_FOUND |
member = { externalId, displayName, avatarUrl, createdAt, updatedAt, lastSeenAt, banned }. The member list is
sorted by external id; nextCursor is the last external id of the page. search matches the name or the id.
When PUT or PATCH changes the name or the avatar of an existing member, open chats of the project rename that
member’s messages right away (member.updated), without reloading.
token is the member’s token for the chat: it doesn’t expire, store it with the user and write it in your pages
(Identify your users). PUT returns a new valid token each time (earlier ones keep working
until you call revoke-tokens). revoke-tokens refuses every token issued before it (401 TOKEN_REVOKED in the
chat), disconnects the member’s open chats and returns a new token.
Channels
Section titled “Channels”| Method & path | Body / query | Response | Errors |
|---|---|---|---|
POST /v1/server/channels | { name (1–255), access?: 'members' | 'private' = 'members', externalId?, attachmentsEnabled?: boolean = true } | 201 { channel } (slug generated) | 400, 403 CHANNEL_LIMIT_EXCEEDED, 409 CHANNEL_EXTERNAL_ID_TAKEN |
GET /v1/server/channels | — | 200 { channels } | |
GET /v1/server/channels/:channel | ?by=externalId | 200 { channel } | 404 CHANNEL_NOT_FOUND |
PATCH /v1/server/channels/:channel | { name?, access?, attachmentsEnabled? } (slug immutable) | 200 { channel } | 400, 404 |
DELETE /v1/server/channels/:channel | — | 200 { success } | 404 |
GET /v1/server/channels/:channel/members | — | 200 { members } (each with its role) | 404 |
PUT /v1/server/channels/:channel/members | { memberIds: string[1..100] } | 200 { success } (idempotent) | 404 MEMBER_NOT_FOUND (all or nothing, lists the unknown ids), 404 channel |
DELETE /v1/server/channels/:channel/members | { memberIds: string[1..100] } | 200 { success } (idempotent) | 404 channel |
PUT /v1/server/channels/:channel/members/:externalId/role | { role: 'user' | 'admin' } | 200 { member } | 400, 404 MEMBER_NOT_FOUND, 404 channel |
GET /v1/server/channels/:channel/messages | ?limit=1..100 (50)&cursor= | 200 { messages, nextCursor } | 404 channel |
GET /v1/server/channels/:channel/pins | — | 200 { messages } (latest pinned first, 50 at most) | 404 channel |
channel = { id, slug, name, access, externalId, attachmentsEnabled, createdAt, updatedAt }. Access levels:
members (any member of the project) or private (only the members added to the channel). Reading and writing
always need a member token: there is no anonymous access.
attachmentsEnabled: false turns images and videos off in the channel (the chat hides the button; uploads are
refused with 403 ATTACHMENTS_DISABLED).
message = { id, channelId, content, authorType, member, owner, authorRole, attachments, pinnedAt, editedAt, createdAt }. authorType is member or owner (the project owner, posting from the dashboard; owner is
{ displayName }). member is null for a deleted member. authorRole is the author’s role in the channel: admin
(the owner included), user, or null for a deleted member. pinnedAt is null unless the message is pinned; editedAt is null unless its
author edited it (message.updated in the chat).
Channel admins
Section titled “Channel admins”A channel member is user (default) or admin. In the chat, an admin deletes any message, pins and unpins and
removes a member from a private channel, on that channel only. Banning from the whole project
stays with you (server API, dashboard).
- On a private channel,
adminadds the member to the channel if needed;userkeeps their access. - On a members channel, the role only adds rights, never access:
GET …/memberslists the admins only, anduserremoves the admin row. - Open chats pick the change up right away (
member.role_changed), without reconnecting.
Moderation
Section titled “Moderation”| Method & path | Body | Response | Errors |
|---|---|---|---|
DELETE /v1/server/messages/:messageId | — | 200 { success } | 404 MESSAGE_NOT_FOUND (scoped to the project) |
PUT /v1/server/messages/:messageId/pin | — | 200 { message } (idempotent) | 404 MESSAGE_NOT_FOUND, 409 PIN_LIMIT_EXCEEDED (50 per channel) |
DELETE /v1/server/messages/:messageId/pin | — | 200 { message } (idempotent) | 404 MESSAGE_NOT_FOUND |
Reports
Section titled “Reports”Members can report a message from the chat (Report button, optional reason: spam, harassment,
inappropriate, other). Reports wait here, and in the dashboard’s Reports page, until you act.
| Method & path | Body | Response | Errors |
|---|---|---|---|
GET /v1/server/reports | — | 200 { reports } (open reports grouped by message, most recently reported first, 100 messages at most) | |
POST /v1/server/reports/:messageId/dismiss | — | 200 { dismissed } (number of reports closed; the message stays) | 404 REPORT_NOT_FOUND (no open report for this message in the project) |
report = { message, channel: { id, slug, name }, reportCount, reasons, firstReportedAt, lastReportedAt }.
reasons counts the reports per reason ({ spam: 2 }; reports without a reason only count in reportCount). Nothing
identifies who reported: no member id, no IP address.
To act on a reported message: DELETE /v1/server/messages/:messageId (its reports go with it),
POST /v1/server/members/:externalId/ban for its author (then dismiss, or delete the message), or …/dismiss to keep
the message.
Side effects
Section titled “Side effects”- Deleting a member: their messages are kept and shown as “Deleted member”, or deleted with
deleteMessages=true(message.deletedis sent for each, files are purged). Their open chats are disconnected. - Banning a member: they can’t read or post in any channel of the project until unbanned; their open chats are
disconnected (
access.revoked). - Removing a member from a private channel: their open chat on that channel is disconnected.
- Changing a channel’s access / deleting a channel: every open chat of that channel re-checks its access (deleting removes messages, integrations, channel members and files).
- Deleting a message: it disappears live for everyone (pinned or not); its files and its reports are deleted.
- Pinning / unpinning: every reader sees it live (
message.pinned/message.unpinned). - Changing a role: open chats show or hide the admin actions and badges right away (
member.role_changed). - Reporting a message (chat): the project owner gets an email (one at most every 15 minutes per project). The same member reporting the same message again within 24 hours counts once. A reported message comes back after a dismiss only if someone reports it again.
Example: one channel per event
Section titled “Example: one channel per event”const channel = await komozu.channels.create({ name: 'Tournament – Nov 2', externalId: `evt_${event.id}` });Then, on the event page, @komozu/react shows it with
<KomozuChat clientId="kz_live_…" channel={{ externalId: 'evt_1102' }} />. Without React, create one integration per
channel in the dashboard.