Private channels¶
A private channel is a conversation outside any guild. It is either a direct message (DM) between two accounts or a group DM with its own participant set. Both are channel objects; the fields specific to private channels are listed below.
List private channels and Create private channel accept a user session (Authorization: Bearer <token>) or a bot token (Authorization: Bot <token>). Every other route on this page is user-only, and a valid bot token is refused there with 403 ACCESS_DENIED.
Open state is per participant
A direct message channel exists once for a pair of accounts, and each participant holds its own open state. Closing one side leaves the channel and its messages in place, so a later Create private channel request for the same pair returns that stored channel.
Private channel object¶
A private channel is returned in the same channel shape the rest of the API uses. The members that matter for a DM or group DM are:
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The channel ID |
| type | string | "DM" or "GROUP_DM" in HTTP responses. The Gateway Channel Create payload carries the numeric code here instead |
| channel_type | integer | The numeric channel type: 1 for a DM, 3 for a group DM |
| name | string | The group DM name, or an empty string for a DM and for an unnamed group DM |
| recipients | array of recipient objects | Every participant, including the caller, ordered by ID |
| owner_id? | snowflake | The group DM owner. Absent on a DM |
| icon? | string | The group DM icon hash. Absent when none is set |
| last_message_id? | snowflake | The ID of the latest message. Absent when the channel has no messages |
| last_message? | last message object | A summary of the latest message. Absent when the channel has no messages or the latest one was deleted |
| last_message_at? | ISO8601 timestamp | The time of the latest message, derived from its ID |
| last_pin_timestamp | ?ISO8601 timestamp | The time of the latest pin, or null |
| nicks? | map[snowflake, string] | Group DM nicknames keyed by user ID. Present only when at least one is set |
| created_at | ISO8601 timestamp | The creation time, derived from the channel ID |
The object also carries the guild-channel members of the shared channel shape with fixed values (guild_id absent, server_id empty, topic and parent_id null, is_nsfw false, and so on). TEBHOS has no age-restricted channels, so is_nsfw is always false.
Example¶
{
"id": "1501314428688998182",
"name": "",
"type": "DM",
"channel_type": 1,
"recipients": [
{ "id": "1501300000000000001", "name": "Aria", "image_url": null },
{ "id": "1501300000000000002", "name": "deniz", "image_url": "3f1c0a9d2b7e4c55" }
],
"last_message_id": "1501320000000000000",
"last_message": {
"id": "1501320000000000000",
"content": "see you tomorrow",
"sender_id": "1501300000000000002",
"created_at": "2026-09-20T18:04:11+00:00",
"deleted": false
},
"last_message_at": "2026-09-20T18:04:11+00:00",
"last_pin_timestamp": null,
"created_at": "2026-09-01T10:00:00+00:00",
"locked": false,
"order": 0,
"category": "",
"category_id": null,
"parent_id": null,
"topic": null,
"slow_mode": 0,
"is_private": false,
"is_nsfw": false,
"bitrate": 0,
"user_limit": 0,
"server_id": "",
"permission_overrides": []
}
Private channel recipient object¶
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The participant's user ID |
| name | string | The participant's global_name, or username when no global name is set. Empty when the account cannot be resolved |
| image_url | ?string | The participant's avatar hash, or null. Build the image URL from the hash as described for user objects |
Last message object¶
A compact summary of one message, used by last_message and by Preload private channel messages.
Structure¶
| Field | Type | Description |
|---|---|---|
| id | snowflake | The message ID |
| content | string | The message text, or an empty string |
| sender_id | snowflake | The author's user ID |
| created_at | ISO8601 timestamp | The time the message was sent, derived from its ID |
| deleted | boolean | Always false; a deleted message is not returned at all |
Preloaded messages object¶
The response of both preload operations. It has no fixed field set: each property name is one requested channel ID.
Structure¶
| Field | Type | Description |
|---|---|---|
| {channel_id} | ?last message object | The latest message in the channel, or null |
Every requested ID gets a property. The value is null when the ID is not a number, the channel does not exist, it is not a DM or group DM, the caller is not a participant, the channel has no messages, or the latest message was deleted. A repeated ID collapses into one property.
List private channels¶
GET /users/@me/channels
Returns the caller's open DM and group DM channels.
A closed DM is absent even though the channel and its history still exist. The list is ordered by latest activity, newest first (by last_message_id, or by channel ID for a channel with no messages). It is not paginated.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | object | The open private channels were returned |
The body wraps the list in an items member:
| Field | Type | Description |
|---|---|---|
| items | array of private channel objects | The caller's open DMs and group DMs |
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:channels bucket, shared with Create private channel, Pin private channel, and Unpin private channel.
Create private channel¶
POST /users/@me/channels
Opens a DM with one account, or creates a group DM. Returns 200 with the created or reopened channel in the compact form below (not the full private channel object).
The number of IDs in recipient_ids selects the channel type. Exactly one ID opens a DM. Any other count, including zero, creates a group DM: an empty array creates a group DM whose only participant is the caller, and more participants can be added later through Add group DM recipient.
JSON body¶
| Field | Type | Description |
|---|---|---|
| recipient_id? | snowflake string | Opens a DM with this account. Send either recipient_id or recipient_ids, not both (400 INVALID_FORM_BODY / INVALID_FORMAT otherwise) |
| recipient_ids? | array of snowflake strings | The other participants, excluding the caller (at most 49) |
| name? | string | The group DM name. Trimmed; an empty value leaves the group unnamed. Ignored for a DM |
Admission¶
Common rules:
- An ID that is not a number returns 400
BAD_REQUEST. - More than 49 recipients returns 400
BAD_REQUEST. A group DM therefore holds at most 50 participants including the caller. - The caller's own ID in
recipient_idsreturns 400BAD_REQUEST.
Direct message. A friend can always be messaged. Otherwise the two accounts must share at least one guild, and the direct message restrictions both accounts set in user settings (including per-guild restrictions) must allow it. A refusal returns 400 CANNOT_SEND_MESSAGES_TO_USER. An ID that names no account fails the same check.
Group DM. Each recipient's group DM add permission setting in user settings is checked against the caller. A friend of the recipient always passes unless the recipient allows nobody; otherwise the setting decides whether friends of friends or members of a shared guild are admitted. The first recipient that rejects the caller fails the whole request with 403 MISSING_ACCESS. No friendship is required when the recipient's setting admits the caller.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | channel object (see below) | The DM or group DM was created, or an existing DM was reopened |
| 400 | error response | The body is invalid, the recipient count or set is invalid, or the DM is refused with CANNOT_SEND_MESSAGES_TO_USER |
| 403 | error response | A group DM recipient's settings reject the caller (MISSING_ACCESS) |
The channel object has id, numeric type (1 = DM, 3 = group DM), last_message_id and last_pin_timestamp (each null when unset) and recipients, the other participants as partial user objects (id, username, discriminator, global_name, avatar, ...; omitted when there is nobody else). A group DM adds name (when set), icon, owner_id and nicks (when any nickname is set).
Side effects¶
Opening a DM, new or existing, opens it for the caller only and emits Channel Create to the caller. The other participant's side stays closed until they open it themselves or until a message is sent in the channel, which opens it for them.
Creating a group DM makes the caller its owner, adds the complete participant set, and emits Channel Create to every participant. It then creates one RECIPIENT_ADD system message authored by the caller for each other recipient and emits Message Create for each. Initial creation emits no Channel Recipient Add, which is reserved for a recipient added later. A group DM created from an empty array produces no system message.
Not supported on TEBHOS
Group DM creation does not use a CAPTCHA, and there is no per-account cap on the number of open group DMs.
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:channels bucket, shared with List private channels, Pin private channel, and Unpin private channel.
A request whose recipient_ids does not hold exactly one ID also draws on the user:group_dm:create bucket: 10 requests per hour for each authenticated user.
Close private channel¶
DELETE /users/@me/channels/{channel_id}
Closes a DM or group DM for the caller only. Returns 204 with an empty body.
The channel, its messages, and the other participants are not touched, and closing a group DM does not leave it. The channel disappears from the caller's List private channels until the caller opens it again with Create private channel, or, for a DM, until a new message is sent in it. To leave a group DM, remove yourself with Remove group DM recipient.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the DM or group DM |
Response¶
| Status | Body | Condition |
|---|---|---|
| 204 | empty | The channel was closed for the caller |
| 400 | error response | channel_id is not a number |
| 404 | error response | The channel does not exist or the caller is not a participant (NOT_FOUND) |
Side effects¶
Channel Delete reaches the caller's own sessions with id, channel_id, and the numeric type, so other devices drop the channel from their list. No other participant receives a Dispatch.
Preload private channel messages¶
POST /users/@me/preload-messages
Returns the preloaded messages object holding the latest message of each requested private channel.
TEBHOS resolves each requested channel independently and reports an unknown channel, a channel the caller does not participate in, or a missing message as null for that channel. The request still answers 200, so a channel the caller cannot see is indistinguishable from one whose history is empty.
JSON body¶
| Field | Type | Description |
|---|---|---|
| channels | array of snowflake strings | The private channels to preload (at most 100) |
channels is required. An empty array returns an empty object, and more than 100 elements returns 400 BAD_REQUEST before any channel is read.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | preloaded messages object | The requested channels were resolved |
| 400 | error response | channels is missing or holds more than 100 elements |
Side effects¶
This read acknowledges no message and does not change the caller's read state.
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:preload_messages bucket, shared with Preload private channel messages by alias.
Preload private channel messages by alias¶
POST /users/@me/channels/messages/preload
An alias of Preload private channel messages. The request body, response, authorisation, and rate limit bucket are identical, and the two paths share one allowance.
List pinned private channels¶
GET /users/@me/pinned-dms
Returns the caller's pinned DM and group DM IDs in pinned order.
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | pinned channels object | The pinned order was returned, possibly empty |
Pinned channels object¶
Structure¶
| Field | Type | Description |
|---|---|---|
| channels | array of snowflake strings | The pinned channel IDs, in pinned order |
Example¶
Pin private channel¶
PUT /users/@me/channels/{channel_id}/pin
Adds a channel to the caller's pinned order. Returns the resulting pinned channels object. Emits a User Pinned DMs Update Gateway event.
A newly pinned channel is appended after every channel already pinned. Pinning an already pinned channel leaves the order untouched and still returns 200 and emits the Dispatch. A channel later closed, left, or deleted stays in the pinned set until it is unpinned.
The channel is not validated
TEBHOS does not check that channel_id names an existing DM or group DM the caller participates in. Pin only IDs taken from List private channels or the Gateway.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the DM or group DM |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | pinned channels object | The channel is present in the pinned set |
| 400 | error response | channel_id is not a number (BAD_REQUEST) |
Side effects¶
The channel is appended to the caller's pinned order, and User Pinned DMs Update reaches only the caller. Its payload is the same pinned channels object, holding the complete resulting order in a channels array. The channel itself is not modified, and no other participant can observe the caller's pinned order.
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:channels bucket, shared with List private channels, Create private channel, and Unpin private channel.
Unpin private channel¶
DELETE /users/@me/channels/{channel_id}/pin
Removes a channel from the caller's pinned order. Returns the resulting pinned channels object. Emits a User Pinned DMs Update Gateway event.
Unpinning preserves the relative order of every channel that remains, and a later pin is appended to the end. The channel does not have to exist, so an entry for a deleted channel can always be removed. Unpinning a channel that is not pinned still returns 200 and emits the same Dispatch.
Path parameters¶
| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the DM or group DM |
Response¶
| Status | Body | Condition |
|---|---|---|
| 200 | pinned channels object | The channel is absent from the pinned set |
| 400 | error response | channel_id is not a number (BAD_REQUEST) |
Side effects¶
The channel is removed from the caller's pinned order, and User Pinned DMs Update reaches only the caller with the complete resulting order. The channel itself is not closed or deleted, and its participants receive no Dispatch.
Rate limit¶
40 requests per 10 seconds for each authenticated user, on the user:channels bucket, shared with List private channels, Create private channel, and Pin private channel.