Duvet API Documentation
Duvet is a Discord-like chat application with a REST API and WebSocket gateway for real-time events.
Base URL
https://api.duvet.chat Architecture
- duvet-api — REST API server (Go/Gin, PostgreSQL, Redis)
- duvet-gateway — WebSocket gateway for real-time events and WebRTC voice
Authentication
All API requests (except auth endpoints) require a Bearer token in the Authorization header:
Authorization: Bearer <access_token> Tokens are obtained via the /api/auth/login endpoint and refreshed via /api/auth/refresh. They last 24 hours; each refresh gives new ones for another 24 hours, up to 30 days after the login, when the user has to log in again.
Users also send their session token (session_token from login or refresh) in the X-Session-Token header. Login and refresh set it as the duvet_session_token cookie too, which the API only reads when there’s no header.
Bots send Authorization: Bot <bot_token> instead, with the token from Create Bot.
Rate Limiting
Rate-limited endpoints return these headers. When several limits apply to a request, they describe the one closest to running out.
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests per window |
X-RateLimit-Remaining | Remaining requests in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
X-RateLimit-Reset-After | Seconds until the window resets |
Limits apply per route and per user, or per IP address on the public endpoints below. Routes that act on a room, conversation or space count each one separately: acknowledging messages in two different rooms uses two limits. Those routes also have a ceiling across all rooms, conversations and spaces. A request only counts against a limit when it gets through all of them, so rejected requests don’t use up the ceiling.
| Endpoints | Limit |
|---|---|
| All authenticated endpoints together | 50 requests per second per user |
| Authenticated endpoints | 64 requests per minute per route (and per room, conversation or space when the route acts on one); routes that act on one also allow at most 640 per minute in total |
POST /api/auth/verify/send, /reset-password/send, /change-email/send | 3 per hour per account, on top of their per-IP limit below |
POST /api/auth/login, /reset-password, /reset-password/send, /forgot-password, /change-email, /change-email/send | 16 per minute per IP |
POST /api/auth/signin, /verify, /verify/send | 16 per hour per IP |
POST /api/auth/refresh, /logout | 32 per minute per IP |
GET /api/applications/:appId/interactions/next | 600 per minute per bot, instead of the per-route limit |
GET /api/proxy/video | 300 per minute per IP |
| Webhook execution | 30 per minute per webhook and IP |
GET /api/invites/:code | 60 per minute per IP |
Over the limit, the API responds 429 Too Many Requests with {"error": "too many requests, try again later"} and a Retry-After header (seconds). Wait that long before retrying; a 429 never means the session is invalid.
Two limits work per email address instead, whatever the IP:
- Failed logins: after 10
POST /api/auth/loginattempts for an address within 15 minutes without a successful one, logging in to it answers429with{"error": "too many failed login attempts, try again later"}(noRetry-After) until 15 minutes have passed since the first failure. A successful login clears the count. - Password reset emails:
POST /api/auth/forgot-passwordsends at most 3 emails per address per hour. Further requests still answer204, so the response doesn’t reveal whether an account exists, but send nothing.
Typing and recording notices have a limit of the same kind: POST /api/rooms/:roomId/typing and /recording, and their conversation versions, send their event at most once every 2 seconds per user and room or conversation, each kind on its own. Further requests in that time still answer 204, but send nothing, so a client can send them as it likes without flooding everyone else.
Request Size
A request body can be up to 32 MiB: one that says it is bigger is answered 413 with {"error": "the request body is over 32 MiB"}, and one sent without its length is cut off at the limit and fails to read. Uploads have smaller limits of their own, given with each endpoint.
Enums
Room Type
| Value | Name | Description |
|---|---|---|
| 1 | Text | Text channel |
| 2 | Voice | Voice channel |
| 3 | Category | Channel category (folder) |
User Flags (bitfield)
| Bit | Value | Name | Description |
|---|---|---|---|
| 0 | 1 << 0 | Staff | Duvet developer |
User Badges (bitfield)
| Bit | Value | Name | Description |
|---|---|---|---|
| 0 | 1 << 0 | Silly | Silly badge |
Permission Flags (bitfield)
Used by a role’s permissions and by a room overwrite’s allow_flags / deny_flags. A member’s base permissions are the bitwise OR of all their roles. In a room, overwrites for the roles the member holds are then applied in three steps, each removing its denied bits before adding its allowed bits: the @everyone overwrite (the @everyone role’s ID is the space ID), then the member’s other role overwrites combined, then the member’s own overwrite. The space owner and members with Administrator get every permission, including in rooms with overwrites; Administrator only works when granted by a role, not by an overwrite.
| Bit | Value | Name | Description |
|---|---|---|---|
| 0 | 1 << 0 | Administrator | Full administrative control over the space |
| 1 | 1 << 1 | ViewChannels | View and access rooms |
| 2 | 1 << 2 | SendMessages | Post messages in rooms |
| 3 | 1 << 3 | ManageMessages | Edit and delete messages in rooms |
| 4 | 1 << 4 | ManageChannels | Create, edit, and delete rooms |
| 5 | 1 << 5 | ChangeNickname | Change their nickname in the space |
| 6 | 1 << 6 | JoinVoice | Join voice rooms |
| 7 | 1 << 7 | SpeakInVoice | Speak while connected to a voice room |
| 8 | 1 << 8 | ManageRoles | Create, edit, and delete roles |
| 9 | 1 << 9 | ManageInvites | Revoke invite links in the space |
| 10 | 1 << 10 | CreateInvites | Create invite links to the space |
| 11 | 1 << 11 | ManageSpaceEmojis | Create, edit, and delete custom emojis |
| 12 | 1 << 12 | ManageSpaceBuckets | Manage storage buckets and quotas |
| 13 | 1 << 13 | ManageSpaceStickers | Create, edit, and delete stickers |
| 14 | 1 << 14 | ManageBots | Add, remove, and configure bot applications |
| 15 | 1 << 15 | ManageSpaceAttachments | Manage file attachments and storage |
| 16 | 1 << 16 | ManageSpaceWebhooks | Create, edit, and delete webhooks |
| 17 | 1 << 17 | PinMessages | Pin and unpin messages in rooms |
Values with bits outside this table are rejected with 400 and {"error": "permissions contain unknown flags"}.
Message Reference Type
| Value | Name |
|---|---|
| 0 | Reply |
| 1 | Forward |