Team chat API
With team chat, your staff write to each other directly or in groups — with files, mentions and a reference to a customer conversation. The dashboard and our app use exactly the endpoints on this page. Base URL as for the rest of the API: https://api.silentchat.de/api
Access
- Included from the first paid plan. Without team chat in the plan, every endpoint responds with 403 ERR_FEATURE_NOT_AVAILABLE.
- Only with a signed-in user’s access token (Authorization: Bearer …). API keys are rejected, because team chat always belongs to a person.
- Lite seats can read and set their read status, but cannot write, create or manage.
- Only members can see a chat — administrators cannot read other people’s direct messages either. For everyone else a chat responds as if it did not exist (404).
Endpoints
All paths relative to the base URL. People appear in responses only as { id, display_name, avatar_url }.
| Method | Path | Description |
|---|---|---|
GET | /v1/team-chat/channels | Your chats, most recently active first, with unread count, last message and, for direct chats, the other person (peer). Cursor-based. |
POST | /v1/team-chat/channels | Create a group: { name, member_ids[] }. At most 200 members. |
POST | /v1/team-chat/direct | Get or create a direct chat with a person: { user_id }. There is exactly one per pair. |
GET | /v1/team-chat/channels/:id | A chat with all members and can_manage (may rename, archive, manage members). |
PATCH | /v1/team-chat/channels/:id | Rename or archive a group: { name?, is_archived? }. Group owner and administrators only. |
POST | /v1/team-chat/channels/:id/members | Add members: { user_ids[] }. Only people from the same account. |
DELETE | /v1/team-chat/channels/:id/members/:user_id | Remove a member or leave the group yourself. |
GET | /v1/team-chat/channels/:id/messages | History, newest first, cursor-based (limit default 25, at most 100). |
POST | /v1/team-chat/channels/:id/messages | Send a message: { content, conversation_id?, file_ids? }. |
POST | /v1/team-chat/channels/:id/read | Read up to a message: { message_id }. |
PATCH | /v1/team-chat/channels/:id/membership | Mute for yourself only: { is_muted }. Muted means no notification for direct messages and no count in /unread — mentions still notify you. Allowed for lite seats too. |
PATCH | /v1/team-chat/messages/:id | Edit your own message: { content } — up to 15 minutes after sending. |
DELETE | /v1/team-chat/messages/:id | Delete your own message. A placeholder remains, attachments are removed. |
GET | /v1/team-chat/unread | Total of all unread messages: { unread_count } — for the sidebar and app badge. |
GET | /v1/team-chat/files/:id | Short-lived link to an attachment (members only). With ?download=true always as a download. |
Paging
Lists return data and pagination. While hasMore is true, fetch the next page with the cursor from the last response. A tampered cursor results in 400 ERR_INVALID_CURSOR.
GET /api/v1/team-chat/channels/:id/messages?limit=50&cursor=<token>
200 → {
"data": [ { "id": "…", "content": "…", "created_at": "2026-09-17T09:04:00Z", … } ],
"pagination": { "cursor": "eyJhdCI6…", "hasMore": true }
}Messages
A message needs text, attachments or a reference to a customer conversation — at least one of them. Text is at most 10,000 characters long.
POST /api/v1/team-chat/channels/:id/messages
{
"content": "<@5f0c6b1e-3a1d-4c55-9a1e-2b7d0c1e4f10> can you take this one?",
"conversation_id": "…", // optional
"file_ids": ["…"] // optional, max. 10
}
201 → { "message": { "id": "…", "mentions": ["5f0c6b1e-…"], "attachments": [ … ], … } }- Mentions appear in the text as <@user-id>. Only chat members count; the response lists them in mentions. Display the person’s name instead.
- conversation_id refers to a customer conversation of your account; a foreign one results in 400 ERR_INVALID_CONVERSATION.
- Editing is possible for 15 minutes, deleting at any time. Deleted messages return removed_at and no content.
- Text is never interpreted as HTML. Display it as plain text.
Attachments
- Request an upload: POST /api/v1/files/upload with file name, type and size — without conversation_id.
- Send the file via PUT to the returned upload_url.
- Pass the file_ids when sending (at most 10). Only now do we check the actual content against the declared type; at most 20 MB per file.
- To display or download, get a link via GET /api/v1/team-chat/files/:id. It is valid for just under an hour (expires_at).
POST /api/v1/files/upload
{ "filename": "screenshot.png", "mime_type": "image/png", "size": 48213 }
201 → { "file": { "id": "…" }, "upload_url": "https://…" }
PUT <upload_url> (Content-Type: image/png, body = file)
POST /api/v1/team-chat/channels/:id/messages
{ "content": "", "file_ids": ["…"] }
GET /api/v1/team-chat/files/:file_id?download=true
200 → { "file": { "url": "https://…", "expires_at": "…", "filename": "…", "mime_type": "…", "size": 48213 } }The general endpoint /api/v1/files/:id/download does not serve team attachments. Uploaded files that are not attached to a message within 24 hours are cleaned up.
Real time
Via the dashboard’s WebSocket connection (user token) you join the room team:<id> — members only. New chats and unread counts also arrive via your personal user room.
| Event | Room | Description |
|---|---|---|
team.message.new | team:<id> | New message, complete as in the API. |
team.message.updated | team:<id> | Message edited. |
team.message.removed | team:<id> | Message deleted: { channel_id, message_id }. |
team.read | team:<id>, user room | Someone has read: { channel_id, user_id, message_id }. |
team.channel.updated | team:<id> | Name, archive state or members changed. With removed: true to the person who was removed. |
team.message.new | user room | Short form { channel_id, message_id } for chats that are not open — a cue to reload the list and counters. |
team.channel.updated | user room | Muted or unmuted on another device: { channel_id, is_muted } — reload the list and the unread count. |
team.channel.added | user room | You were added to a chat: { channel_id }. |
Notifications
Direct messages and mentions in groups trigger the event team.message — as an entry in the bell, as a toast and as a browser push, depending on the person’s settings (GET/PUT /api/v1/me/notification-preferences). The metadata contains channel_id and message_id, and for mentions mention: "true".
Error codes
| Status | Code | Description |
|---|---|---|
| 403 | ERR_USER_TOKEN_REQUIRED | Called with an API key instead of a user token. |
| 403 | ERR_FEATURE_NOT_AVAILABLE | The plan does not include team chat. |
| 403 | ERR_LITE_SEAT_RESTRICTED | A lite seat tried to write. |
| 404 | ERR_NOT_CHANNEL_MEMBER | The chat does not exist, or you are not a member. |
| 400 | ERR_USER_NOT_IN_TENANT | The person does not belong to your account. |
| 400 | ERR_INVALID_CONVERSATION | The customer conversation does not belong to your account. |
| 400 | ERR_INVALID_ATTACHMENT | The file does not exist, belongs to someone else, is already attached to a message or was never uploaded. |
| 400 | ERR_FILE_TOO_LARGE | The file is larger than 20 MB. |
| 400 | ERR_FILE_MIME_MISMATCH | The content does not match the declared type; the file was removed. |
| 400 | ERR_INVALID_CURSOR | The cursor is invalid. |
| 409 | ERR_CHANNEL_ARCHIVED | The group is archived. |
| 409 | ERR_MESSAGE_NOT_EDITABLE | The message is older than 15 minutes or was deleted. |
All errors follow the usual format { "error": …, "code": … } — see error codes.