Skip to content

Calls

A call is the live voice session of a direct message or a group direct message. Channels defines the channel itself and its recipient set.

Joining and leaving a call is a Gateway operation. A client joins or leaves by sending Voice State Update with a null guild_id, and Call Create, Call Update, and Call Delete publish the resulting call state. The Gateway owns the call's ringing timers and participant roster. Voice describes the model.

Every route on this page is user-only. A bot token is rejected with 403 ACCESS_DENIED.

Access rules

Every route requires a direct message or group direct message whose recipients include the caller.

Condition Response
The channel does not exist 404 NOT_FOUND
The channel exists but is not a direct message or group direct message 403 FORBIDDEN
The caller is not a recipient of the channel 403 FORBIDDEN

Voice moderation in a private call

A private call has no moderator and no permission overwrites. No account other than the participant itself can change that participant's voice state, and the owner of a group direct message is no exception. The mute, deaf, and suppress fields of a call voice state are therefore always false. Only the participant's own Voice State Update changes self_mute, self_deaf, self_video, or self_stream.

Silencing another participant happens in the client. A client MAY mute a participant or change per-participant volume, and MUST keep each setting local to the listening device.

Call eligibility object

TEBHOS computes eligibility for one caller against one private channel at the moment of the read.

Structure

Field Type Description
ringable boolean Whether the authenticated user can ring the other side of this channel
silent boolean Whether a newly started call notifies the other recipient without audible ringing. Only meaningful for a direct message, and false whenever ringable is false
reason? string A human-readable explanation, present when ringable is false. For display only; do not parse it

Example

{
  "ringable": true,
  "silent": false
}

Get call eligibility

GET /channels/{channel_id}/call

Returns the call eligibility object for a direct message or group direct message. The caller must satisfy the access rules.

Path parameters

Field Type Description
channel_id snowflake The ID of the direct message or group direct message channel

ringable is false when the caller is already connected to the channel's call, as reported by the Gateway. If the Gateway cannot be reached, that check is skipped.

In a group direct message, ringable is otherwise true and silent is false.

In a direct message, the other recipient's incoming call policy decides. A block in either direction is never ringable. Otherwise TEBHOS reads the recipient's incoming call flags from their user settings in this order: the nobody flag rejects every caller; the friends-only flag admits only a friend; a friend is always admitted under any other policy; a caller who shares a friend with the recipient is admitted when the friends-of-friends flag is set; a caller who shares a guild is admitted when the guild-members flag is set; the everyone flag admits every remaining caller; any other caller is rejected. The silent-everyone flag reports silent as true for a caller admitted by the friends-of-friends, guild-members, or everyone branch. When the settings cannot be read, the caller is treated as ringable.

Ring call recipients evaluates the same policy again.

Response

Status Body Condition
200 call eligibility object Eligibility was returned
403 error response The channel is not a private channel, or the caller is not a recipient
404 error response The channel does not exist

Rate limit

60 requests per 10 seconds for each user and channel, on the channel:call:get bucket.

Ring call recipients

POST /channels/{channel_id}/call/ring

Starts a call in a direct message or group direct message, or rings more recipients of an existing call. The caller must satisfy the access rules. The caller does not have to be connected to the call yet.

Path parameters

Field Type Description
channel_id snowflake The ID of the direct message or group direct message channel

JSON body

Field Type Description
recipients? array[snowflake] The IDs of the recipients to ring. Omitting the field targets every other recipient. An empty array creates the call and rings nobody

Each entry is a snowflake string or number. An ID that is not a current recipient other than the caller returns 400 INVALID_FORM_BODY with the validation code USER_NOT_IN_CHANNEL at the recipients path.

A targeted recipient is rung only when the incoming call policy described by Get call eligibility admits the caller audibly. A recipient the policy excludes, and a recipient it admits only silently, are left out of the ringing set while the request still succeeds.

Response body

Field Type Description
ringing array[snowflake] The recipients this request rang
{
  "ringing": ["1189375284394692611"]
}

Response

Status Body Condition
200 response body The ring operation completed
400 error response A named ID is not a recipient and the request returns INVALID_FORM_BODY with USER_NOT_IN_CHANNEL
403 error response The channel is not a private channel, or the caller is not a recipient
404 error response The channel does not exist

Side effects

The private channel reopens for the caller and every targeted recipient who had closed it, and those accounts receive Channel Create.

When no call is active, TEBHOS creates one: it writes a call system message into the channel (delivered as Message Create), sends the API form of Call Create to every recipient, and starts the Gateway call process, which sends its own Call Create.

Each rung recipient receives a CALL_RING Dispatch with channel_id, call_id, and the initiator (id, name, image_url), and the Gateway call process adds the recipients to its ringing set and sends Call Update.

Ringing lasts 30 seconds per recipient, and its expiry emits Call Update. A call with no connected participant and nobody ringing ends on the Gateway's 120-second idle timer with Call Delete. When a call ends, the call message is updated with its end time and participants.

Rate limit

5 requests per 10 seconds for each user and channel, on the channel:call:ring bucket.

Stop ringing call recipients

POST /channels/{channel_id}/call/stop-ringing

Removes recipients from the ringing set of the active call. The caller must satisfy the access rules.

Path parameters

Field Type Description
channel_id snowflake The ID of the direct message or group direct message channel

JSON body

Field Type Description
recipients? array[snowflake] The IDs of the recipients to stop ringing. Omitting the field targets the caller alone, which is how a client declines its own incoming call

Response body

Field Type Description
stopped array[snowflake] The recipients the request targeted

Response

Status Body Condition
200 response body The stop-ringing operation completed
403 error response The channel is not a private channel, or the caller is not a recipient
404 error response The channel does not exist, or no call is active in it

Side effects

Each targeted recipient receives a CALL_STOP_RING Dispatch with channel_id, call_id, stopped_by, and recipients, so the recipient's other devices stop ringing too. The Gateway call process removes them from its ringing set and sends Call Update.

When that leaves the call with no participant and nobody ringing, the call ends: every recipient receives Call Delete (including an API form with reason: "empty") and the call message is marked ended. Otherwise the call continues, so a caller can keep waiting alone.

Rate limit

20 requests per 10 seconds for each user and channel, on the channel:call:stop_ringing bucket.

Modify call region

PATCH /channels/{channel_id}/call

Changes the voice region of the active call. The caller must satisfy the access rules and does not have to be connected to the call.

Path parameters

Field Type Description
channel_id snowflake The ID of the direct message or group direct message channel

JSON body

Field Type Description
region? ?string The ID of the RTC region to select. An empty string, automatic, and auto select automatic routing. Omitting the field or sending null changes nothing

A region the caller cannot use returns 400 INVALID_OR_RESTRICTED_RTC_REGION.

Response body

Field Type Description
id string The API's call ID
channel_id snowflake The channel
initiator_id snowflake The user who started the call
livekit_room_name string The media room, dm_{channel_id}
started_at ISO8601 timestamp When the call started
region? string The selected region, automatic for automatic routing
voice_states array[object] Always empty; the Gateway holds the live roster
ringing array[snowflake] Recipients the API recorded as ringing

Response

Status Body Condition
200 response body The region was updated, or no change was requested
400 error response The region is unknown or restricted: INVALID_OR_RESTRICTED_RTC_REGION
403 error response The channel is not a private channel, or the caller is not a recipient
404 error response The channel does not exist, or no call is active in it

Side effects

Supplying region records the new region, releases the call's pinned media server, and hands the region to the Gateway call process, which issues each connected participant a fresh Voice Server Update for the new region. Every recipient receives the API form of Call Update.

Rate limit

10 requests per 10 seconds for each user and channel, on the channel:call:update bucket, shared with End call.

End call

POST /channels/{channel_id}/call/end

DELETE /channels/{channel_id}/call

Both routes run the same operation and return 204 with an empty body. They mark the active call as ended in the API. The caller must satisfy the access rules and must be the user who started the call.

Path parameters

Field Type Description
channel_id snowflake The ID of the direct message or group direct message channel

Response

Status Body Condition
204 empty The call was ended
403 error response The channel is not a private channel, the caller is not a recipient, or the caller did not start the call
404 error response The channel does not exist, or no call is active in it

Side effects

The call record and its ringing set are cleared, the call message is marked ended, and every recipient receives the API form of Call Delete with channel_id, call_id, and ended_by.

Leaving is a Gateway operation

Ending the call over HTTP does not disconnect anyone's voice connection. A participant leaves by sending Voice State Update with a null channel and the connection_id to drop. The Gateway ends the call on its own when the last participant leaves and nobody is ringing.

Rate limit

10 requests per 10 seconds for each user and channel, on the channel:call:update bucket.

Start call (legacy)

POST /channels/{channel_id}/call

Starts a call and returns a media credential directly, without the Gateway. It is kept for older TEBHOS clients. New clients join with Voice State Update and start ringing with Ring call recipients.

The caller must satisfy the access rules. A direct message recipient whose privacy settings do not accept calls from the caller returns 403 MISSING_ACCESS. When a call is already active, the route returns that call with a fresh credential.

Response body

Field Type Description
call_id string The API's call ID
channel_id snowflake The channel
livekit_room_name string The media room, dm_{channel_id}
livekit_url string The LiveKit server URL
access_token string A LiveKit access token for the caller
started_at ISO8601 timestamp When the call started
message_id? snowflake The call system message
unavailable? boolean Present and true when every other recipient is currently unavailable
unavailable_recipients? array[snowflake] Recipients that are currently unavailable

A new call writes the call system message and sends the API form of Call Create to every recipient. This route rings nobody.