--- url: /mcp-server.md --- # MCP Server Connect AI assistants and agents to a Restream account over the [Model Context Protocol](https://modelcontextprotocol.io/). The Restream MCP server is a remote, OAuth-secured server that exposes Restream's channel, event, destination, recording, storage, clips, and analytics capabilities as MCP tools, so an MCP client can read and operate a user's account on their behalf. ```text https://mcp.restream.io/mcp ``` ## How It Works * **Transport:** Remote server over Streamable HTTP. Nothing is installed locally; clients connect to the hosted endpoint above. * **Auth:** OAuth 2.1. On first use the client opens a browser window for the user to sign in to Restream and authorize access. The client stores and refreshes the token, so there are no long-lived API keys to manage in config. * **Scope:** The server acts strictly within the authenticated user's account and plan. It can only do what that user could already do in the dashboard. * **Relationship to the REST API:** The MCP tools are a curated, action-oriented surface over Restream capabilities. For raw HTTP endpoints, OAuth app registration, scopes, and webhooks, use the [Restream API docs](https://developers.restream.io/guide/getting-started). The MCP server is for agent workflows; the REST API is for application integrations. :::info One-click installation from the Claude and ChatGPT connector directories is not available yet. Until then, add the server manually with the endpoint above. Setup is otherwise identical. ::: ## Prerequisites * [A Restream account](https://restream.io/signup). Free accounts can connect channels and create events; some destinations and limits depend on plan. * An MCP-capable client that supports a remote server. ## Connecting ### Step 1: Review The Security Model :::warning The MCP server can create clip projects, create and reschedule events, remove events, and toggle destinations. Keep human confirmation enabled in your client and review write tool calls before approving them. ::: ### Step 2: Configure Your Client All clients point at the same endpoint: `https://mcp.restream.io/mcp`. The first tool call triggers the OAuth browser sign-in. **Claude Code** ```bash claude mcp add --transport http restream https://mcp.restream.io/mcp # Then authorize: /mcp ``` **Claude.ai / Claude Desktop** Settings → Connectors → **Add custom connector**: * Name: `Restream` * URL: `https://mcp.restream.io/mcp` **ChatGPT** Enable Developer mode in Settings → Security and login → Developer mode. Then go to Settings → Plugins → Browse plugins and click the **+** icon next to the search bar to create a connector: * Name: `Restream` * MCP server URL: `https://mcp.restream.io/mcp` * Authentication: `OAuth` **Grok** Go to **Plugins → New connector → Custom** and paste the endpoint URL (`https://mcp.restream.io/mcp`). Sign in when prompted. xAI's menus change frequently; check their docs if this has moved. **Grok Bot** In Grok Bot, go to the **Marketplace**, search for **Restream**, and click **Add**. Sign in when prompted. **Cursor** Open **Customize** from the left sidebar, search for **Restream**, and click **Add**. Sign in when prompted. **VS Code with GitHub Copilot** Add the server to `.vscode/mcp.json`: ```json { "servers": { "restream": { "type": "http", "url": "https://mcp.restream.io/mcp" } } } ``` **Codex** In Settings → MCP Servers, add a Streamable HTTP server named `restream` with URL `https://mcp.restream.io/mcp`. **stdio-only clients** Bridge with the `mcp-remote` shim, which handles the OAuth handshake: ```json { "mcpServers": { "restream": { "command": "npx", "args": ["mcp-remote", "https://mcp.restream.io/mcp"] } } } ``` :::info Client UIs change frequently. If a menu label differs from the steps above, check your client's MCP documentation. ::: ### Step 3: Verify Ask the assistant: "List my connected Restream channels." It should request permission to call the Restream tools and return your channels by `platform` and `displayName`. If you see them, you are connected. ## Authentication And Authorization * The server implements OAuth 2.1; your MCP client performs the authorization-code flow in the browser and manages token refresh. * Access is bound to the signed-in user. There is no separate API-key or bearer-token path for the MCP server. Authentication is OAuth only. * For security, the MCP server deliberately will not: * accept passwords, including account or channel logins; * accept RTMP/SRT credentials to add custom channels; * return stream keys or other internal IDs. Perform those actions in the [dashboard](https://app.restream.io/) or via the REST API. ## Tool Reference The server exposes 26 tools across six areas. Tools are tagged **read** for no side effects or **write** for actions that mutate account state. Your client should confirm before executing write tools. ### Account And Channels #### `get_current_user` - read Returns the authenticated user's profile and workspace context. #### `list_channels` - read Lists channels connected to the account. #### `check_channel_api_access` - read Checks every connected channel for platform API access issues and returns user-facing remediation steps for any that need attention. ### Events #### `list_user_events` - read Finds events across their lifecycle states. #### `get_event` - read Returns one event, including its active destinations. #### `create_event` - write Creates a Restream event from the user's chosen source, such as Studio, encoder, uploaded video, or saved recording. #### `edit_event` - write Updates event details such as title and description. #### `reschedule_event` - write Schedules a draft event or moves a scheduled one. #### `unschedule_event` - write Removes the start time, turning the event back into a draft. External platform deletion stays disabled. #### `remove_event` - write Removes the event from the Restream account. Only affects Restream state; it never deletes or modifies broadcasts or posts on external platforms. #### `set_event_loop_count` - write Sets how many times a file- or recording-backed event repeats playback. #### `enable_event_external_scheduled_broadcasts` - write Enables creation and sync of scheduled broadcasts on supported external platforms for an already-scheduled event. This cannot be disabled through MCP. ### Destinations #### `list_event_destinations` - read Lists active destinations on an event. #### `check_event_channels_activation` - read Validates that the user's plan allows the selected channels to be active simultaneously and explains any plan limits. #### `toggle_event_channel_on` - write Connects a channel as an event destination. #### `toggle_event_channel_off` - write Disconnects a destination from an event. Affects Restream only; nothing is changed on the platform. #### `edit_event_destination` - write Updates platform-specific settings on an existing destination. ### Recordings And Storage #### `list_user_recordings` - read Lists recordings grouped by recording, with file names. #### `get_recording_download_url` - read Returns a temporary download link for one recording file. #### `list_user_storage_files` - read Lists video storage files. ### Clips #### `list_user_clips_projects` - read Lists clip projects for the authenticated account, with pagination and sort options. #### `get_user_clips_project_details` - read Returns clips and posted clip information for one clip project. #### `create_user_clips_project` - write Creates a clip project from a supported source. Currently, use `VideoStorageFile` sources discovered with `list_user_storage_files`. #### `create_user_clip_download` - read Creates a temporary download URL for one clip. Discover clip IDs with `get_user_clips_project_details` before requesting a download. ### Analytics #### `get_event_analytics_viewers` - read Returns viewer analytics for an event, with totals and per-channel values. #### `get_event_analytics_messages` - read Returns chat-message analytics for an event, with totals and per-channel values. ## Common Workflows **Go live to multiple platforms** Choose connected destinations, check plan limits, and prepare a Restream event for the selected platforms. **Schedule with external broadcasts** Create or update a scheduled event and sync broadcast details to supported platforms. **File- or recording-backed event** Create an event from stored media or a saved recording, with optional replay behavior. **Post-stream analytics** Review viewer and chat activity after a stream. **Download a recording** Find a recording and provide temporary download access. **Generate and download clips** Find a video storage file, create a clip project, review generated clips and posted status, then provide a temporary download URL for the selected clip. ## Security Connecting an LLM to an account that can create clip projects, create and remove events, and update destinations carries the usual agent risks. Recommended practices: * **Same-access principle:** The MCP server operates as the signed-in user. Grant it only to people who should already have that access. * **Keep human confirmation on:** Most clients prompt before each tool call. Leave this enabled and review write tool calls before approving them. * **Prompt injection:** If an agent processes untrusted content, such as a webpage, chat message, or file, and that content contains instructions, the agent may try to act on them. Review planned actions before approving, and do not auto-approve write tools when untrusted input is in context. * **Removing destinations is non-destructive externally:** `toggle_event_channel_off` and `remove_event` only change Restream state; they never delete or modify anything on the destination platforms. * **Verify the endpoint:** Only connect to the official `https://mcp.restream.io/mcp`. * **Credential boundary:** The server never accepts passwords or RTMP/SRT credentials and never returns stream keys. Handle those in the dashboard or REST API. ## Limitations * No password, RTMP/SRT credential entry, or stream-key retrieval. * Not yet listed in the Claude or ChatGPT connector directories. Add the server manually with the endpoint above. Directory listings are coming soon. --- url: /guide.md --- # Restream API — Getting Started The Restream API lets you manage channels, schedule and control live events, read analytics, generate clips, and drive Restream Studio — all programmatically. Most endpoints are authenticated with OAuth 2.0. Follow the [Getting Started guide](/guide/getting-started.md) for a step-by-step walkthrough of registering an app, authenticating, and calling your first endpoint. ## Explore the API * [Authentication](/authentication/overview.md) — Authorize your app with OAuth 2.0 and manage access tokens. * [Public API](/public-api.md) — Read-only platform and ingest-server reference data, no auth required. * [Channels](/channels.md) — List, add, and manage the destinations you stream to. * [Events](/events.md) — Schedule, control, and review live streaming events. * [Analytics](/analytics.md) — Pull viewer and chat-engagement metrics for your streams. * [Storage](/storage.md) — Access recordings and files saved to your Restream storage. * [Clips](/clips.md) — Generate and download short clips from your streams. * [Studio](/studio.md) — Programmatically manage Restream Studio assets. * [Chat](/chat.md) — Stream live chat across platforms over WebSocket. --- url: /guide/getting-started.md --- # Getting Started :::info You need a Restream account to create an application. [Sign up](https://restream.io) ::: :::info If you have any issues, contact developers@restream.io or [suggest an idea](https://ideas.restream.io/api-requests). ::: ## Step 1: Create a new application Open the [Applications](https://developers.restream.io/apps) page and click the **Create New** button. ## Step 2: Display name and avatar Choose a display name and upload an avatar. This information will be shown to users when they authorize your app. ## Step 3: Copy your Client ID and Client Secret On the app settings page you will find two important credentials: * **Client ID** — a unique public identifier for your app. Click the copy button next to it to copy. * **Client Secret** — a private key used to authenticate your app. It is hidden by default. Click on the masked field to reveal it, then use the copy button to copy. Keep this value secret and never expose it publicly. :::warning If your Client Secret is ever compromised, click the **"Regenerate"** link to create a new one. Note that your app will stop working until you update the secret in your integration code. ::: ## Step 4: Add a Redirect URI Under the **"Redirect URI's"** section, add the callback URL where users will be sent after they authorize your app. This is required for the OAuth2 flow. If you are using a third-party platform or automation tool, it will typically provide you with a specific redirect URL to paste here. Add it by typing or pasting the URL into the input field. You can add multiple redirect URIs if needed by clicking **"Add another"**. ## Step 5: Select scopes Under the **"Scopes"** section, check the permissions your application needs. Only select the scopes your integration actually requires. :::info Changing scopes after users have already authorized your app may require them to re-authenticate. ::: --- url: /authentication.md --- # Restream API Authentication (OAuth 2.0) The Restream API uses OAuth 2.0 for authentication. Your application redirects the user to Restream to grant access, receives an authorization code, and exchanges it for an access token used to call protected endpoints. Read the [Authentication Overview](/authentication/overview.md) for the full walkthrough, including scopes and token lifetimes. ## The OAuth 2.0 flow * [Overview](/authentication/overview.md) — How OAuth 2.0 works with Restream, including scopes. * [Authorize Dialog](/authentication/authorize-dialog.md) — Redirect users to grant your app access. * [Capture the Code](/authentication/capture-the-code.md) — Receive the authorization code on your redirect URI. * [Code Exchange](/authentication/code-exchange.md) — Exchange the code for an access token. * [Refreshing Tokens](/authentication/refreshing-tokens.md) — Keep access alive with refresh tokens. * [Revoking Tokens](/authentication/revoking-tokens.md) — Invalidate tokens when access is no longer needed. --- url: /authentication/overview.md --- # Overview You will need to perform the following sequence of steps in order to authenticate the user. ## OAuth 2.0 flow ### Step 1: Open the authorize dialog Send the user to Restream OAuth2 [Login page](/authentication/authorize-dialog.md). ### Step 2: Capture the authorization code [Capture](/authentication/capture-the-code.md) the response code on your redirect endpoint via a webhook. ### Step 3: Exchange the code for tokens [Exchange](/authentication/code-exchange.md) a code for a token pair. ### Step 4: Refresh access tokens [Refresh](/authentication/refreshing-tokens.md) expired access tokens. ### Step 5: Revoke tokens [Revoke](/authentication/revoking-tokens.md) tokens when the user signs out or you revoke access. --- url: /authentication/authorize-dialog.md --- # Authorize Dialog ## Parameters | Parameters | | |--------------|--------------| | **response\_type:** | This value should always be `code` | ## Authorization URL In order to obtain an OAuth2 authorization code you should first send the user to the Restream OAuth2 Login page. Send the user to the following URL with the specified parameters: ```bash title="URL" https://api.restream.io/login?response_type=code&client_id=[your client id]&redirect_uri=[your redirect URI]&state=[random opaque token] ``` ## User flow This endpoint will render a view and automatically attempt to authenticate the user *if they already have a valid session* from Restream.io. If successful, the user will automatically be presented with the authorization dialog. Otherwise, they will be presented with a form where the user can login or create a new account. Upon successful login or registration, the user will be presented with the authorization dialog. If the user denies the dialog, they will be redirected back to the `redirect_uri` with **no** parameters. :::info **Info:** While it is **not** implemented yet, in the future if a user only selects a subset of permissions your application has asked for, you must allow the user to continue without enforcing permissions unless it were to affect the functionality of your client app. A `scope` parameter will be returned in the [Capture](/authentication/capture-the-code.md) step. ::: *** ## Security :::danger The `state` token is used to prevent CSRF attacks and should be generated and saved using web storage when the client presents the user to authenticate with Restream. In the [Capture](/authentication/capture-the-code.md) step, the `state` parameter will be sent back as a query parameter. This value should equal the initial value sent in the original request. If it is not equal, do not issue a request for exchanging the `code` for a `token`. ::: --- url: /authentication/capture-the-code.md --- # Capture the Code ## Redirect parameters | Parameters | | |--------------|--------------| | **code:** | The short lived `code` from the `authorization` grant | | **scope:** | A comma-separated string of permissions the user has agreed to allow. | | **state:** | The `state` parameter specified in the original request | ## Handling the redirect After user has granted access for the application, he should be redirected back to the specified redirect uri (valid to the application) with an OAuth2 `code`, `scope`, and `state` appended as get parameters. ```bash title="URL" https://your-website.com/oauth?code=[code]&scope=[allowed permissions]&state=[original state parameter] ``` :::tip If a user has declined the application **-or-** has declined required permissions for your application, you should inform the user with a warning to prompt them to try again with the minimum permissions your application needs to avoid confusion. ::: *** ## Security :::danger It is the client's responsibility to verify the `state` parameter received in the webhook is the same as the one sent in the original [Authorize dialog](/authentication/authorize-dialog.md) request to prevent CSRF attacks. ::: --- url: /authentication/code-exchange.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Code Exchange | Parameters | | |--------------|--------------| | **grant\_type:** | For a `code` exchange, this value should be set to `authorization_code` | | **redirect\_uri:** | Redirect URI of your Restream API application | | **code:** | Code, received from the previous method: [Capture the code](/authentication/capture-the-code.md) | | **client\_id:** | Client id of your Restream API application | | **client\_secret:** | Client secret of your Restream API application | There are two ways to exchange a code for an access / refresh token pair: 1. Using a Basic Auth header to send the `client_id` and `client_secret` 2. Use the `client_id` and `client_secret` as part of the x-www-form-urlencoded body :::tip Use the Basic Auth header to prevent passing confidential information as part of a query string that could be logged. For example a web server hosting an Nginx load balancer could log the request with the full query string. ::: You can exchange the received code for token by sending a POST request to the following URL: ```bash curl -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --user [your client id]:[your client secret] \ --data "grant_type=authorization_code&redirect_uri=[your redirect URI]&code=[code]" \ https://api.restream.io/oauth/token ``` Upon a successful exchange, you will receive a pair of tokens with their expiration times. The expiration time for access tokens is **1 hour** and **1 year** for refresh tokens from the time of grant. ### Response ```json { "access_token": "2a407cfcfdf8920242fd6dc7e5f83c86374ef925", "expires": 1520280067, "expires_in": 3600, "refresh_token": "9ca0e1e99ae3eae136a2bd98fc9c1534ea928ed3", "scope": "profile.read channels.read chat.read stream.read", "token_type": "Bearer", "accessToken": "2a407cfcfdf8920242fd6dc7e5f83c86374ef925", "accessTokenExpiresIn": 3600, "accessTokenExpiresAt": "2018-03-05T20:01:07.215Z", "accessTokenExpiresEpoch": 1520280067, "refreshToken": "9ca0e1e99ae3eae136a2bd98fc9c1534ea928ed3", "refreshTokenExpiresIn": 31536000, "refreshTokenExpiresAt": "2019-03-05T19:01:07.215Z", "refreshTokenExpiresEpoch": 1551812467, "scopeJson": [ "profile.read", "channels.read", "chat.read", "stream.read" ], "tokenType": "Bearer" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "Invalid grant: authorization code is invalid", "name": "invalid_grant" } } ``` --- url: /authentication/refreshing-tokens.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Refreshing Tokens | Parameters | | |--------------|--------------| | **grant\_type:** | This value should be set to `refresh_token` | | **refresh\_token:** | The refresh token received from the `code` exchange or a previous `refresh_token` grant | | **client\_id:** | Client id of your Restream API application | | **client\_secret:** | Client secret of your Restream API application | There are two ways to refresh tokens: 1. Using a Basic Auth header to send the `client_id` and `client_secret` 2. Use the `client_id` and `client_secret` as part of the x-www-form-urlencoded body :::tip Use the Basic Auth header to prevent passing confidential information as part of a query string that could be logged. For example a web server hosting an Nginx load balancer could log the request with the full query string. ::: You can refresh an access token by sending a POST request to the following URL: ```bash curl -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --user [your client id]:[your client secret] \ --data "grant_type=refresh_token&refresh_token=[refresh token]" \ https://api.restream.io/oauth/token ``` Upon a successful refresh, the previous access & refresh token pair will be invalidated and you will receive a new pair of tokens with new expiration times. The expiration time for access tokens is **1 hour** and **1 year** for refresh tokens from the time of grant. :::warning Because this endpoint requires the submission of the `client_secret`, user's devices should never have or use the secret to make refresh requests directly to Restream. Instead, their device should make a request to a proxy endpoint provided by the client web application. The client should then forward the request with the appropriate credentials to the refresh token route listed above and relay the results back to the user. ::: ## Response ```json { "access_token": "7e61c8a5e2f99404730c511de6580412e618da35", "expires": 1520280099, "expires_in": 3600, "refresh_token": "0e633c3343a2df84b1526f4c2e6993ff17e05cab", "scope": "profile.read channels.read chat.read stream.read", "token_type": "Bearer", "accessToken": "7e61c8a5e2f99404730c511de6580412e618da35", "accessTokenExpiresIn": 3600, "accessTokenExpiresAt": "2018-03-05T20:01:38.518Z", "accessTokenExpiresEpoch": 1520280099, "refreshToken": "0e633c3343a2df84b1526f4c2e6993ff17e05cab", "refreshTokenExpiresIn": 31536000, "refreshTokenExpiresAt": "2019-03-05T19:01:38.518Z", "refreshTokenExpiresEpoch": 1551812499, "scopeJson": [ "profile.read", "channels.read", "chat.read", "stream.read" ], "tokenType": "Bearer" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "Invalid grant: refresh token is invalid", "name": "invalid_grant" } } ``` --- url: /authentication/revoking-tokens.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Revoking Tokens | Parameters | | |--------------|--------------| | **token:** | This parameter should contain the token string to be revoked | | **token\_type\_hint:** | Optional for performance - this can be either one of `access_token` or `refresh_token`. | Revoking a token should be done when a user wishes to logout of the client integration. The response is always returned with an empty body and a 200 http status code regardless if a token was revoked or not. You can revoke an access or refresh token by sending a POST request to the following URL: ```bash curl -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ --data "token=[access or refresh token]&token_type_hint=['access_token' or 'refresh_token']" \ https://api.restream.io/oauth/revoke ``` Upon a successful revocation, any access or refresh tokens which were associated with each other will be permanently invalidated. Ex: A revocation request for an access token will also revoke the corresponding refresh token and vice versa. ## Response ``` ``` ``` ``` --- url: /public-api.md --- # Restream Public API The Restream Public API exposes read-only reference data you can query without authentication. Use it to look up every streaming platform Restream supports and to find the optimal ingest server for reliable, low-latency RTMP delivery. ## Endpoints * [Platforms](/public-api/platforms.md) — List every streaming platform Restream can broadcast to. * [Ingest Servers](/public-api/servers.md) — Discover available RTMP ingest servers and their locations. --- url: /public-api/platforms.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Platforms List all Restream's supported platforms :::info This is a public method, authentication is not required ::: List all Restream's supported platforms. Platforms are the destinations where users can send their streams. ```bash curl -X GET \ https://api.restream.io/v2/platform/all ``` ## Response ```json [ { "id": 1, "name": "Twitch", "url": "http://twitch.tv", "image": { "png": "https://restream.io/img/api/platforms/platform-1.png", "svg": "https://restream.io/img/api/platforms/platform-1.svg" } }, { "id": 5, "name": "Youtube", "url": "https://www.youtube.com", "image": { "png": "https://restream.io/img/api/platforms/platform-5.png", "svg": "https://restream.io/img/api/platforms/platform-5.svg" } }, ... ] ``` --- url: /public-api/servers.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Ingest Servers List all Restream's ingest servers :::info This is a public method, authentication is not required ::: List all Restream's ingest servers. Ingest servers are the RTMP ingestion points where users can send their streams. ```bash curl -X GET \ https://api.restream.io/v2/server/all ``` ## Response ```json [ { "id": 20, "name": "Autodetect", "url": "live.restream.io", "rtmpUrl": "rtmp://live.restream.io/live", "latitude": "0.00000000", "longitude": "0.00000000" }, { "id": 1, "name": "EU-West (London, GB)", "url": "london.restream.io", "rtmpUrl": "rtmp://london.restream.io/live", "latitude": "51.50735100", "longitude": "-0.12775800" }, ... ] ``` --- url: /private-api.md --- # Restream Private API The Restream Private API gives an authenticated user access to their own account data — profile details, the currently selected ingest, the RTMP stream key, and the chat URL — plus a WebSocket channel for real-time streaming updates. ## Endpoints * [Profile](/private-api/profile.md) — Read the authenticated user's account profile. * [Selected Ingest](/private-api/selected-ingest.md) — Get the user's currently selected ingest server. * [Stream Key](/private-api/stream-key.md) — Retrieve the user's RTMP stream key. * [Chat URL](/private-api/chat-url.md) — Get the URL for the user's combined chat. * [Streaming Updates](/private-api/streaming-updates.md) — Subscribe to real-time streaming status over WebSocket. --- url: /private-api/profile.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Profile List user's profile details :::info **Required scope:** profile.read ::: :::warning This method requires authentication ::: Retrieve a user's profile information. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/profile ``` ## Response ```json { "id": 000, "username": "xxx", "email": "xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /private-api/selected-ingest.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Selected Ingest Get user's selected ingest id :::info **Required scope:** channels.read ::: :::warning This method requires authentication ::: Retrieves a user's selected ingest id. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/ingest ``` ## Response ```json { "ingestId": 8 } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /private-api/stream-key.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Stream Key Get a user's stream key and SRT url :::info **Required scope:** stream.read ::: :::warning This method requires authentication ::: Retrieve a user's stream key and SRT url. If user does not have access to SRT streaming - SRT url will have a NULL value in the response. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/streamKey ``` ## Response ```json { "streamKey": "re_xxx_xxx", "srtUrl": "srt://live.restream.io:2010?streamid=srt_xxx_xxx_xxx&passphrase=re_xxx_xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /private-api/chat-url.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Chat URL Get a user's chat URL :::info **Required scope:** chat.read ::: :::warning This method requires authentication ::: Retrieve a user's chat URL. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/webchat/url ``` :::info The URL returned has the default parameters set. You can modify these values as needed. ::: ## Response ```json { "webchatUrl": "https://chat.restream.io/embed?token=xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /private-api/streaming-updates.md --- # Streaming Updates Listen for streaming-related updates events This is a newer and improved replacement for stream status events Restream had before. :::warning This method requires authentication ::: Here is a sample in TypeScript that connects to the API using WebSocket API as it is available in the browser: ```typescript // OAuth `bearer` token const accessToken = '[access token]'; const url = `wss://streaming.api.restream.io/ws?accessToken=${accessToken}`; const connection = new WebSocket(url); connection.onmessage = (message) => { // IUpdates interface is provided below const update: IUpdates = JSON.parse(message.data); console.log(update); }; connection.onerror = console.error; ``` The way this API works is following: * upon connection you'll receive all updates since about 1 minute ago (this should be enough information to reconstruct the whole state if needed) * you will continue receiving new updates as they happen ## Message Types All WebSocket messages are JSON strings and should strictly satisfy following schema (TypeScript syntax, new actions and fields may be added over time): ```typescript // Incoming stream has created or updated (there is no separate event for creation here) interface IIncomingStreamUpdated { action: 'updateIncoming'; userId: number; eventId: string | null; // When stream started, Unix timestamp in seconds createdAt: number; // Streaming session identifier suid: string; // Streaming parameters streaming: { fps: number; keyframeInterval: number; lossRate: number; // Bits per second bitrate: { total: number, audio: number, video: number, }; codec: { audio: string, video: string, }, profileAndLevel: string; height: number; width: number; }; } // Incoming stream has finished interface IIncomingStreamDeleted { action: 'deleteIncoming'; userId: number; eventId: string | null; // When stream started, Unix timestamp in seconds createdAt: number; // Streaming session identifier suid: string; } // Outgoing stream to end platform has created or updated // (there is no separate event for creation here) interface IOutgoingStreamUpdated { action: 'updateOutgoing'; userId: number; eventId: string | null; // Streaming platform ID on Restream platformId: number; channelId: number; // When outgoing stream started, Unix timestamp in seconds createdAt: number; // Channel details channelIdentifier: string; eventIdentifier: string; // Streaming parameters streaming: { // Status from the point of view of Restream status: 'CONNECTING' | 'CONNECTED' | 'DISCONNECTED', // Bits per second bitrate: number, bufferedBytes: number, }; } // Outgoing stream has finished interface IOutgoingStreamDeleted { action: 'deleteOutgoing'; userId: number; eventId: string | null; // Streaming platform ID on Restream platformId: number; channelId: number; // When outgoing stream started, Unix timestamp in seconds createdAt: number; } // Stream information from the point of view of end platform interface IStatusesUpdated { action: 'updateStatuses'; userId: number; eventId: string | null; // Streaming platform ID on Restream platformId: number; channelId: number; // When outgoing stream started, Unix timestamp in seconds createdAt: number; // When the information was collected // (this can be a bit outdated cached information for long streaming sessions) updatedAt: number; // Channel details channelIdentifier: string; eventIdentifier: string; // Information from end platform channelViews: number | null; followers: number | null; gameTitle: string | null; online: boolean; streamViews: number | null; title: string | null; viewers: number | null; } export type IUpdates = IIncomingStreamUpdated | IIncomingStreamDeleted | IOutgoingStreamUpdated | IOutgoingStreamDeleted | IStatusesUpdated; ``` :::info `null` means that information is not available for the stream (temporarily or permanently) ::: --- url: /channels.md --- # Restream Channels API Channels are the destinations a Restream account broadcasts to — YouTube, Twitch, Facebook, and more. The Channels API lets you list connected channels, inspect a single channel, add new destinations, and remove ones you no longer use. ## Endpoints * [Channels](/channels/channels.md) — List all channels connected to the account. * [Channel](/channels/channel.md) — Retrieve a single channel by ID. * [Add Channel](/channels/channel-add.md) — Connect a new streaming destination. * [Delete Channel](/channels/channel-delete.md) — Remove a connected channel. --- url: /channels/channels.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Channels List user's channels :::info **Required scope:** channels.read ::: :::warning This method requires authentication ::: Retrieve a list of the user's channels. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/channels ``` ## Response ```json { "channels": [ { "id": 123456, "platformId": 29, "channelUrl": "https://example.com/live", "displayName": "My Custom RTMP" }, { "id": 123457, "platformId": 73, "channelUrl": "https://instagram.com/xxx", "displayName": "My Instagram" } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /channels/channel.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Channel Get user channel by ID :::info **Required scope:** channels.read ::: :::warning This method requires authentication ::: Retrieve a user channel by ID. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/channels/123456 ``` ## Path Parameters | Parameter | Type | Description | | ----------- | ------ | ---------------------------- | | `channelId` | number | The ID of the channel to retrieve | ## Response ```json { "id": 123456, "platformId": 29, "channelUrl": "https://example.com/live", "displayName": "My Custom RTMP" } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "ChannelNotFound", "name": "channel_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /channels/channel-add.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Add Channel Add a manually-configured streaming channel :::info **Required scope:** channels.write ::: :::warning This method requires authentication ::: ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"platformId": 29, "streamUrl": "rtmp://example.com/live", "streamKey": "abc123"}' \ https://api.restream.io/v2/user/channels ``` ## Request Body | Field | Type | Description | | ----- | ---- | ----------- | | `platformId` | number | Platform ID (see table below) | | `streamUrl` | string | Stream URL (required for some platforms) | | `streamKey` | string | Stream key (required for some platforms) | | `displayName` | string | Optional display name for the channel | | `rtmpUsername` | string | Optional RTMP auth username (Custom RTMP only) | | `rtmpPassword` | string | Optional RTMP auth password (Custom RTMP only) | | `instagramUsername` | string | Optional Instagram username (Instagram only) | ## Supported Platforms | Platform | `platformId` | `streamUrl` | `streamKey` | Additional | | -------- | ------------ | :---------: | :---------: | ---------- | | Custom RTMP | 29 | required | optional | `rtmpUsername`, `rtmpPassword` optional | | Facebook Group | 37 | — | required | | | Steam | 49 | required | required | | | Nimo | 60 | required | required | | | Naver | 61 | required | required | | | Mixcloud | 68 | — | required | | | Telegram | 72 | required | required | | | Instagram | 73 | — | required | `instagramUsername` optional; `displayName` ignored | | Amazon Live | 74 | required | required | | | Custom SRT | 78 | required | — | | | Substack | 79 | — | required | | | Mux | 80 | — | required | | | Custom WHIP | 81 | required | — | | | Custom HLS | 82 | required | — | | ## Response ```json { "id": 123456, "platformId": 29, "channelUrl": "https://example.com/live", "displayName": "My Custom RTMP" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "ChannelInvalidStreamKey", "name": "channel_invalid_stream_key" } } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "ChannelInvalidUrl", "name": "channel_invalid_url" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /channels/channel-delete.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Delete Channel Remove a user's channel :::info **Required scope:** channels.write ::: :::warning This method requires authentication ::: :::danger If the channel is used as a destination for any upcoming or in-progress event, deleting it will prevent the stream from being delivered to that destination. ::: ```bash curl -X DELETE \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/channels/123456 ``` ## Path Parameters | Parameter | Type | Description | | ----------- | ------ | ---------------------------- | | `channelId` | number | The ID of the channel to delete | ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "ChannelNotFound", "name": "channel_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /events.md --- # Restream Events API A Restream event is a scheduled or live broadcast with its own destinations, stream keys, and recordings. The Events API lets you create and configure events, fetch the keys needed to stream, track events through their lifecycle, and retrieve recordings afterward. ## Manage events * [Create Event](/events/event-create.md) — Schedule a new streaming event. * [Add Event Destination](/events/event-destination-add.md) — Attach a channel destination to an event. * [Delete Event Destination](/events/event-destination-delete.md) — Remove a destination from an event. * [Upcoming Events](/events/upcoming-events.md) — List scheduled events that haven't started. * [In Progress Events](/events/in-progress-events.md) — List events currently live. * [Events History](/events/events-history.md) — List past events. * [Event](/events/event.md) — Retrieve a single event by ID. ## Stream keys & recordings * [Event Stream Key](/events/event-stream-key.md) — Get the RTMP stream key for an event. * [Event SRT Stream Keys](/events/event-srt-stream-keys.md) — Get SRT stream keys for an event. * [Event Recordings](/events/events-recordings.md) — List recordings produced by an event. * [Recording Download URL](/events/events-recording-download-url.md) — Generate a download URL for a recording. * [Event Recording Transcriptions](/events/events-recording-transcriptions.md) — List transcriptions generated for an event's recordings. --- url: /events/event-create.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Create Event Create a new event for the authenticated user :::info **Required scope:** stream.write ::: :::warning This method requires authentication ::: Create an event that the user can stream to later. The created event has no destinations attached — use [Add Event Destination](/events/event-destination-add.md) to attach the channels you want to broadcast to. The `streamType` field selects the event's source. Pass `"studio"` or `"encoder"` for a live event, or `"file"` to play a file from the user's [Video Storage](/storage/storage-files.md). ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"streamType": "encoder", "title": "My weekly show", "description": "Episode 12", "scheduledFor": "2026-05-10T15:00:00Z"}' \ https://api.restream.io/v2/user/events/new ``` ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"streamType": "file", "fileId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "loopsCount": 3, "title": "Replay", "scheduledFor": "2026-05-10T15:00:00Z"}' \ https://api.restream.io/v2/user/events/new ``` ## Request Body | Field | Type | Description | | -------------- | ------ | ---------------------------------------------------------------------------------------------------- | | `streamType` | string | `"studio"`, `"encoder"`, or `"file"` (required). See below. | | `title` | string | Event title (optional). | | `description` | string | Event description (optional). | | `scheduledFor` | string | When the event is scheduled to start, ISO 8601 date-time (optional). | | `fileId` | string | Video Storage file UUID to play. Required when `streamType` is `"file"`; ignored otherwise. | | `loopsCount` | number | How many extra times to loop the file (`0`–`9`). Only for `"file"` events; omit or `0` plays once. | **`streamType`:** | Value | Description | | ----------- | -------------------------------------------------------------------- | | `studio` | Event will be streamed from Restream Studio. | | `encoder` | Event will be streamed from a third-party encoder. | | `file` | Event will play a file from the user's Video Storage. | :::info Looping (`loopsCount` greater than `0`) requires the looping feature on the user's plan. Without it, set `loopsCount` to `0` or omit it. ::: ## Response ```json { "id": "2527849f-f961-4b1d-8ae0-8eae4f068327" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "EventTitleTooLong", "name": "event_title_too_long" } } ``` ```json { "error": { "statusCode": 403, "status": 403, "code": 403, "message": "Insufficient scope: authorized scope is insufficient", "name": "insufficient_scope" } } ``` ```json { "error": { "statusCode": 403, "status": 403, "code": 403, "message": "EventLoopingNotAvailable", "name": "event_looping_not_available" } } ``` ```json { "error": { "statusCode": 409, "status": 409, "code": 409, "message": "EventLoopsCountLimitReached", "name": "event_loops_count_limit_reached" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StorageFileNotFound", "name": "storage_file_not_found" } } ``` --- url: /events/event-destination-add.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Add Event Destination Attach a channel as a destination to an existing event :::info **Required scope:** stream.write ::: :::warning This method requires authentication ::: Attach an existing channel to an event as a destination. Create the event first with [Create Event](/events/event-create.md), and the channel with [Add Channel](/channels/channel-add.md). To detach it again, use [Delete Event Destination](/events/event-destination-delete.md). ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"channelId": 12345, "streamingOrientation": "horizontal"}' \ https://api.restream.io/v2/user/events/{eventId}/destinations ``` ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"channelId": 12345, "title": "My weekly show", "description": "Episode 12", "privacyStatus": "public", "createEventPost": true, "latencyPreference": "low"}' \ https://api.restream.io/v2/user/events/{eventId}/destinations ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Request Body ### Common Fields These fields apply to every platform. | Field | Type | Required | Description | | ---------------------- | ------ | -------- | ----------------------------------------------------------------- | | `channelId` | number | Yes | ID of an existing channel that belongs to the authenticated user. | | `streamingOrientation` | string | No | `"horizontal"` or `"vertical"`. Defaults to `"horizontal"`. | ### Platform-Specific Fields Some platforms accept extra fields alongside the common ones. Which fields are read depends on the platform of the channel passed in `channelId`; fields that belong to a different platform are ignored. Platforms not listed below take no platform-specific fields. #### YouTube (platformId 5) On YouTube the destination is a YouTube broadcast, so its metadata is part of the request. | Field | Type | Required | Description | | --------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | | `title` | string | Yes | Broadcast title. Must be non-empty. | | `description` | string | No | Broadcast description. Empty when omitted. | | `privacyStatus` | string | No | `"private"`, `"public"`, or `"unlisted"`. Defaults to `"public"`. | | `videoCategoryId` | string | No | Video category ID for the broadcast. | | `latencyPreference` | string | No | `"normal"`, `"low"`, or `"ultraLow"`. Defaults to `"normal"`, and is forced to `"normal"` when `useEmbeddedClosedCaptions` is `true`. | | `useEmbeddedClosedCaptions` | boolean | No | Enable embedded closed captions. Disabled when omitted. | | `createEventPost` | boolean | No | Create the broadcast on YouTube when the destination is added, instead of when the stream starts. Disabled when omitted. | | `reuseExistingBroadcastId` | string | No | Attach to an existing YouTube broadcast instead of creating a new one. | | `enableMonetization` | boolean | No | Enable monetization for the broadcast. Disabled when omitted. Requires `adsFrequency`. | | `adsFrequency` | string | No | `"low"`, `"medium"`, or `"high"`. Required when `enableMonetization` is `true`. | #### Embed Player (platformId 83) | Field | Type | Required | Description | | ------- | ------ | -------- | ----------------------------- | | `title` | string | Yes | Display title for the player. | ## Supported Platforms The channel's platform determines whether it can be attached. | Platform | `platformId` | | ------------ | ------------ | | YouTube | 5 | | Custom RTMP | 29 | | Steam | 49 | | Nimo | 60 | | Naver | 61 | | Mixcloud | 68 | | Telegram | 72 | | Amazon Live | 74 | | Custom SRT | 78 | | Substack | 79 | | Mux | 80 | | Custom WHIP | 81 | | Custom HLS | 82 | | Embed Player | 83 | :::info Channels on other platforms (Facebook, Instagram, TikTok, LinkedIn, X) are not yet supported. ::: ## Response ```json { "id": "8f3b1c2a-1d4e-4a9b-9c7d-2e5f6a7b8c9d" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "This channel platform is not supported for event destinations yet", "name": "event_destination_platform_not_supported" } } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "adsFrequency: Ads frequency must be specified when monetization is enabled", "name": "invalid_body_params" } } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "EventContainsRestrictedSymbol", "name": "event_contains_restricted_symbol" } } ``` ```json { "error": { "statusCode": 409, "status": 409, "code": 409, "message": "EventDestinationAlreadyExists", "name": "event_destination_already_exists" } } ``` --- url: /events/event-destination-delete.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Delete Event Destination Remove a destination from a scheduled event :::info **Required scope:** stream.write ::: :::warning This method requires authentication ::: Remove a destination previously attached with [Add Event Destination](/events/event-destination-add.md). This only detaches the destination from the event — the channel itself is kept and can be attached again later. Use [Delete Channel](/channels/channel-delete.md) to remove the channel. Destinations can only be removed while the event is still scheduled. Deleting a destination of an event that is in progress or already finished is not allowed. ```bash curl -X DELETE \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/destinations/{destinationId} ``` ## Path Parameters | Parameter | Type | Description | | --------------- | ------------- | ------------------------------------------ | | `eventId` | string (UUID) | The UUID of the event | | `destinationId` | string (UUID) | The UUID of the destination to remove | ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "EventNotFound", "name": "event_not_found" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "EventDestinationNotFound", "name": "event_destination_not_found" } } ``` ```json { "error": { "statusCode": 409, "status": 409, "code": 409, "message": "EventInProgress", "name": "event_in_progress" } } ``` ```json { "error": { "statusCode": 409, "status": 409, "code": 409, "message": "EventFinished", "name": "event_finished" } } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "destinationId: Invalid UUID", "name": "invalid_path_params" } } ``` --- url: /events/upcoming-events.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Upcoming Events List of user's events :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve a list of the user's events. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/upcoming ``` **Query Parameters:** | Parameter | Type | Description | | ----------- | ------- | ----------------------------------------------------------------------- | | `source` | integer | Filter events by source type (`1` - Studio, `2` - Encoder, `3` - Video) | | `scheduled` | boolean | When `true`, returns only scheduled events | Example request: ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/events/upcoming?source=1&scheduled=true" ``` ## Response ```json [ { "id": "2527849f-f961-4b1d-8ae0-8eae4f068327", "status": "upcoming", "title": "Event title", "description": "Event description", "coverUrl": "URL or null", "scheduledFor": 1599983310, "startedAt": null, "finishedAt": null, "destinations": [ { "id": "8f14e45f-ceea-467a-9575-9a1f5a5b1a51", "channelId": 1, "externalUrl": "URL or null", "streamingPlatformId": 5 } ] }, ... ] ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /events/in-progress-events.md --- import { Tab, Tabs } from '@rspress/core/theme'; # In Progress Events List of user's in-progress events :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve a list of the user's in-progress events. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/in-progress ``` ## Response ```json [ { "id": "2527849f-f961-4b1d-8ae0-8eae4f068327", "status": "in-progress", "title": "Event title", "description": "Event description", "coverUrl": "URL or null", "isRecordOnly": false, "scheduledFor": 1599983310, "startedAt": 1599983310, "finishedAt": null, "destinations": [ { "id": "8f14e45f-ceea-467a-9575-9a1f5a5b1a51", "channelId": 1, "externalUrl": "URL or null", "streamingPlatformId": 5 } ] }, ... ] ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info `isRecordOnly` is `true` if the event is streamed in a record-only mode ::: --- url: /events/events-history.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Events History List of user's finished and missed events :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve a list of the user's finished and missed events. ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/events/history?page=1&limit=10" ``` ## Response ```json { "items": [ { "id": "2527849f-f961-4b1d-8ae0-8eae4f068327", "status": "finished", "title": "Event title", "description": "Event description", "coverUrl": "URL or null", "isRecordOnly": false, "scheduledFor": 1599983310, "startedAt": 1599983310, "finishedAt": 1599983310, "destinations": [ { "id": "8f14e45f-ceea-467a-9575-9a1f5a5b1a51", "channelId": 1, "externalUrl": "URL or null", "streamingPlatformId": 5 } ] }, ... ], "pagination": { "pages_total": 10, "page": 1, "limit": 10 } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info `status` can be `"finished"` or `"missed"`. `isRecordOnly` is `true` if the event was streamed in a record-only mode. ::: --- url: /events/event-chat-history.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event Chat History Chat history for an event :::info **Required scopes:** chat.read ::: :::warning This method requires authentication ::: Retrieve a paginated list of chat messages for a specific event, ordered chronologically and aggregated across every connected platform. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/chat/history ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | **Query Parameters:** | Parameter | Type | Description | | ----------- | ------- | ----------------------------------------------------------------------------------------------- | | `pageSize` | integer | Number of messages to return, from `1` to `1000` (optional) | | `pageToken` | string | Opaque cursor from a previous response's `nextPageToken` or `previousPageToken` (optional) | | `timestamp` | integer | Unix epoch seconds to seek to. Mutually exclusive with `pageToken` (optional) | Use `pageToken` to page through results, or `timestamp` to jump to a point in time — do not send both in the same request. Example request: ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/events/{eventId}/chat/history?pageSize=100&pageToken=[token]" ``` ## Response ```json { "messages": [ { "timestamp": "2024-07-13T14:35:00Z", "platform": "YouTube", "channelName": "channel-name", "author": "author-name", "text": "message text", "meta": null } ], "nextPageToken": "token-or-null", "previousPageToken": "token-or-null" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info `platform`, `channelName`, `text`, and `meta` may be `null`. `nextPageToken` and `previousPageToken` are `null` at the ends of the range. ::: --- url: /events/event.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event Get user event by id :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve a user event by id. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/2527849f-f961-4b1d-8ae0-8eae4f068327 ``` ## Response ```json { "id": "2527849f-f961-4b1d-8ae0-8eae4f068327", "status": "upcoming | in-progress | finished", "title": "Event title", "description": "Event description", "coverUrl": "URL or null", "isRecordOnly": false, "scheduledFor": 1599983310, "startedAt": 1599983310, "finishedAt": 1599983310, "destinations": [ { "id": "8f14e45f-ceea-467a-9575-9a1f5a5b1a51", "channelId": 1, "externalUrl": "URL or null", "streamingPlatformId": 5 } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info `isRecordOnly` is `true` if the event is/was streamed in a record-only mode. Timestamps are in seconds and can be `null`. ::: --- url: /events/event-stream-key.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event Stream Key Get stream key and SRT url for user's event by id :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve a stream key and SRT url by event id. If user does not have access to SRT streaming - SRT url will have a NULL value in the response. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/2527849f-f961-4b1d-8ae0-8eae4f068327/streamKey ``` ## Response ```json { "streamKey": "re_xxx_xxx", "srtUrl": "srt://live.restream.io:2010?streamid=srt_xxx_xxx_xxx&passphrase=re_xxx_xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /events/event-srt-stream-keys.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event SRT Stream Keys Get SRT stream keys and url for user's event by id :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: :::warning SRT is available on Business and custom Enterprise plans only. ::: Retrieve SRT stream keys and url by event id. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/2527849f-f961-4b1d-8ae0-8eae4f068327/srt/streamKey ``` ## Response ```json { "streamId": "srt_xxx_xxx_xxx", "passPhrase": "re_xxx_xxx", "url": "srt://live.restream.io:2010?streamid=srt_xxx_xxx_xxx&passphrase=re_xxx_xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /events/events-recordings.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event Recordings List recordings for an event :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Retrieve a list of recordings for a specific event, categorized by type: primary videos, secondary videos, and audio files. * **primaryVideos** — the main recording. This is typically a landscape video, but if the stream was portrait-only, the primary video will be portrait. * **secondaryVideos** — a portrait recording, only present when streaming in both landscape and portrait simultaneously. * **audio** — the audio track of the recording. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/recordings ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Response ```json { "primaryVideos": [ { "fileName": "video-2026-02-26-22-01-19.mp4", "expiresAt": "2026-03-08T22:01:19.000Z" } ], "secondaryVideos": [ { "fileName": "video-1772143281190-2026-02-26-22-01-22.mp4", "expiresAt": "2026-03-08T22:01:19.000Z" } ], "audio": [ { "fileName": "audio-2026-02-26-22-01-19.m4a", "expiresAt": "2026-03-08T22:01:19.000Z" } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /events/events-recording-download-url.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Recording Download URL Generate a download URL for a recording file :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Generate a temporary download URL for a specific recording file. Use the `fileName` from the event recordings endpoint to request a download URL. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"fileName": "video-2026-02-26-22-01-19.mp4"}' \ https://api.restream.io/v2/user/events/{eventId}/recordings/download-url ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Request Body | Parameter | Type | Description | | ---------- | ------ | ----------------------------------------------------------- | | `fileName` | string | The file name from the event recordings response (required) | ```json { "fileName": "video-2026-02-26-22-01-19.mp4" } ``` ## Response ```json { "downloadUrl": "https://example.com/recording-download" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "RecordingsFileNotFound", "name": "recordings_file_not_found" } } ``` --- url: /events/events-chat-history-download-url.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Chat History Download URL Generate a download URL for an event's chat history :::info **Required scope:** chat.read ::: :::warning This method requires authentication ::: Generate a one-time URL to download an event's full chat history export. The request takes no body. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/chat/history/download-url ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Response The returned `downloadUrl` is a short-lived, tokenized link. Fetch it directly to download the chat history export — no `Authorization` header is required on that URL. ```json { "downloadUrl": "https://api.restream.io/protected/chat/export?token=xxx" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "ChatHistoryNotFound", "name": "chat_history_not_found" } } ``` --- url: /events/events-recording-transcriptions.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Event Recording Transcriptions List transcriptions for an event's recordings :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Retrieve a list of transcriptions generated for the recordings of a specific event. Each transcription has a `status` describing where it is in processing: * **InProgress** — the transcription is queued or currently being generated. * **Completed** — the transcription is ready and `downloadUrl` points to the transcript file. * **Failed** — the transcription could not be generated, or it has expired and is no longer available. * **Unknown** — the status could not be determined. `downloadUrl` is `null` until the transcription is `Completed`. Transcriptions are not stored indefinitely — download the file while it is available; an expired transcription is reported as `Failed`. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/recordings/transcriptions ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Response ```json { "transcriptions": [ { "id": "7f3c9a4e-1b2d-4c5f-8a6b-9d0e1f2a3b4c", "fileName": "video-2026-02-26-22-01-19.mp4", "status": "Completed", "downloadUrl": "https://storage.restream.io/transcriptions/7f3c9a4e-1b2d-4c5f-8a6b-9d0e1f2a3b4c.txt?token=..." }, { "id": "2a1b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "fileName": "video-1772143281190-2026-02-26-22-01-22.mp4", "status": "InProgress", "downloadUrl": null } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /analytics.md --- # Restream Live Stream Analytics API The Analytics API returns performance metrics for a live stream event. Pull viewer numbers and chat-engagement data as combined totals and per-channel breakdowns, each available as minute-by-minute time series. ## Endpoints * [Viewers](/analytics/event-analytics-viewers.md) — Viewer-count analytics for an event, total and per channel. * [Chat Messages](/analytics/event-analytics-messages.md) — Chat message and unique-chatter analytics for an event. --- url: /analytics/event-analytics-viewers.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Live Stream Analytics - Viewers Get viewer analytics for an event :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve viewer analytics for a specific event, both as a combined total and broken down per channel. * **total** — aggregated viewer metrics across all channels for the event. * **byChannel** — the same metrics keyed by channel id, with one entry per channel the event streamed to. Each section reports: * **mean** — average concurrent viewers. * **max** — peak concurrent viewers. * **viewsTotal** — total number of views. * **peakTime** — Unix timestamp (seconds) when peak viewers was reached. * **watchedTime** — total watched time in seconds. * **viewersPerMinute** — time series of concurrent viewers, one point per minute (`timestamp` in seconds). ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/analytics/viewers ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Response ```json { "total": { "mean": 42, "max": 87, "viewsTotal": 1530, "peakTime": 1599983310, "watchedTime": 372840, "viewersPerMinute": [ { "timestamp": 1599983310, "viewers": 35 }, { "timestamp": 1599983370, "viewers": 41 } ] }, "byChannel": { "123456": { "mean": 28, "max": 60, "viewsTotal": 980, "peakTime": 1599983310, "watchedTime": 248560, "viewersPerMinute": [ { "timestamp": 1599983310, "viewers": 22 }, { "timestamp": 1599983370, "viewers": 27 } ] } } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info Returns `404` when the event has no analytics — for example, an event that never received an incoming stream. ::: --- url: /analytics/event-analytics-messages.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Live Stream Analytics - Chat Messages Get chat message analytics for an event :::info **Required scopes:** stream.read ::: :::warning This method requires authentication ::: Retrieve chat message analytics for a specific event, both as a combined total and broken down per channel. * **total** — aggregated chat metrics across all channels for the event. * **byChannel** — the same metrics keyed by channel id, with one entry per channel the event streamed to. Each section reports: * **messagesTotal** — total number of chat messages. * **chattersTotal** — total number of unique chatters. * **messagesPerMinute** — time series of message counts, one point per minute (`timestamp` in seconds). ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/events/{eventId}/analytics/messages ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------------- | -------------------------------- | | `eventId` | string (UUID) | The UUID of the event (required) | ## Response ```json { "total": { "messagesTotal": 1840, "chattersTotal": 312, "messagesPerMinute": [ { "timestamp": 1599983310, "messages": 24 }, { "timestamp": 1599983370, "messages": 31 } ] }, "byChannel": { "123456": { "messagesTotal": 1120, "chattersTotal": 198, "messagesPerMinute": [ { "timestamp": 1599983310, "messages": 15 }, { "timestamp": 1599983370, "messages": 19 } ] } } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` :::info Returns `404` when the event has no analytics — for example, an event that never received an incoming stream. ::: --- url: /storage.md --- # Restream Storage API The Storage API gives access to files saved in a Restream account's storage, such as recordings. List the files you have stored, inspect a single file's metadata, and generate a temporary URL to download its contents. ## Endpoints * [Storage Files](/storage/storage-files.md) — List all files in the account's storage. * [Storage File](/storage/storage-file.md) — Retrieve metadata for a single stored file. * [Storage File Download URL](/storage/storage-file-download-url.md) — Generate a temporary download URL for a file. --- url: /storage/storage-files.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Storage Files List user's storage files :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Retrieve a list of all files in the user's video storage. The `status` field indicates the current processing state of the file: * **Processing** — file is being uploaded, analyzed, transcoded, or imported * **Ready** — file is ready for use * **Failed** — file processing failed `sizeBytes` and `durationSeconds` may be `null` while the file is still being processed. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/storage/files ``` ## Response ```json { "files": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "My stream recording.mp4", "sizeBytes": 104857600, "durationSeconds": 3600, "status": "Ready", "createdAt": "2026-02-26T22:01:19.000Z" }, { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "title": "Intro video.mp4", "sizeBytes": null, "durationSeconds": null, "status": "Processing", "createdAt": "2026-02-20T10:30:00.000Z" } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /storage/storage-file.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Storage File Get a single storage file :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Retrieve details of a specific file in the user's video storage. See [Storage Files](/storage/storage-files.md) for the list of possible `status` values. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/storage/files/{fileId} ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------ | ------------------------------------- | | `fileId` | string | The ID of the storage file (required) | ## Response ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "My stream recording.mp4", "sizeBytes": 104857600, "durationSeconds": 3600, "status": "Ready", "createdAt": "2026-02-26T22:01:19.000Z" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StorageFileNotFound", "name": "storage_file_not_found" } } ``` --- url: /storage/storage-file-download-url.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Storage File Download URL Generate a download URL for a storage file :::info **Required scope:** storage.read ::: :::warning This method requires authentication ::: Generate a temporary download URL for a specific file in the user's video storage. ```bash curl -X POST -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/storage/files/{fileId}/download-url ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------ | ------------------------------------- | | `fileId` | string | The ID of the storage file (required) | ## Response ```json { "downloadUrl": "https://example.com/storage-file-download" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StorageFileNotFound", "name": "storage_file_not_found" } } ``` --- url: /clips.md --- # Restream Clips API The Clips API turns long streams into short, shareable clips. Browse your clip projects, open a project to see every generated clip along with its virality score and posting history, and download individual clips for reuse. ## Endpoints * [Clip Projects](/clips/clips-projects.md) — List all clip projects in the account. * [Project Details](/clips/clips-project-details.md) — Retrieve a clip project with all its clips and posting history. * [Download Clip](/clips/clips-download.md) — Generate a temporary download URL for a clip. --- url: /clips/clips-project-create.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Create Project Generate clips from a video source :::info **Required scope:** clips.write ::: :::warning This method requires authentication ::: Start generating clips from a source — a [Video Storage](/storage/storage-files.md) file, a past [event's](/events/events-history.md) recording, or a public video URL. Restream's AI analyzes the source and produces clips asynchronously — the response returns immediately. Poll [Project Details](/clips/clips-project-details.md) to retrieve clips as they become available; `generatingClipsNow` indicates whether more are still being generated. Each source has one clip project. The first call creates it; later calls for the same source add clips to that project instead of starting a new one. What gets processed depends on whether you pass `selectedTimeRange`: * **Without `selectedTimeRange`** — Restream clips the whole video. If it was already clipped this way, the request returns `409` `ClipsInputAlreadyProcessed` instead of clipping again. That's a no-op, not a real failure, so a repeated call is harmless. To clip the whole video again from scratch, set `forceReprocess: true`. * **With `selectedTimeRange`** — Restream clips only that span and adds the results to the project. Call again with a different range to clip another part of the same video. The video must be at least **10 seconds** long, whether you clip all of it or a `selectedTimeRange`. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"sourceType": "VideoStorageFile", "videoStorageFileId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "My webinar"}' \ https://api.restream.io/v2/user/clips/projects ``` ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"sourceType": "PublicUrl", "publicUrl": "https://example.com/video.mp4", "title": "My public video"}' \ https://api.restream.io/v2/user/clips/projects ``` ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"sourceType": "Event", "eventId": "2527849f-f961-4b1d-8ae0-8eae4f068327", "title": "My live stream"}' \ https://api.restream.io/v2/user/clips/projects ``` ## Request Body | Field | Type | Description | | -------------------- | ------- | ------------------------------------------------------------------------------------------------- | | `sourceType` | string | Type of video source. Use `"VideoStorageFile"` for account files, `"PublicUrl"` for a directly downloadable public video URL, or `"Event"` for a past event's recording (required). | | `videoStorageFileId` | string | UUID of the [Video Storage](/storage/storage-files.md) file to clip. Required when `sourceType` is `"VideoStorageFile"`. | | `publicUrl` | string | Directly downloadable public video URL. Required when `sourceType` is `"PublicUrl"`. | | `eventId` | string | UUID of the event whose recording to clip. Required when `sourceType` is `"Event"`. Get it from [Events History](/events/events-history.md) or the other event-listing endpoints. | | `title` | string | Project title (optional). | | `singleClipMode` | boolean | Turn the whole video into one clip instead of letting AI pick highlights (optional). See below. | | `selectedTimeRange` | object | Limit clip generation to a portion of the video (optional). See below. | | `forceReprocess` | boolean | Clip the whole video again even if it was already processed (optional). Only applies when `selectedTimeRange` is omitted; ignored otherwise. | **`singleClipMode`:** By default Restream's AI scans the video, picks the most engaging moments, and produces several clips. With `singleClipMode: true` it skips that step and turns the video into a single clip as-is. Use `selectedTimeRange` to pick which part becomes the clip, or omit it to use the whole video. The clip is capped at **10 minutes**; anything longer fails with `400` `InvalidBodyParams`. **`selectedTimeRange`:** | Field | Type | Description | | -------------------- | ------ | -------------------------------------------------------------------- | | `startOffsetSeconds` | number | Start of the range in seconds from the beginning (`0` or greater). | | `endOffsetSeconds` | number | End of the range in seconds from the beginning. Must not exceed the file's duration. | ## Response ```json { "projectId": "31a1e161-67aa-4a9e-a95b-3cb37ab6ea89", "sourceTypeName": "VideoStorageFile", "sourceIdentifier": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "title": "My webinar", "createdAt": "2026-01-28T15:50:30.420Z" } ``` ```json { "error": { "statusCode": 400, "status": 400, "code": 400, "message": "InvalidBodyParams", "name": "invalid_body_params" } } ``` ```json { "error": { "statusCode": 403, "status": 403, "code": 403, "message": "Insufficient scope: authorized scope is insufficient", "name": "insufficient_scope" } } ``` ```json { "error": { "statusCode": 403, "status": 403, "code": 403, "message": "ClipsFeatureNotActive", "name": "clips_feature_not_active" } } ``` ```json { "error": { "statusCode": 403, "status": 403, "code": 403, "message": "ClipsUsageLimitReached", "name": "clips_usage_limit_reached" } } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StorageFileNotFound", "name": "storage_file_not_found" } } ``` ```json { "error": { "statusCode": 409, "status": 409, "code": 409, "message": "ClipsInputAlreadyProcessed", "name": "clips_input_already_processed" } } ``` --- url: /clips/clips-projects.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Clip Projects List user's clip projects :::info **Required scope:** clips.read ::: :::warning This method requires authentication ::: Retrieve a paginated list of the user's clip projects. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/clips/projects ``` **Query Parameters:** | Parameter | Type | Description | | --------- | ------ | --------------------------------------------------------------------------- | | `limit` | number | Maximum number of projects to return (max `100`) | | `cursor` | string | Cursor for pagination. Use `nextCursor` from the previous response | | `sortBy` | string | Sort order for results: `CreatedAt` or `LastActivity` (default: `CreatedAt`) | All query parameters are optional. Example request: ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/clips/projects?limit=1&sortBy=LastActivity" ``` ## Response ```json { "projects": [ { "projectId": "31a1e161-67aa-4a9e-a95b-3cb37ab6ea89", "sourceTypeName": "Event", "sourceIdentifier": "61c2559a-2acf-4420-ac01-78786d06da5c", "title": "My first live stream", "clipsCount": 3, "clipThumbnails": [ "https://example.com/thumbnail1.jpg", "https://example.com/thumbnail2.jpg", "https://example.com/thumbnail3.jpg" ], "generatingClipsNow": false, "createdAt": "2026-01-28T15:50:30.420Z" } ], "pagination": { "nextCursor": "eyJzb3J0VmFsdWUi...", "hasMore": true } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /clips/clips-project-details.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Project Details Get a single clip project with its clips :::info **Required scope:** clips.read ::: :::warning This method requires authentication ::: Retrieve a clip project by its ID, including all generated clips and their posting history. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/clips/projects/{projectId} ``` **Path Parameters:** | Parameter | Type | Description | | ----------- | ------ | -------------------------- | | `projectId` | string | The ID of the clip project | ## Response ```json { "generatingClipsNow": false, "clips": [ { "clipId": "6ece2c7f-6fc5-4545-8cec-37db6a79a0e0", "name": "Clip title", "viralityScore": 84, "viralityScoreExplanation": "Virality score explanation text", "transcriptText": "Transcript of the clip content", "sourceStartSeconds": 411, "sourceEndSeconds": 469, "thumbnailUrl": "https://example.com/thumbnail.jpg", "favouritedAt": null, "createdAt": "2026-01-28T15:56:37.452Z" } ], "postedClips": [ { "clipId": "6ece2c7f-6fc5-4545-8cec-37db6a79a0e0", "platform": "YouTube", "channelId": 16034701, "status": "Published", "createdAt": "2026-02-06T18:11:53.057Z", "platformUrl": "https://example.com/video" } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /clips/clips-download.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Download Clip Get a download URL for a clip :::info **Required scope:** clips.read ::: :::warning This method requires authentication ::: Retrieve a temporary download URL for a specific clip. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/clips/{clipId}/download ``` **Path Parameters:** | Parameter | Type | Description | | --------- | ------ | ------------------ | | `clipId` | string | The ID of the clip | ## Response ```json { "downloadUrl": "https://example.com/clip-download" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio.md --- # Restream Studio API Restream Studio is the in-browser live production tool. Its API lets you manage the assets that customize a broadcast — background audio, brand overlays, captions, fonts, QR codes, and scrolling tickers — so you can automate and template your shows. ## Asset types * [Audio](/studio/audio.md) — Countdown music and background audio tracks. * [Brands](/studio/brands.md) — Brand overlays available in Studio. * [Captions](/studio/captions.md) — Create, update, and manage on-screen captions. * [Fonts](/studio/fonts.md) — Fonts available for Studio text. * [QR Codes](/studio/qr-codes.md) — Create and manage on-screen QR codes. * [Tickers](/studio/tickers.md) — Create and manage scrolling tickers. --- url: /studio/audio.md --- # Restream Studio Audio API Studio audio assets let you set the mood of a broadcast with background tracks and add anticipation with countdown music before you go live. Use these endpoints to list what's available to your account. ## Endpoints * [List Countdown Music](/studio/audio/studio-audio-countdown-music.md) — Audio tracks for pre-stream countdowns. * [List Audio Backgrounds](/studio/audio/studio-audio-backgrounds.md) — Background music tracks for broadcasts. --- url: /studio/audio/studio-audio-countdown-music.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Countdown Music List user's countdown music tracks :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of audio tracks available to the authenticated user for use as countdown music in Studio. Each track ships one or more `sources` (encoded variants of the same audio), so consumers can pick the codec that suits them. Tracks can optionally be linked to a [brand](/studio/brands/studio-brands.md) via the `brandId` field. When `brandId` is `null`, the track is part of the default catalog and available to all of the user's brands. Default catalog entries are flagged with `isDefault: true`. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/audio/countdown-music ``` ## Response ```json { "tracks": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "filename": "countdown-uplift.m4a", "brandId": null, "isDefault": true, "sources": [ { "url": "https://studio-assets.restream.io/audio/countdown/uplift.m4a", "duration": 60, "bitrate": 192000, "codec": "Aac" }, { "url": "https://studio-assets.restream.io/audio/countdown/uplift.opus", "duration": 60, "bitrate": 128000, "codec": "Opus" } ] }, { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "filename": "brand-intro.mp3", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "isDefault": false, "sources": [ { "url": "https://studio-assets.restream.io/audio/user/brand-intro.mp3", "duration": 45, "bitrate": 256000, "codec": "Mpeg" } ] } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/audio/studio-audio-backgrounds.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Audio Backgrounds List the audio background catalog :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve the catalog of audio background channels available to the authenticated user in Studio. The catalog is organized as **categories → groups → channels**, mirroring how it is presented in the Studio UI. A `channel` is the smallest addressable unit — pass its `id` to the Studio playback APIs to start streaming. `emoji` and `icon` are decorative hints for rendering the channel; either may be `null`. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/audio-backgrounds ``` ## Response ```json { "backgrounds": [ { "id": 1, "name": "Focus", "groups": [ { "id": 11, "name": "Lo-fi", "channels": [ { "id": 101, "name": "Lo-fi Beats", "emoji": "🎧", "icon": "https://studio-assets.restream.io/audio-backgrounds/lofi-beats.png" }, { "id": 102, "name": "Late Night Study", "emoji": null, "icon": "https://studio-assets.restream.io/audio-backgrounds/late-night.png" } ] } ] }, { "id": 2, "name": "Energy", "groups": [ { "id": 21, "name": "Workout", "channels": [ { "id": 201, "name": "Power Hour", "emoji": "💪", "icon": null } ] } ] } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/brands.md --- # Restream Studio Brands API Brands let you keep a consistent look across every broadcast with reusable overlays. Use this endpoint to list the brands available to your Studio account. ## Endpoints * [List Brands](/studio/brands/studio-brands.md) — List the brand overlays available in Studio. --- url: /studio/brands/studio-brands.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Brands List user's studio brands :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of all studio brands belonging to the authenticated user. Brands are used to organize and group studio content such as [captions](/studio/captions/studio-captions.md), [tickers](/studio/tickers/studio-tickers.md), and [QR codes](/studio/qr-codes/studio-qr-codes.md). ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/brands ``` ## Response ```json { "brands": [ { "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "name": "My Brand" }, { "id": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "name": "Gaming Stream" } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/captions.md --- # Restream Studio Captions API Captions are the lower-third text overlays you display during a broadcast. The Captions API lets you list, read, create, update, and delete the captions in your Studio account. ## Endpoints * [List Captions](/studio/captions/studio-captions.md) — List all captions. * [Get Caption](/studio/captions/studio-caption.md) — Retrieve a single caption by ID. * [Create Caption](/studio/captions/studio-caption-create.md) — Add a new caption. * [Update Caption](/studio/captions/studio-caption-update.md) — Modify an existing caption. * [Delete Caption](/studio/captions/studio-caption-delete.md) — Remove a caption. --- url: /studio/captions/studio-captions.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Captions List user's studio captions :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of all studio captions belonging to the authenticated user. Captions can optionally be linked to a [brand](/studio/brands/studio-brands.md) via the `brandId` field. When `brandId` is `null`, the caption is not associated with any brand. ## Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `brandId` | string (UUID) | Optional. Filter captions by brand ID. When provided, only captions belonging to the specified brand are returned. | ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/captions ``` ### Filter by brand ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/studio/captions?brandId=f47ac10b-58cc-4372-a567-0e02b2c3d479" ``` ## Response ```json { "captions": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "text": "Welcome to the stream!", "secondaryText": "Follow for more content", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" }, { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "text": "Thanks for watching!", "secondaryText": null, "brandId": null } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/captions/studio-caption.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Get Caption Fetch a user's studio caption by ID :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a specific studio caption owned by the authenticated user. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/captions/{captionId} ``` ## Path Parameters | Parameter | Type | Description | | ----------- | ------------- | --------------------------------- | | `captionId` | string (UUID) | The ID of the caption to retrieve | ## Response ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "text": "Welcome to the stream!", "secondaryText": "Follow for more content", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioCaptionNotFound", "name": "studio_caption_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/captions/studio-caption-create.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Create Caption Add a new studio caption :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Create a studio caption owned by the authenticated user. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"text": "Welcome to the stream!", "secondaryText": "Follow for more content", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"}' \ https://api.restream.io/v2/user/studio/captions ``` ## Request Body | Field | Type | Description | | --------------- | ------ | -------------------------------------------- | | `text` | string | Caption text (required) | | `secondaryText` | string | Secondary caption text (optional) | | `brandId` | string | Brand ID to associate with the caption (required) | ```json { "text": "Welcome to the stream!", "secondaryText": "Follow for more content", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ## Response ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "text": "Welcome to the stream!", "secondaryText": "Follow for more content", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/captions/studio-caption-update.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Update Caption Update a user's studio caption :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Update the text or secondary text of a studio caption owned by the authenticated user. ```bash curl -X PATCH \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"text": "Updated caption text", "secondaryText": "Updated secondary text"}' \ https://api.restream.io/v2/user/studio/captions/{captionId} ``` ## Path Parameters | Parameter | Type | Description | | ----------- | ------------- | ------------------------------- | | `captionId` | string (UUID) | The ID of the caption to update | ## Request Body | Field | Type | Description | | --------------- | -------------- | --------------------------------------------------------------------------- | | `text` | string | Caption text (optional) | | `secondaryText` | string | Secondary caption text (optional) | ```json { "text": "Updated caption text", "secondaryText": "Updated secondary text" } ``` ## Response ```json { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "text": "Updated caption text", "secondaryText": "Updated secondary text", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "NotFound", "name": "not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/captions/studio-caption-delete.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Delete Caption Remove a user's studio caption :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Permanently delete a studio caption owned by the authenticated user. ```bash curl -X DELETE \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/captions/{captionId} ``` ## Path Parameters | Parameter | Type | Description | | ----------- | ------------- | ------------------------------- | | `captionId` | string (UUID) | The ID of the caption to delete | ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "NotFound", "name": "not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/fonts.md --- # Restream Studio Fonts API Fonts control the typography of captions, tickers, and other on-screen text in Studio. Use this endpoint to list the fonts available to your account. ## Endpoints * [List Fonts](/studio/fonts/studio-fonts.md) — List the fonts available in Studio. --- url: /studio/fonts/studio-fonts.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Fonts List user's studio fonts :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of studio fonts available to the authenticated user. Each font exposes one or more `variants` (weight + style combinations) and `previews` rendered by Studio. Fonts can optionally be linked to a [brand](/studio/brands/studio-brands.md) via the `brandId` field. When `brandId` is `null`, the font is part of the default catalog and available to all of the user's brands. Default catalog entries are flagged with `isDefault: true`. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/fonts ``` ## Response ```json { "fonts": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "filename": "Inter-Regular.woff2", "family": "Inter", "brandId": null, "isDefault": true, "variants": [ { "weight": 400, "style": "Normal", "localNames": ["Inter Regular", "Inter-Regular"], "sources": [ { "url": "https://studio-assets.restream.io/fonts/inter/inter-regular.woff2", "format": "Woff2" } ] }, { "weight": 700, "style": "Normal", "localNames": ["Inter Bold", "Inter-Bold"], "sources": [ { "url": "https://studio-assets.restream.io/fonts/inter/inter-bold.woff2", "format": "Woff2" } ] } ], "previews": [ { "orientation": "Landscape", "url": "https://studio-assets.restream.io/fonts/inter/preview-landscape.png", "width": 640, "height": 160 } ] }, { "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "filename": "BrandSans.ttf", "family": "Brand Sans", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "isDefault": false, "variants": [ { "weight": 400, "style": "Normal", "localNames": [], "sources": [ { "url": "https://studio-assets.restream.io/fonts/user/brand-sans.ttf", "format": "Ttf" } ] } ], "previews": [] } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes.md --- # Restream Studio QR Codes API QR codes let viewers scan their way to a link without typing a URL. The QR Codes API lets you list, read, create, update, delete, and reorder the QR codes shown during a Studio broadcast. ## Endpoints * [List QR Codes](/studio/qr-codes/studio-qr-codes.md) — List all QR codes. * [Get QR Code](/studio/qr-codes/studio-qr-code.md) — Retrieve a single QR code by ID. * [Create QR Code](/studio/qr-codes/studio-qr-code-create.md) — Add a new QR code. * [Update QR Code](/studio/qr-codes/studio-qr-code-update.md) — Modify an existing QR code. * [Delete QR Code](/studio/qr-codes/studio-qr-code-delete.md) — Remove a QR code. * [Reorder QR Codes](/studio/qr-codes/studio-qr-code-reorder.md) — Change the display order of QR codes. --- url: /studio/qr-codes/studio-qr-codes.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List QR Codes List user's studio QR codes :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of all studio QR codes belonging to the authenticated user. QR codes are linked to a [brand](/studio/brands/studio-brands.md) via the `brandId` field. ## Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `brandId` | string (UUID) | Optional. Filter QR codes by brand ID. When provided, only QR codes belonging to the specified brand are returned. | ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/qr-codes ``` ### Filter by brand ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/studio/qr-codes?brandId=f47ac10b-58cc-4372-a567-0e02b2c3d479" ``` ## Response ```json { "qrCodes": [ { "id": "e5f6a7b8-c9d0-1234-ef56-789012345678", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "My Website", "link": "https://example.com", "shouldShowTitle": true }, { "id": "f6a7b8c9-d0e1-2345-f678-901234567890", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "Donate", "link": "https://donate.example.com", "shouldShowTitle": false } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes/studio-qr-code.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Get QR Code Fetch a user's studio QR code by ID :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a specific studio QR code owned by the authenticated user. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/qr-codes/{qrCodeId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | -------------------------------- | | `qrCodeId` | string (UUID) | The ID of the QR code to retrieve | ## Response ```json { "id": "e5f6a7b8-c9d0-1234-ef56-789012345678", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "My Website", "link": "https://example.com", "shouldShowTitle": true } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioQrCodeNotFound", "name": "studio_qr_code_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes/studio-qr-code-create.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Create QR Code Add a new studio QR code :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Create a studio QR code owned by the authenticated user. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "My Website", "link": "https://example.com", "shouldShowTitle": true}' \ https://api.restream.io/v2/user/studio/qr-codes ``` ## Request Body | Field | Type | Description | | ----------------- | ------- | ------------------------------------------------ | | `brandId` | string | Brand ID associated with the QR code (required) | | `title` | string | QR code title (required) | | `link` | string | URL to encode in the QR code (required) | | `shouldShowTitle` | boolean | Whether to display the title on screen (optional)| ```json { "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "My Website", "link": "https://example.com", "shouldShowTitle": true } ``` ## Response ```json { "id": "e5f6a7b8-c9d0-1234-ef56-789012345678", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "My Website", "link": "https://example.com", "shouldShowTitle": true } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes/studio-qr-code-update.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Update QR Code Update a user's studio QR code :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Update the title, link, or display settings of a studio QR code owned by the authenticated user. ```bash curl -X PATCH \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"title": "Updated Title", "link": "https://example.com/new"}' \ https://api.restream.io/v2/user/studio/qr-codes/{qrCodeId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | ------------------------------- | | `qrCodeId` | string (UUID) | The ID of the QR code to update | ## Request Body | Field | Type | Description | | ----------------- | ------- | ------------------------------------------------ | | `title` | string | QR code title (optional) | | `link` | string | QR code URL (optional) | | `shouldShowTitle` | boolean | Whether to display the title on screen (optional)| ```json { "title": "Updated Title", "link": "https://example.com/new", "shouldShowTitle": true } ``` ## Response ```json { "id": "e5f6a7b8-c9d0-1234-ef56-789012345678", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "title": "Updated Title", "link": "https://example.com/new", "shouldShowTitle": true } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioQrCodeNotFound", "name": "studio_qr_code_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes/studio-qr-code-delete.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Delete QR Code Remove a user's studio QR code :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Permanently delete a studio QR code owned by the authenticated user. ```bash curl -X DELETE \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/qr-codes/{qrCodeId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | ------------------------------- | | `qrCodeId` | string (UUID) | The ID of the QR code to delete | ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioQrCodeNotFound", "name": "studio_qr_code_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/qr-codes/studio-qr-code-reorder.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Reorder QR Codes Update the order of a user's studio QR codes :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Set a custom order for the authenticated user's QR codes by providing an array of QR code IDs. ```bash curl -X PATCH \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"ids": ["e5f6a7b8-c9d0-1234-ef56-789012345678", "f6a7b8c9-d0e1-2345-f678-901234567890"]}' \ https://api.restream.io/v2/user/studio/qr-codes/order ``` ## Request Body | Field | Type | Description | | ----- | ----------------- | ---------------------------------------- | | `ids` | array of strings | Ordered list of QR code IDs (required) | ```json { "ids": [ "e5f6a7b8-c9d0-1234-ef56-789012345678", "f6a7b8c9-d0e1-2345-f678-901234567890" ] } ``` ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers.md --- # Restream Studio Tickers API Tickers are the scrolling text bars that run across a broadcast to share announcements, scores, or social messages. The Tickers API lets you list, read, create, update, delete, and reorder the tickers in your Studio account. ## Endpoints * [List Tickers](/studio/tickers/studio-tickers.md) — List all tickers. * [Get Ticker](/studio/tickers/studio-ticker.md) — Retrieve a single ticker by ID. * [Create Ticker](/studio/tickers/studio-ticker-create.md) — Add a new ticker. * [Update Ticker](/studio/tickers/studio-ticker-update.md) — Modify an existing ticker. * [Delete Ticker](/studio/tickers/studio-ticker-delete.md) — Remove a ticker. * [Reorder Tickers](/studio/tickers/studio-ticker-reorder.md) — Change the display order of tickers. --- url: /studio/tickers/studio-tickers.md --- import { Tab, Tabs } from '@rspress/core/theme'; # List Tickers List user's studio tickers :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a list of all studio tickers belonging to the authenticated user. Tickers can optionally be linked to a [brand](/studio/brands/studio-brands.md) via the `brandId` field. When `brandId` is `null`, the ticker is not associated with any brand. ## Query Parameters | Parameter | Type | Description | |-----------|------|-------------| | `brandId` | string (UUID) | Optional. Filter tickers by brand ID. When provided, only tickers belonging to the specified brand are returned. | ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/tickers ``` ### Filter by brand ```bash curl -H "Authorization: Bearer [access token]" \ "https://api.restream.io/v2/user/studio/tickers?brandId=f47ac10b-58cc-4372-a567-0e02b2c3d479" ``` ## Response ```json { "tickers": [ { "id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "text": "Follow us on Twitter @restream", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" }, { "id": "d4e5f6a7-b8c9-0123-def4-567890123456", "text": "Subscribe for more content!", "brandId": null } ] } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers/studio-ticker.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Get Ticker Fetch a user's studio ticker by ID :::info **Required scope:** studio.read ::: :::warning This method requires authentication ::: Retrieve a specific studio ticker owned by the authenticated user. ```bash curl -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/tickers/{tickerId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | ------------------------------- | | `tickerId` | string (UUID) | The ID of the ticker to retrieve | ## Response ```json { "id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "text": "Follow us on Twitter @restream", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioTickerNotFound", "name": "studio_ticker_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers/studio-ticker-create.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Create Ticker Add a new studio ticker :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Create a studio ticker owned by the authenticated user. ```bash curl -X POST \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"text": "Subscribe for more content!", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"}' \ https://api.restream.io/v2/user/studio/tickers ``` ## Request Body | Field | Type | Description | | --------- | ------ | ---------------------------------------- | | `text` | string | Ticker text (required) | | `brandId` | string | Brand ID to associate with the ticker (required) | ```json { "text": "Subscribe for more content!", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ## Response ```json { "id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "text": "Subscribe for more content!", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers/studio-ticker-update.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Update Ticker Update a user's studio ticker :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Update the text of a studio ticker owned by the authenticated user. ```bash curl -X PATCH \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"text": "Updated ticker message"}' \ https://api.restream.io/v2/user/studio/tickers/{tickerId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | ------------------------------ | | `tickerId` | string (UUID) | The ID of the ticker to update | ## Request Body | Field | Type | Description | | ------ | ------ | ---------------------------- | | `text` | string | Ticker text (required) | ```json { "text": "Updated ticker message" } ``` ## Response ```json { "id": "c3d4e5f6-a7b8-9012-cdef-345678901234", "text": "Updated ticker message", "brandId": "f47ac10b-58cc-4372-a567-0e02b2c3d479" } ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioTickerNotFound", "name": "studio_ticker_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers/studio-ticker-delete.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Delete Ticker Remove a user's studio ticker :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Permanently delete a studio ticker owned by the authenticated user. ```bash curl -X DELETE \ -H "Authorization: Bearer [access token]" \ https://api.restream.io/v2/user/studio/tickers/{tickerId} ``` ## Path Parameters | Parameter | Type | Description | | ---------- | ------------- | ------------------------------ | | `tickerId` | string (UUID) | The ID of the ticker to delete | ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 404, "status": 404, "code": 404, "message": "StudioTickerNotFound", "name": "studio_ticker_not_found" } } ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /studio/tickers/studio-ticker-reorder.md --- import { Tab, Tabs } from '@rspress/core/theme'; # Reorder Tickers Update the order of a user's studio tickers :::info **Required scope:** studio.write ::: :::warning This method requires authentication ::: Set a custom order for the authenticated user's tickers by providing an array of ticker IDs. ```bash curl -X PATCH \ -H "Authorization: Bearer [access token]" \ -H "Content-Type: application/json" \ -d '{"ids": ["c3d4e5f6-a7b8-9012-cdef-345678901234", "d4e5f6a7-b8c9-0123-def4-567890123456"]}' \ https://api.restream.io/v2/user/studio/tickers/order ``` ## Request Body | Field | Type | Description | | ----- | ----------------- | ---------------------------------------- | | `ids` | array of strings | Ordered list of ticker IDs (required) | ```json { "ids": [ "c3d4e5f6-a7b8-9012-cdef-345678901234", "d4e5f6a7-b8c9-0123-def4-567890123456" ] } ``` ## Response ``` 204 No Content ``` ```json { "error": { "statusCode": 401, "status": 401, "code": 401, "message": "Invalid token: access token is invalid", "name": "invalid_token" } } ``` --- url: /chat.md --- # Restream Chat API The Restream Chat API unifies live chat from every platform you stream to into a single real-time WebSocket feed. Receive chat events as they arrive, send replies back to viewers, run moderation actions, and relay messages between connections. Start with the [Chat Getting Started guide](/chat/getting-started.md) for connection details and authentication. ## Topics * [Getting Started](/chat/getting-started.md) — Connect to the chat WebSocket and authenticate. * [Event Sources](/chat/event-sources.md) — Understand where chat events originate. * [Events](/chat/events.md) — The chat events you receive over the connection. * [Actions](/chat/actions.md) — Moderation and chat actions you can perform. * [Connections](/chat/connections.md) — Manage chat WebSocket connections. * [Reply](/chat/reply.md) — Send replies back to a platform's chat. * [Relay](/chat/relay.md) — Relay chat messages between connections. --- url: /chat/getting-started.md --- # Chat ## Overview Listen for chat actions. This API allows you to connect to Restream Chat and receive all actions which include: information about current chat connections; incoming events collected from event sources like Twitch, YouTube, etc.; sent replies and relays. :::warning This method requires authentication ::: ## Connect with WebSocket Here is a sample in JavaScript that connects to the API using WebSocket API as it is available in the browser: ```javascript // OAuth `bearer` token const accessToken = '[access token]'; const url = `wss://chat.api.restream.io/ws?accessToken=${accessToken}`; const connection = new WebSocket(url); connection.onmessage = (message) => { const action = JSON.parse(message.data); console.log(action); }; connection.onerror = console.error; ``` ## Message direction This API works one way — from the server to the client. The server will ignore any incoming messages. --- url: /chat/event-sources.md --- # Event Sources ## Overview Events sources are the platforms and services which supported by Restream Chat. That means that we can receive events from their APIs and for most of them - send events back (reply) and relay events between them (relay). List of supported event sources along with the features available for them provided in the table below. Some of event sources are called `event-based` (marked *italic* in the table), which means that every stream has a separate event (link, page, etc.). In most cases, chat requires for that event to be live in order to be able to connect and operate properly. ## Supported event sources | `eventSourceId` | Name | Read | Reply | Relay | | :-------------: | ----------------------------- | :--: | :---: | :---: | | 1 | Restream | | | | | 2 | Twitch | ✅ | ✅ | ✅ | | 13 | *YouTube* | ✅ | ✅ | ✅ | | 19 | *Facebook (Personal profile)* | ✅ | ❌ | ❌ | | 20 | *Facebook (Public page)* | ✅ | ✅ | ❌ | | 24 | DLive | ✅ | ✅ | ✅ | | 25 | Discord | ✅ | ✅ | ✅ | | 26 | *LinkedIn* | ✅ | ❌ | ❌ | | 27 | Trovo | ✅ | ✅ | ❌ | | 28 | X | ✅ | ❌ | ❌ | | 29 | Kick | ✅ | ✅ | ✅ | | 33 | Rumble | ✅ | ❌ | ❌ | --- url: /chat/actions.md --- # Actions Each message received from the API is a JSON encoded object which represents one of actions. The full list of actions along with a short description and the TypeScript interfaces names provided in the table below. A detailed description of different actions provided in the separate sections. :::warning This list can be extended over time ::: | Action | Interface | Description | |--------------|--------------|--------------| | **heartbeat** | `IActionHeartbeat` | Sent by server approx every 45 seconds. If not received in 60 seconds - consider the connection broken and try to reconnect. | | **connection\_info** | `IActionConnectionInfo` | Information about connections to the event sources. | | **connection\_closed** | `IActionConnectionClosed` | Sent when the connection is closed. | | **event** | `IActionEvent` | The event received from the event source. | | **reply\_created** | `IActionReplyCreated` | The user created a reply. | | **reply\_accepted** | `IActionReplyAccepted` | The event source accepted the reply. | | **reply\_confirmed** | `IActionReplyConfirmed` | The reply delivery confirmed. | | **reply\_failed** | `IActionReplyFailed` | The reply failed to send to the event source. | | **relay\_accepted** | `IActionRelayAccepted` | The event source accepted the relay. | | **relay\_confirmed** | `IActionRelayConfirmed` | The relay delivery confirmed. | | **relay\_failed** | `IActionRelayFailed` | The relay failed to send to the event source. | ## TypeScript Interfaces :::warning TypeScript syntax, new actions and fields may be added over time ::: ```typescript type IActionType = 'heartbeat' | 'connection_info' | 'connection_closed' | 'event' | 'reply_created' | 'reply_accepted' | 'reply_confirmed' | 'reply_failed' | 'relay_accepted' | 'relay_confirmed' | 'relay_failed'; interface IBasicAction { action: IActionType; payload: {}; // timestamp in seconds timestamp: number; } interface IActionHeartbeat extends IBasicAction { action: 'heartbeat'; } interface IActionConnectionInfo extends IBasicAction { action: 'connection_info'; payload: { // unique identifier of connection target connectionIdentifier: string; // unique identifier of connection connectionUuid: string; eventSourceId: number; // text code of error, not null only when `status` = 'error' reason: string | null; status: 'connecting' | 'connected' | 'error'; // event source specific target description target: IConnectionTargetBasic; userId: number; }; } interface IActionConnectionClosed extends IBasicAction { action: 'connection_closed'; payload: { connectionUuid: string; reason: // channel deleted or disabled 'removed' // replaced by another connection | 'replaced' // no longer needed | 'expired' // replaced by another connection | 'superseded' // instance which serving that connection is shutting down | 'shutdown' // connection was disabled before it was established | 'connection_establish_too_long' | 'internal'; }; } interface IActionEvent extends IBasicAction { action: 'event'; payload: { connectionIdentifier: string; // do NOT use it to uniquely identify events eventIdentifier: string; // event type specific payload eventPayload: IEventTypePayloadBasic; eventSourceId: number; eventTypeId: number; userId: number; }; } interface IActionReplyCreated extends IBasicAction { action: 'reply_created'; payload: { clientReplyUuid: string; connectionIdentifiers: [string]; eventSourceId: number; replyUuid: string; text: string; }; } interface IActionReplyAccepted extends IBasicAction { action: 'reply_accepted'; payload: { connectionIdentifier: string; replyUuid: string; }; } interface IActionReplyConfirmed extends IBasicAction { action: 'reply_confirmed'; payload: { connectionIdentifier: string; replyUuid: string; }; } interface IActionReplyFailed extends IBasicAction { action: 'reply_failed'; payload: { connectionIdentifier: string; // text code of the error reason: string; replyUuid: string; }; } interface IActionRelayAccepted extends IBasicAction { action: 'relay_accepted'; payload: { connectionIdentifier: string; // `eventIdentifier` of the event which was relayed sourceEventIdentifier: string; }; } interface IActionRelayConfirmed extends IBasicAction { action: 'relay_confirmed'; payload: { connectionIdentifier: string; sourceEventIdentifier: string; }; } interface IActionRelayFailed extends IBasicAction { action: 'relay_failed'; payload: { connectionIdentifier: string; // text code of the error reason: string; sourceEventIdentifier: string; }; } ``` --- url: /chat/connections.md --- # Connections Used to display the list of connected channels and Discords. After establishing WebSocket connection, you'll receive messages with `connection_info` action - one for each enabled channel which belongs to supported event sources and one for each connected Discord channel. Your application should keep the map of connections using `connectionIdentifier` as a key and the last received payload of `connection_info` with that `connectionIdentifier` as a value. The connection should be deleted from that map only after receiving `connection_closed` with `connectionUuid` (not a `connectionIdentifier`) equivalent to the one saved in the value. You can receive duplicates of exactly the same `connection_info` messages. You can either ignore them or just follow the rule and overwrite the previous payload in the map of connections. Also, there can be a case when you will receive a `connection_closed` message and won't be able to find a connection in the map to delete. This can happen because that connection can be already overwritten by the next one which replaced it. When we need to establish a connection to the same target one more time (let's say chat settings was changed), we will first establish a new one and only then terminate the previous in order to avoid downtime (API will handle event deduplication for you). Connections have a target which is different for each event source. The full list of target interfaces can be found below. ## Connection Errors You can receive `connection_info` action with `status = 'error'`. In that case, `reason` field will contain error code. List of possible reasons along with description can be found in the table below. :::warning Most reasons are self-explanatory. Only the most common ones will be provided and explained. ::: | Reason | Event Source | Description | |---|---|---| | `restream_channel_connection_expired` | any | Credentials used to connect to event source API expired. User should go to Restream Dashboard and refresh them. | | `youtube_livechat_ended` | YouTube | Live chat is no longer live. Broadcast completed. No comments can be received from completed broadcasts. | | `facebook_event_not_live` | Facebook | LiveVideo is not live. Can get comments only from live events. | | `youtube_broadcast_completed` | YouTube | Broadcast is completed. No comments can be received from completed broadcasts. | | `event_not_started` | event-based | No streams via Restream were made from this channel yet. So there is no event to connect to. | | `discord_invalid_refresh_token` | Discord | User should reconnect that Discord channel via Restream Chat settings. | | `channel_added_manually` | any | Channel was added to Restream Dashboard manually. We can't get access to event source in such case. | | `internal` | any | Some internal error occurred. Try to reconnect a channel in Restream Dashboard. If the problem persists - user will need to contact Restream Support in order to resolve that issue. | ## TypeScript Interfaces :::warning TypeScript syntax, new interfaces and fields may be added over time ::: ```typescript interface IConnectionTargetBasic { } interface IConnectionTargetTwitch extends IConnectionTargetBasic { owner: { avatar?: string; displayName: string; id: string; name?: string; url?: string; }; // Restream `channel_id` websiteChannelId: number; } interface IConnectionTargetYouTube extends IConnectionTargetBasic { event: { id: string; title?: string; url: string; }; owner: { avatar?: string; displayName: string; id: string; }; websiteChannelId: number; } interface IConnectionTargetFacebookPersonal extends IConnectionTargetBasic { liveVideo: { id: string; status?: string; title?: string; url?: string; }; user: { avatar?: string; id: string; name: string; }; websiteChannelId: number; } interface IConnectionTargetFacebookPage extends IConnectionTargetBasic { liveVideo: { id: string; status?: string; title?: string; url?: string; }; page: { id: string; name: string; picture?: string; }; websiteChannelId: number; } interface IConnectionTargetDLive extends IConnectionTargetBasic { owner: { avatar?: string; displayName: string; url: string; username: string; }; websiteChannelId: number; } interface IConnectionTargetDiscord extends IConnectionTargetBasic { channel: { id: string, name?: string, url: string, }; owner?: { avatar: string; id: string; name: string; }; server?: { icon: string | null; id: string; name: string; }; } interface IConnectionTargetLinkedIn extends IConnectionTargetBasic { organization?: { avatarUrl?: string; id: number; name: string; }; post: { id: string; url?: string; }; user?: { avatarUrl?: string; id?: string; name: string; }; websiteChannelId: number; } ``` --- url: /chat/events.md --- # Events Used to display incoming events. Action `event` represents a single incoming message collected from one of the supported event sources. Event payload will differ depending on the event type. The full list of supported event types along with a short description can be found in the table below. That list can be extended the same as more fields can be added to interfaces. | `eventTypeId` | Event Type | Description | |:-------------:|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 | Discord Text | Payload can include emojis as UTF symbols and links. Mentions are provided in the text format (@username). | | 2 | DLive Text | Payload can include emojis as UTF symbols and links in text format. Mentions are provided in the text format (@username). | | 3 | DLive Emoji | Payload contains a link to image with DLive emoji. | | 4 | Twitch Text | Payload can include emojis as UTF symbols and links in text format. Mentions are provided in the text format (@username). Twitch emotes are just words in `text`. The payload includes `replaces`, which point to emojis in the text. Avatar will be provided only if the user has `Display avatar` setting enabled. | | 5 | YouTube Text | Payload can include emojis as UTF symbols and links in text format. Mentions are provided in the text format (@username). | | 7 | YouTube Super Chat | Payload contains an amount which was donated and currency. Also, a text message can be added to this event. | | 8 | YouTube Super Sticker | Payload contains an amount which was donated and currency. StickerId saved for reference, API does not provide a link to an image. Also, altText which describes sticker added to the text field. | | 11 | Facebook (Personal profile) Text | Payload can include emojis as UTF symbols and links and mentions in text format. Sometimes Facebook can omit information about the author, so fields inside `author` are optional. `parent` field, if present, contains the name of the author of the comment to which this comment was sent as a reply. | | 12 | Facebook (Personal profile) Sticker | Extended payload of the Facebook (Personal profile) Text event. `link` has a URL of sticker image. | | 13 | Facebook (Public page) Text | Payload can include emojis as UTF symbols and links and mentions in text format. Sometimes Facebook can omit information about the author, so fields inside `author` are optional. `parent` field, if present, contains the name of the author of the comment to which this comment was sent as a reply. | | 14 | Facebook (Public page) Sticker | Extended payload of the Facebook (Public page) Text event. `link` has a URL of sticker image. | | 21 | LinkedIn Text | It can include emojis as UTF symbols and links and mentions in text format. The author can be retrieved only for users who allows getting such information, so this field is optional. | | 22 | Trovo Text | It can include emojis as UTF symbols and links and mentions in text format. The author can be retrieved only for users who allows getting such information, so this field is optional. | | 23 | YouTube Member Milestone | Channel members can send one special highlighted message per month in live chat to recognize and celebrate how long they've been a member. This feature is available to members who are in, at least, their 2nd consecutive month as a member. Messages can only be sent during live streams or Premieres, and are visible to all viewers. | | 24 | X Text | X text message. It can include emojis as UTF symbols and links in text format. Mentions are provided in the text format (@username). | | 25 | Kick Text | Kick text message from live chat. Includes author information with user ID and username. Supports custom emotes through the replaces array for emote substitution. Can include emojis as UTF symbols and links in text format. The optional parent field contains the name of the author being replied to | | 26 | Kick Subscription | Kick subscription event triggered when a user subscribes to a channel. Includes the subscriber's username and optionally the subscription duration in months (subscribedFor). For gifted subscriptions, includes an array of recipient usernames in giftedTo | | 28 | YouTube Membership | YouTube Membership: includes new sponsors and gifted subscriptions | | 29 | YouTube Membership Gifting | YouTube Membership Gifting: someone gifted membership to other users | | 32 | Rumble Text | Rumble text message from live chat. Includes author information with display name and optional avatar. Supports custom emotes through the replaces array, which provides image URLs and positions for emote substitution in the text. Can include emojis as UTF symbols and links in text format | You can use `connectionIdentifier` to associate an event with the connection from which it received. You **can't** use `eventIdentifier` to uniquely identify events. Each event has enough information to render it the same way it shown in Restream chat. If event has some attachments (stickers for example) payload will contain all data so no additional requests need to be made in order to display event content. ## Emojis and Replaces Emojis provided as UTF-8 symbols, so it's your decision about how to render them (Links processing and display is your choice as well.). In cases when event source has a support for custom emojis, `replaces` will be included into event payload. `replaces` is an array of objects with required `from`, `to` and `type` fields, where `from` is a position in the event text from which you should start the replacement and `to` is a position of replacement end. Replaces provided in reverse order so you can just iterate over them and replace directly in the text and don't worry about replacement position change. All images used for attachments are stored on event source side. So Restream can't guaranty that all of them will be accessible all the time. ## TypeScript Interfaces :::warning TypeScript syntax, new actions and fields may be added over time ::: ```typescript interface IEventTypePayloadBasic {} interface IEventTypePayloadDiscordText extends IEventTypePayloadBasic { author: { avatar: string | null; discriminator: string; id: string; name: string; nickname: string | null; nicknameColor: string | null; roles: Array<{ color: string; name: string; }> | null; }; bot: boolean; text: string; } interface IEventTypePayloadDLiveText extends IEventTypePayloadBasic { author: { avatar: string; badges: string[]; name: string; partnerStatus: string; role: string; roomRole: string; subscribing: boolean; username: string; }; bot: boolean; text: string; } interface IEventTypePayloadDLiveEmoji extends IEventTypePayloadBasic { author: { avatar: string; badges: string[]; name: string; partnerStatus: string; role: string; roomRole: string; subscribing: boolean; username: string; }; bot: boolean; link: string; } interface IEventTypePayloadTwitchText extends IEventTypePayloadBasic { author: { avatar: string | null; badges: Array<{ title: string; imageUrl: string; clickUrl: string | null; }>; // color of the nickname and `/me` messages color: string | null; displayName: string | null; id: string; name: string; subscribedFor: number | null; username: string; }; // message created by bot bot: boolean; contentModifiers: { me: boolean; whisper: boolean; }; firstMessage: boolean; replaces: Array<{ from: number; to: number; type: 'imageUrl'; payload: { url: string; }; }>; repliedInThread: { displayName: string; twitchMessageId: string; twitchUserId: string; } | null; repliedTo: { displayName: string; twitchMessageId: string; twitchUserId: string; } | null; text: string; twitchMessageId: string | null; } interface IEventTypePayloadYouTubeText extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: boolean; liveChatMessageId: string; text: string; } interface IEventTypePayloadYouTubeSuperChat extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: false; donation: { amount: string; currencyString: string; tier: number; }; liveChatMessageId: string; text: string; } interface IEventTypePayloadYouTubeSuperSticker extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: boolean; donation: { amount: string; currencyString: string; stickerId: string; tier: number; }; liveChatMessageId: string; text: string; } interface IEventTypePayloadFacebookPersonalText extends IEventTypePayloadBasic { author: { id?: string; name?: string; picture?: string; }; bot: false; parent?: { name?: string; }; text: string; } interface IEventTypePayloadFacebookPersonalSticker extends IEventTypePayloadBasic { author: { id?: string; name?: string; picture?: string; }; bot: false; parent?: { name?: string; }; link: string; text: string; } interface IEventTypePayloadFacebookPageText extends IEventTypePayloadBasic { author: { id?: string; name?: string; picture?: string; }; bot: false; parent?: { name?: string; }; text: string; } interface IEventTypePayloadFacebookPageSticker extends IEventTypePayloadBasic { author: { id?: string; name?: string; picture?: string; }; bot: false; parent?: { name?: string; }; link: string; text: string; } interface IEventTypePayloadLinkedInText extends IEventTypePayloadBasic { author?: { avatarUrl?: string; name: string; }; bot: false; text: string; } interface IEventTypePayloadTrovoText extends IEventTypePayloadBasic { author?: { avatarUrl?: string; name: string; }; bot: false; text: string; replaces: Array<{ from: number; payload: { url: string; }; to: number; type: 'imageUrl' | string; }>; } interface IEventTypePayloadYouTubeMemberMilestone extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: false; liveChatMessageId: string; memberMilestone: { memberMonth: number; memberLevelName?: string; }; text: string; } interface IEventTypePayloadXplatformText extends IEventTypePayloadBasic { author: { id: string; avatar?: string; displayName: string; username: string; verified: boolean; }; text: string; } interface IEventTypePayloadKickText extends IEventTypePayloadBasic { author: { id: number; username: string; }; parent?: { name: string; }; bot: boolean; replaces: Array<{ from: number; payload: { url: string; }; to: number; type: 'imageUrl' | string; }>; text: string; } interface IEventTypePayloadKickSubscription extends IEventTypePayloadBasic { author: { username: string; }; subscribedFor?: number; giftedTo?: string[]; } interface IEventTypePayloadYouTubeMembership extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: false; liveChatMessageId: string; membership: { gift: boolean; memberLevelName: string; }; text: string; } interface IEventTypePayloadYouTubeMembershipGifting extends IEventTypePayloadBasic { author: { id: string; avatar: string; displayName: string; isChatModerator: boolean; isChatOwner: boolean; isChatSponsor: boolean; isVerified: boolean; }; bot: false; giftMemberships: { count: number; levelName: string; }; liveChatMessageId: string; text: string; } interface IEventTypePayloadRumbleText extends IEventTypePayloadBasic { author: { avatar?: string; displayName: string; id: string; name: string; }; bot: false; replaces: Array<{ from: number; payload: { url: string; }; to: number; type: 'imageUrl' | string; }>; text: string; } ``` --- url: /chat/reply.md --- # Reply ## Overview Used to display replies send from Restream Chat. ## Reply lifecycle When reply created in Restream Chat, you will receive `reply_created` action. It will contain `replyUuid` which used to associate that reply with `reply_accepted`, `reply_failed`, and `reply_confirmed` actions received afterwards. Be aware that `reply_confirmed` can be received before `reply_accepted`. Also if `reply_confirmed` not received at all that doesn't mean that message wasn't delivered. `reply_confirmed` send only when we 100% sure that message was delivered, which not always can't be guaranteed because of specifics of each event source. ## Common and direct replies In Restream Chat, a reply can be sent to all connections (common reply) or one connection (direct reply). `eventSourceId` will be set to 1 when this is a common reply (Event Source with `id = 1` is Restream). For direct reply, it will be set to Event Source to which that connection belongs. ## Errors In case of error, `reply_failed` action will have `reason` field with text code of the occurred error. ## Reply failure reasons List of possible reasons along with description can be found in the table below. :::warning Most reasons are self-explanatory. Only the most common ones will be provided and explained. ::: | Reason | Event Source | Description | |---|---|---| | `dlive_api_send_message_rate_limit` | DLive | Rate limit on DLive side. | | `facebook_event_not_live` | Facebook | The event should be live in order to leave a comment. | | `connection_in_error_state` | any | The connection has an error. We won't send a reply while the connection is broken. | | `discord_rate_limit` | Discord | Rate limit on Discord side. | | `connection_not_established_yet` | any | The connection is not established yet, can't send reply right now. | | `internal` | any | Some internal error occurred. Try to send reply one more time. If the problem persists - user will need to contact Restream Support in order to resolve that issue. | --- url: /chat/relay.md --- # Relay ## Overview Used to display relay delivery and errors. Relay is a feature of Restream Chat which allows to sync chats between different event sources. When relay enabled in settings, whenever the user receives an event, it will be sent to other connections from the name of Restream Bot. That way chatters on any platform will see all messages which streamer receives from different event sources. ## Relay lifecycle and errors Relay has much in common with a reply. They have a common list of errors, so for relay error reasons, you can check the [reply section](/chat/reply.md). Also, the workflow is the same. First, you will receive `relay_accepted` action, followed by `relay_confirmed` if we are 100% sure that message was delivered. The only difference is that relay triggered by an incoming event, and because of this, it's related to that incoming event by `sourceEventIdentifier`. --- url: /index.md --- --- url: /apps/index.md --- import { AppsPage } from '../../src/features/clientApps/AppsPage';