API Reference

Complete reference for the Disqua REST API. Base URL: https://api.disqua.com/v1

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 + limit query params.

Auth

POST /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": "..." } }
POST /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
  }
}
POST /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_..." } }
POST /v1/auth/logout Auth required

Revoke the current session's refresh token. Pass { "all": true } to revoke all sessions.

Workspaces

MethodEndpointDescriptionScope
GET/v1/workspacesList workspaces the current user belongs to
POST/v1/workspacesCreate a new workspace
GET/v1/workspaces/:idGet workspace details
PATCH/v1/workspaces/:idUpdate workspace (name, slug, icon)admin
GET/v1/workspaces/:id/membersList workspace membersusers:read
DELETE/v1/workspaces/:id/members/:userIdRemove member from workspaceadmin
POST/v1/workspaces/:id/invitesCreate invite link or send email inviteadmin

Channels

MethodEndpointDescription
GET/v1/workspaces/:wId/channelsList channels. Query: type, cursor, limit
POST/v1/workspaces/:wId/channelsCreate channel. Body: name, type (public|private), topic
GET/v1/channels/:idGet channel details
PATCH/v1/channels/:idUpdate channel name, topic, description
DELETE/v1/channels/:idArchive channel (soft delete)
GET/v1/channels/:id/membersList channel members
POST/v1/channels/:id/membersAdd member(s) to channel. Body: { userIds: [...] }
DELETE/v1/channels/:id/members/:userIdRemove member from channel
GET/v1/channels/:id/pinsList pinned messages
POST/v1/channels/:id/pinsPin a message. Body: { messageId }

Messages

MethodEndpointDescription
GET/v1/channels/:id/messagesList messages. Query: cursor, limit (max 100), before, after
POST/v1/channels/:id/messagesSend message. Body: content (markdown), threadId?, attachmentIds?
GET/v1/messages/:idGet single message
PATCH/v1/messages/:idEdit message content (author only)
DELETE/v1/messages/:idDelete 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

MethodEndpointDescription
GET/v1/messages/:id/threadGet thread with all replies for a parent message
GET/v1/workspaces/:wId/threadsList 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

MethodEndpointDescription
POST/v1/messages/:id/reactionsAdd reaction. Body: { "emoji": "👍" }
DELETE/v1/messages/:id/reactions/:emojiRemove reaction (current user's reaction only)

Users

MethodEndpointDescription
GET/v1/users/meGet current user profile
PATCH/v1/users/meUpdate display name, avatar, bio
PATCH/v1/users/me/statusSet status. Body: { "status": "online|away|dnd|invisible" }
PATCH/v1/users/me/preferencesUpdate notification, theme, language preferences
GET/v1/workspaces/:wId/users/:idGet workspace member profile

Files

Disqua uses a two-step presigned URL upload flow — files never transit through the API server.

MethodEndpointDescription
POST/v1/files/presignGet a presigned upload URL. Returns uploadUrl + fileId
POST/v1/files/:id/confirmConfirm upload complete. Triggers processing (thumbnail, virus scan)
GET/v1/workspaces/:wId/filesList files in workspace. Query: type, channelId, cursor
DELETE/v1/files/:idDelete file (uploader or admin only)
File upload flow
// 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>"] }

Notifications

MethodEndpointDescription
GET/v1/notificationsList notifications for current user
POST/v1/notifications/mark-readMark notifications as read. Body: { ids: [...] } or { all: true }
POST/v1/notifications/push/subscribeRegister push subscription (Web Push / VAPID)