Skip to content

Streams

A Go Live stream is a screen share on a member's existing voice connection. A stream key names one stream. The operations here record a region preference and manage the stream's JPEG preview image.

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

Stream lifecycle

Starting and stopping a stream is a Gateway operation. Going live publishes a screen share track on the LiveKit participant the member already holds, and the publisher advertises it by setting self_stream on that connection with Voice State Update. TEBHOS opens no second connection and issues no new grant. Voice states the full model.

Stream key

A stream key is one path segment with exactly three colon-separated parts. The form is {guild_id}:{channel_id}:{connection_id} for a guild voice stream and dm:{channel_id}:{connection_id} for a private call stream.

Part Type Description
scope string The literal dm for a private call, or the decimal guild ID for a guild voice channel
channel_id string The decimal ID of the voice channel or private channel the stream runs in
connection_id string The connection_id of the publisher's voice connection, the one in its voice state and in its media participant identity

A key with a different part count, an empty channel or connection part, a non-numeric channel part, or a scope that is neither dm nor a number returns 400 BAD_REQUEST.

Every route resolves the channel from the channel part of the key. Upload stream preview and Create stream preview upload URL also take a channel_id in their body, which must equal the key's channel part, or the request returns 400 BAD_REQUEST.

TEBHOS checks the scope against the resolved channel. A guild-scoped key must name the channel's own guild, and a dm key must name a channel that belongs to no guild. A mismatch returns 403 FORBIDDEN.

Access rules

Get stream preview requires read access. Every other route on this page requires mutation access.

Read access is access to the resolved channel: the channel must exist, the caller must be a member of its guild (or a recipient of the private channel), and the caller must hold VIEW_CHANNEL and CONNECT there. The caller does not have to own the stream, so any member who can join the channel can read its preview.

Condition Response
The channel does not exist, the caller is not a guild member, or the caller is not a recipient of the private channel 404 NOT_FOUND
The caller lacks VIEW_CHANNEL or CONNECT 403 FORBIDDEN

Mutation access requires everything read access does. A guild channel also requires the STREAM permission (403 FORBIDDEN without it). The caller must hold a voice connection in the resolved channel whose connection_id is the connection part of the key, according to the Gateway's voice states. A caller that holds no such connection is rejected with 403 FORBIDDEN, even when it owns the channel.

A preview can exist with no live stream

A caller can record a region and upload a preview for any voice connection it holds, whether or not it is streaming.

Stream preview upload object

A short-lived URL that accepts one stream's preview image. Create stream preview upload URL returns it, and the client uploads the image with a single PUT to upload_url.

Structure

Field Type Description
upload_url string A presigned object storage URL that accepts the preview bytes with one PUT request
method string Always PUT
content_type string The media type to send with the PUT. The request's content_type is returned verbatim, and an absent field yields image/jpeg
expires_at ISO8601 timestamp The time the URL expires
expires_in integer The lifetime of the URL in seconds, always 86400
max_bytes integer The maximum accepted byte count, always 1000000

A client MUST send the URL exactly as returned and MUST send the returned content_type. The URL accepts repeated PUT requests until it expires, and each one replaces the previous preview.

Example

{
  "upload_url": "https://cdn.example.com/stream_previews/1189375284394692608-1189375284394692610-abc123-3f9a1c2d4e5b6a70.jpg?X-Amz-Signature=...",
  "method": "PUT",
  "content_type": "image/jpeg",
  "expires_at": "2026-09-24T12:00:00.000Z",
  "expires_in": 86400,
  "max_bytes": 1000000
}

Modify stream region

PATCH /streams/{stream_key}/stream

Records the preferred media region for a stream and returns 204 with an empty body. The caller must satisfy the mutation access boundary.

Path parameters

Field Type Description
stream_key string The stream key whose region is set

JSON body

Field Type Description
region? ?string The RTC region ID, 1 through 64 characters. Omitting it stores no region

A region identifier is the id of an RTC region object, as returned by List RTC regions. A value outside the length bound returns 400 BAD_REQUEST.

Response

Status Body Condition
204 empty The preference was recorded
400 error response The stream key is malformed, or region is out of bounds
403 error response The key's scope disagrees with the channel, the caller lacks a required permission, or it holds no matching voice connection
404 error response The channel does not exist or is not accessible to the caller

Side effects

The preference is kept for one day. The call does not move the stream, TEBHOS does not validate the region against the caller's accessible regions, and no Gateway Dispatch is emitted.

Rate limit

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

Get stream preview

GET /streams/{stream_key}/preview

Returns the stored preview bytes for a stream. The caller must satisfy the read access boundary.

Path parameters

Field Type Description
stream_key string The stream key whose preview is returned

Response

Status Body Condition
200 preview bytes A stored preview exists
400 error response The stream key is malformed
403 error response The key's scope disagrees with the channel, or the caller lacks VIEW_CHANNEL or CONNECT
404 error response The channel does not exist or is not accessible, or no preview is stored for this key

The 200 response carries Cache-Control: no-store, private and Pragma: no-cache. Its Content-Type is the media type recorded when the preview was written, and image/jpeg when none was recorded.

A stored preview expires one day after it was written. A read after that point, a stored object that is missing or unreadable, and an object larger than 1,000,000 bytes all answer 404. A client SHOULD treat a 404 whose channel is otherwise accessible as "no preview yet".

The uploaded bytes are not inspected on every path, so a client MUST NOT treat Content-Type as proof of the payload format and MUST decode defensively.

Rate limit

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

Upload stream preview

POST /streams/{stream_key}/preview

Replaces the stream's preview with a JPEG sent inline as base64 and returns 204 with an empty body. The caller must satisfy the mutation access boundary.

To upload the image bytes with a separate PUT request, use Create stream preview upload URL.

Path parameters

Field Type Description
stream_key string The stream key that receives the preview

JSON body

Field Type Description
channel_id snowflake The ID of the channel the stream is active in, equal to the key's channel part. A string or a number
thumbnail base64 string The base64-encoded JPEG, 1 through 2,000,000 characters
content_type? string The declared media type

A content_type containing jpeg or jpg in any case is accepted without inspecting the bytes. Any other value, and an absent field, is accepted only when the decoded bytes begin with FF D8 and end with FF D9. The decoded image is at most 1,000,000 bytes. TEBHOS stores the accepted content_type verbatim, or image/jpeg when the field is absent. The image may also be scanned for content-policy violations.

Response

Status Body Condition
204 empty The preview was accepted
400 error response The stream key is malformed, the body channel disagrees with the key, thumbnail is empty, too long, not valid base64, or decodes to nothing, the image is not a JPEG, it exceeds 1,000,000 bytes, or it violates the content policy
403 error response The key's scope disagrees with the channel, the caller lacks a required permission, or it holds no matching voice connection
404 error response The channel does not exist or is not accessible to the caller

Side effects

The decoded bytes replace whatever preview the stream key already had, and the preview expires one day after the write. No Gateway Dispatch is emitted, so viewers see the new image on their next Get stream preview read.

Rate limit

20 requests per 10 seconds for each stream key, on the stream:preview:post bucket.

Create stream preview upload URL

POST /streams/{stream_key}/preview/upload-url

Returns a stream preview upload object granting a bounded PUT upload of the JPEG preview. The caller must satisfy the mutation access boundary.

Path parameters

Field Type Description
stream_key string The stream key that receives the preview

JSON body

Field Type Description
channel_id snowflake The ID of the channel the stream is active in, equal to the key's channel part. A string or a number
content_type? string The declared media type. Only a value containing jpeg or jpg in any case is accepted, and an absent field means image/jpeg

Response

Status Body Condition
200 stream preview upload object The upload URL was issued
400 error response The stream key is malformed, the body channel disagrees with the key, or the declared media type is not JPEG
403 error response The key's scope disagrees with the channel, the caller lacks a required permission, or it holds no matching voice connection
404 error response The channel does not exist or is not accessible to the caller

Side effects

There is no separate completion call. After the PUT succeeds, Get stream preview returns the uploaded image; until then it returns 404 or the previous image. The preview record written by this call expires one day later, so a URL used after that writes an object no read can find. No Gateway Dispatch is emitted.

The upload is not inspected

The presigned URL enforces the signed media type and size only as far as object storage does. An object above max_bytes that still gets stored is never served: Get stream preview answers 404 for it.

Rate limit

20 requests per 10 seconds for each stream key, on the stream:preview:upload_url bucket.

Delete stream preview

DELETE /streams/{stream_key}/preview

Deletes the stored preview for a stream and returns 204 with an empty body. Deleting an absent preview also returns 204. The caller must satisfy the mutation access boundary.

Path parameters

Field Type Description
stream_key string The stream key whose preview is deleted

Response

Status Body Condition
204 empty The preview was deleted, or none was stored
400 error response The stream key is malformed
403 error response The key's scope disagrees with the channel, the caller lacks a required permission, or it holds no matching voice connection
404 error response The channel does not exist or is not accessible to the caller

Side effects

The preview stops being readable, and its stored object is removed. No Gateway Dispatch is emitted.

Rate limit

20 requests per 10 seconds for each stream key, on the stream:preview:delete bucket.