Conventions
- All requests and responses are JSON unless noted.
- Path parameters shown as
:paramName. - Query parameters shown in the URL as
?key=value. - All timestamps are ISO 8601 in UTC:
2026-03-18T09:45:00.000Z. - IDs are
CHAR(36)UUIDs. - Pagination is cursor-based via
cursor+limitquery params.
Auth
/v1/auth/register
Register a new user account. Sends a verification email.
Body
{
"email": "jane@example.com", // required
"password": "min8chars", // required, min 8 chars
"username": "janedoe", // required, unique, 3-30 chars
"displayName": "Jane Doe" // required
}Response 201
{ "ok": true, "data": { "userId": "..." } }/v1/auth/login
Authenticate with email + password. Returns access + refresh tokens.
// Body
{ "email": "jane@example.com", "password": "..." }
// Response 200
{
"ok": true,
"data": {
"accessToken": "dqt_...", // expires in 15 min
"refreshToken": "dqr_..." // expires in 7 days
}
}/v1/auth/refresh
Exchange a refresh token for a new access token. Refresh tokens are rotated on each use.
// Body
{ "refreshToken": "dqr_..." }
// Response 200
{ "ok": true, "data": { "accessToken": "dqt_...", "refreshToken": "dqr_..." } }/v1/auth/logout
Auth required
Revoke the current session's refresh token. Pass { "all": true } to revoke all sessions.
Workspaces
| Method | Endpoint | Description | Scope |
|---|---|---|---|
| GET | /v1/workspaces | List workspaces the current user belongs to | — |
| POST | /v1/workspaces | Create a new workspace | — |
| GET | /v1/workspaces/:id | Get workspace details | — |
| PATCH | /v1/workspaces/:id | Update workspace (name, slug, icon) | admin |
| GET | /v1/workspaces/:id/members | List workspace members | users:read |
| DELETE | /v1/workspaces/:id/members/:userId | Remove member from workspace | admin |
| POST | /v1/workspaces/:id/invites | Create invite link or send email invite | admin |
Channels
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/workspaces/:wId/channels | List channels. Query: type, cursor, limit |
| POST | /v1/workspaces/:wId/channels | Create channel. Body: name, type (public|private), topic |
| GET | /v1/channels/:id | Get channel details |
| PATCH | /v1/channels/:id | Update channel name, topic, description |
| DELETE | /v1/channels/:id | Archive channel (soft delete) |
| GET | /v1/channels/:id/members | List channel members |
| POST | /v1/channels/:id/members | Add member(s) to channel. Body: { userIds: [...] } |
| DELETE | /v1/channels/:id/members/:userId | Remove member from channel |
| GET | /v1/channels/:id/pins | List pinned messages |
| POST | /v1/channels/:id/pins | Pin a message. Body: { messageId } |
Messages
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/channels/:id/messages | List messages. Query: cursor, limit (max 100), before, after |
| POST | /v1/channels/:id/messages | Send message. Body: content (markdown), threadId?, attachmentIds? |
| GET | /v1/messages/:id | Get single message |
| PATCH | /v1/messages/:id | Edit message content (author only) |
| DELETE | /v1/messages/:id | Delete message (author or admin) |
Message object
{
"id": "01HQ8B...",
"channelId": "01HQ7A...",
"workspaceId": "01HQ6Z...",
"authorId": "01HQ7Z...",
"content": "Hello **world**!",
"contentHtml": "<p>Hello <strong>world</strong>!</p>",
"type": "message", // message | system | bot
"threadId": null, // UUID if this is a thread reply
"replyCount": 3,
"reactions": [
{ "emoji": "👍", "count": 2, "userIds": ["...", "..."] }
],
"attachments": [],
"mentions": [],
"editedAt": null,
"createdAt": "2026-03-18T09:45:00.000Z"
}
Threads
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/messages/:id/thread | Get thread with all replies for a parent message |
| GET | /v1/workspaces/:wId/threads | List threads the current user participates in |
To reply in a thread, use POST /v1/channels/:id/messages with threadId set to the parent message ID.
Reactions
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/messages/:id/reactions | Add reaction. Body: { "emoji": "👍" } |
| DELETE | /v1/messages/:id/reactions/:emoji | Remove reaction (current user's reaction only) |
Users
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/users/me | Get current user profile |
| PATCH | /v1/users/me | Update display name, avatar, bio |
| PATCH | /v1/users/me/status | Set status. Body: { "status": "online|away|dnd|invisible" } |
| PATCH | /v1/users/me/preferences | Update notification, theme, language preferences |
| GET | /v1/workspaces/:wId/users/:id | Get workspace member profile |
Files
Disqua uses a two-step presigned URL upload flow — files never transit through the API server.
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/files/presign | Get a presigned upload URL. Returns uploadUrl + fileId |
| POST | /v1/files/:id/confirm | Confirm upload complete. Triggers processing (thumbnail, virus scan) |
| GET | /v1/workspaces/:wId/files | List files in workspace. Query: type, channelId, cursor |
| DELETE | /v1/files/:id | Delete file (uploader or admin only) |
// 1. Get presigned URL
POST /v1/files/presign
{ "filename": "screenshot.png", "contentType": "image/png", "size": 204800 }
// → { "fileId": "...", "uploadUrl": "https://...", "expiresAt": "..." }
// 2. PUT file directly to uploadUrl (no auth header needed)
PUT <uploadUrl>
Content-Type: image/png
<binary data>
// 3. Confirm upload
POST /v1/files/:fileId/confirm
// → { "ok": true, "data": { "id": "...", "url": "https://cdn.disqua.com/..." } }
// 4. Use fileId in message
POST /v1/channels/:id/messages
{ "content": "Here's the screenshot", "attachmentIds": ["<fileId>"] }
Search
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/workspaces/:wId/search | Search messages, files, users. Query: q, type, channelId, from, before, after |
Free plan: search last 10,000 messages. Pro+: unlimited search history. Results include highlighted contentHtml with match terms wrapped in <mark> tags.
Notifications
| Method | Endpoint | Description |
|---|---|---|
| GET | /v1/notifications | List notifications for current user |
| POST | /v1/notifications/mark-read | Mark notifications as read. Body: { ids: [...] } or { all: true } |
| POST | /v1/notifications/push/subscribe | Register push subscription (Web Push / VAPID) |