Duvet DOCS
GitHub

Conversations

All conversation endpoints are human-only and require authentication.

List Conversations

GET /api/users/@me/conversations

Returns all conversations the authenticated user is a member of, the one with the latest message or change first.

Returns: Array of Conversation objects.

Create Conversation

POST /api/users/@me/conversations

Create a new conversation. Type 1 is a DM (requires user_id), type 2 is a group conversation (requires user_ids and optional name).

JSON Body

FieldTypeDescription
type integerConversation type: 1 = DM, 2 = group
user_id ?stringUser ID for DM conversations (type 1)
user_ids ?string[]Array of user IDs for group conversations (type 2)
name ?stringGroup conversation name (type 2 only)

Returns: Conversation object with members.

Update Conversation

PATCH /api/users/@me/conversations/{conversationId}

Update a conversation's name. Only applicable to group conversations.

JSON Body

FieldTypeDescription
name ?stringNew conversation name

Returns: Conversation object.


Members

Add Member

POST /api/users/@me/conversations/{conversationId}/members

Add a friend to a group conversation. Only its owner can, and only while they're in it.

JSON Body

FieldTypeDescription
user_id stringID of the user to add

Returns: MemberWithUser object.

Errors: 403 when the caller isn't a member or isn't the owner, or isn't friends with the user.

Remove Member

DELETE /api/users/@me/conversations/{conversationId}/members/{userId}

Remove a user from a group conversation. The owner can remove anyone; any member can remove themselves (userId or @me) to leave.

When the owner leaves and someone stays, the member who joined first becomes the owner, and every member gets a CONVERSATION_UPDATE with the new owner_id.

Returns: 204 No Content


Messages

Get Messages

GET /api/users/@me/conversations/{conversationId}/messages

Retrieve paginated message history for a conversation.

Query Parameters

FieldTypeDescription
count ?integerNumber of messages to return
before ?uuidReturn messages before this message ID
after ?uuidReturn messages after this message ID

Returns: Array of MessageResponse objects.

Get Messages After

GET /api/users/@me/conversations/{conversationId}/messages/after

Retrieve messages after a specific point in a conversation. Used for catching up on missed messages.

Returns: Array of MessageResponse objects.

Search Messages

GET /api/users/@me/conversations/{conversationId}/messages/search

Search the conversation's messages, newest first. It takes the query parameters of a space's message search but room_id, and its words and filters work the same way.

Returns: MessageSearchResponse object.

Errors: 400 for a search without text or filters, a room_id, an unknown has value or an ID that isn't a UUID; 403 when the user isn't in the conversation; 404 when it doesn't exist; 503 when the search takes over 5 seconds.

Get Message

GET /api/users/@me/conversations/{conversationId}/messages/{messageId}

Retrieve a specific message in a conversation.

Returns: MessageResponse object.

Send Message

POST /api/users/@me/conversations/{conversationId}/messages

Send a message in a conversation. Supports multipart/form-data for file attachments.

JSON Body

FieldTypeDescription
content ?stringMessage text content
nonce stringClient-generated nonce for deduplication
type integerMessage type: 0 (a message) or 1 (a voice note); 2, a pin notice, is the API's only, and anything else is a 400
message_reference ?MessageReferenceThe message this one replies to: one of this conversation's (type 0, the only one)
embeds ?Embed[]Array of embed objects
sticker_attachment_id ?stringThe attachment_id of a sticker of a space you're in, to send it

A message is sent with all its files or not at all: if one fails to upload, the message and the files already uploaded are deleted and the request fails.

Returns: the created message, with the same fields as the DM_MESSAGE_CREATE event.

Errors: 400 with invalid message_reference: ... for a type other than 0 or a message_id that isn't a UUID; 400 with invalid sticker_attachment_id when it isn't a UUID, or that sticker can't be sent here when it isn't the attachment_id of a sticker of a space you're in; 404 with referenced message not found when it isn't a message of this conversation; 413 for a file over your upload limit or the storage bucket's, or with no room left for it.

Update Message

PATCH /api/users/@me/conversations/{conversationId}/messages/{messageId}

Update the content of a conversation message. Only the author can edit.

JSON Body

FieldTypeDescription
content stringNew message content

Returns: the updated message, with the same fields as the DM_MESSAGE_UPDATE event (no attachments or reactions).

Errors: 400 for a pin notice, which can't be edited.

Delete Message

DELETE /api/users/@me/conversations/{conversationId}/messages/{messageId}

Delete a message from a conversation. Only its author can, except for a pin notice (type 2): in a group its owner deletes it, and in a DM either member.

Returns: 204 No Content


Pinned Messages

Every member of a conversation can pin and unpin its messages, 250 at most. Pinning posts the conversation’s pin notice, and pins and unpins reach the members as DM_MESSAGE_PIN_UPDATE.

Get Pinned Messages

GET /api/users/@me/conversations/{conversationId}/pins

List the conversation's pinned messages, the last pinned first.

Returns: Array of MessageResponse objects.

Errors: 403 when the user isn't in the conversation; 404 when it doesn't exist.

Pin Message

PUT /api/users/@me/conversations/{conversationId}/pins/{messageId}

Pin a message of the conversation, which posts the conversation's pin notice for it. Pinning a pinned message changes nothing and posts no second notice.

Returns: 204 No Content

Errors: 400 for a pin notice, or when the conversation has 250 pinned messages already; 403 when the user isn't in the conversation; 404 for a message that isn't in it.

Unpin Message

DELETE /api/users/@me/conversations/{conversationId}/pins/{messageId}

Unpin a message of the conversation. Unpinning a message that isn't pinned changes nothing.

Returns: 204 No Content

Errors: 403 when the user isn't in the conversation; 404 for a message that isn't in it.


Typing & Read State

Send Typing Indicator

POST /api/users/@me/conversations/{conversationId}/typing

Notify the conversation's other members that the authenticated user is typing (DM_TYPING). Each user's notices go out at most once every 2 seconds per conversation: extra ones are still answered 204, and dropped.

Returns: 204 No Content

Send Recording Indicator

POST /api/users/@me/conversations/{conversationId}/recording

Notify the conversation's other members that the authenticated user is recording a voice message (DM_RECORDING). Members only, with its own limit: at most once every 2 seconds per conversation, extra ones answered 204 and dropped.

Returns: 204 No Content

Acknowledge Conversation

POST /api/users/@me/conversations/{conversationId}/ack

Mark the conversation as read for the authenticated user. The read state only moves forward: an older message ID than the one stored leaves it as it is, so acks arriving out of order are harmless. A message_id that isn't a UUID is rejected (400).

JSON Body

FieldTypeDescription
message_id uuidNewest message read in the conversation

Returns: 204 No Content


Reactions

Add Reaction

PUT /api/users/@me/conversations/{conversationId}/messages/{messageId}/reactions

Add an emoji reaction to a conversation message.

JSON Body

FieldTypeDescription
emoji_name stringUnicode emoji character or custom emoji UUID string
emoji_id ?uuidCustom emoji attachment ID (for custom emojis only)

Returns: 204 No Content

Errors: 404 when the message isn't one of this conversation's.

Remove Reaction

DELETE /api/users/@me/conversations/{conversationId}/messages/{messageId}/reactions/{emojiName}

Remove your emoji reaction from a conversation message.

Returns: 204 No Content

Errors: 404 when the message isn't one of this conversation's.