---
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';