# Speak Restreaming via API

> Use the Kinescope API to set up restream profiles for a Speak room: create them with the room, change the URL and stream key, delete them, limits, and errors.

Source: https://docs.kinescope.com/developer-guides/speak-restreaming-via-api/
Updated: 2026-10-12

A Speak room stores restream profiles, which say where the meeting stream goes and with which key. A profile belongs to the room rather than to a single call, so a YouTube or Twitch destination set once works in every meeting held in that room. The room interface calls this object a destination; the API calls it a restream profile and uses the `restreams` and `restream_id` fields.

Through the API you define profiles when you create a room and change them with a separate request. The host no longer pastes a URL and a key before going live: they open the restream panel and flip a toggle. Before you begin, read the [general API guidelines](https://docs.kinescope.com/developer-guides/api-general-rules/).

## Who this article is for

* **Webinar platform developers** — you create rooms that must already contain streaming destinations
* **LMS and CRM backend developers** — the broadcast schedule and platform URLs live in your system
* **Integrators** — you need to fill in stream keys without involving the host

## When to set restream profiles in code

* **Rooms are created in batches.** Your system creates a room per webinar, and each one needs YouTube and Twitch ready to go.
* **The host must not see the keys.** You keep platform keys on your side, and the host only flips a toggle in the room.
* **A platform reissued a key.** You change `stream_key` with one request instead of opening every room in the interface.
* **Destinations depend on the schedule.** One course streams to YouTube, another to Twitch, and your backend decides which.

## How restream profiles work

1. You create a room and pass a `restreams` array with a name, URL, and stream key.
2. Kinescope detects the platform from the URL domain and returns it in the `service` field.
3. Profiles come back in the `restreams` field of the room object and stay there until the room is deleted.
4. A host or co-host turns on the profile they need in the restream panel, with no URL or key to type.
5. While a profile is not streaming, you can change it with `PATCH`.

> **Info:**
>
> **The API cannot start or stop a restream.** It only handles profiles: creating, reading, and updating them. A participant turns the stream on and off inside the room, as described in [Restreaming in Speak](https://docs.kinescope.com/speak-beta/restreaming-in-speak/).

## What you need

* A workspace API token in the `Authorization: Bearer YOUR_API_TOKEN` header.
* The room owner's ID for the `owner_id` field. The user must belong to the same workspace, otherwise the request fails.
* A URL and a stream key from the external platform. [Restreaming in Speak](https://docs.kinescope.com/speak-beta/restreaming-in-speak/) explains where each platform keeps them.

## Create a room with restream profiles

`POST https://api.kinescope.io/v1/speak/rooms`

The API has no "add a profile to a room" method. Profiles are defined only when the room is created.

```bash
curl -X POST 'https://api.kinescope.io/v1/speak/rooms' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "owner_id": "5b8a54a1-1f4f-4a5f-9a3a-2a4b0e2f1c77",
    "name": "Webinar: case studies",
    "type": "permanent_meeting",
    "restreams": [
        {
            "name": "YouTube Live",
            "url": "rtmp://a.rtmp.youtube.com/live2",
            "stream_key": "xxxx-xxxx-xxxx-xxxx-xxxx"
        },
        {
            "name": "Twitch",
            "url": "rtmp://live.twitch.tv/app",
            "stream_key": "live_xxxxxxxxxx"
        }
    ]
}'
```

Profile fields:

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | Yes | Profile name, 1 to 128 characters |
| `url` | string | Yes | The platform's RTMP URL, without the stream key |
| `stream_key` | string | Yes | The platform's stream key, 1 to 512 characters |

Send `url` and `stream_key` separately: do not join them into one string. Kinescope trims spaces and trailing slashes in `url` itself.

Profiles are created together with the room in a single transaction. If even one profile fails validation, the room is not created, so fix the request body and send the whole thing again. A room holds up to 15 profiles.

A successful request returns `200 OK` with the room object:

```json
{
  "data": {
    "id": "9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41",
    "name": "Webinar: case studies",
    "type": "permanent_meeting",
    "code": "jqi-qhua-glk",
    "link": "https://speak.kinescope.io/jqi-qhua-glk",
    "status": "pending",
    "restreams": [
      {
        "id": "3c7f1b84-5d20-4e91-a0c6-8f2b1d4e7a93",
        "room_id": "9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41",
        "name": "YouTube Live",
        "url": "rtmp://a.rtmp.youtube.com/live2",
        "stream_key": "xxxx-xxxx-xxxx-xxxx-xxxx",
        "service": "youtube",
        "created_at": "2026-10-12T13:20:00.000000Z",
        "updated_at": null
      }
    ],
    "created_at": "2026-10-12T13:20:00.000000Z"
  }
}
```

Save the profile `id`: you need it to change the URL or the key.

### Which platforms Kinescope recognizes

Kinescope computes `service` from the `url` domain, so you never send it. When the URL changes, the value is recalculated.

| Value | Domain |
|---|---|
| `youtube` | youtube.com |
| `twitch` | twitch.tv |
| `telegram` | t.me |
| `kinescope` | kinescope.io |
| `custom` | any other domain |

Subdomains count too: `rtmp://a.rtmp.youtube.com/live2` resolves to `youtube`.

## Read the saved profiles

There is no separate endpoint that lists profiles. They arrive in the `restreams` field of the room object, sorted by creation date:

```bash
curl 'https://api.kinescope.io/v1/speak/rooms/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

Every room in the `GET /v1/speak/rooms` response carries the same field, so you can export the profiles of all rooms with one paginated request.

## Change the URL, key, or name

`PATCH https://api.kinescope.io/v1/speak/rooms/{room_id}/restreams/{restream_id}`

Send only the fields you need to change:

```bash
curl -X PATCH 'https://api.kinescope.io/v1/speak/rooms/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41/restreams/3c7f1b84-5d20-4e91-a0c6-8f2b1d4e7a93' \
  -H 'Authorization: Bearer YOUR_API_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "stream_key": "yyyy-yyyy-yyyy-yyyy-yyyy"
}'
```

| Value to send | Result |
|---|---|
| Omit a field | Its value does not change |
| `"name": "New name"` | Changes the profile label in the destination list |
| `"url": "rtmp://..."` | Changes the platform URL, and `service` is recalculated from the new domain |
| `"stream_key": "..."` | Changes the stream key |

Length limits match those at creation: `name` is 1 to 128 characters, `stream_key` is 1 to 512.

The response contains the updated profile, not the whole room. The change applies the next time the restream starts.

> **Warning:**
>
> **A profile that is streaming right now cannot be changed.** The API returns `409` with code `409298`. Stop the restream in the room first, then repeat the request.

## Delete profiles together with the room

`DELETE https://api.kinescope.io/v1/speak/rooms/{room_id}`

The API has no method for deleting a single profile. Profiles live exactly as long as the room does:

```bash
curl -X DELETE 'https://api.kinescope.io/v1/speak/rooms/9f1d3c02-77a0-4f5e-8b26-1e0a5d3c8b41' \
  -H 'Authorization: Bearer YOUR_API_TOKEN'
```

The request ends the room, disconnects participants, shuts down active restreams, and deletes all of the room's profiles. Repeating `DELETE` on the same room also returns success, so it is safe inside a retry loop.

To remove a single destination and keep the room, a host or co-host clicks **Delete** next to the destination in the restream panel. The same panel is where profiles are added to a room that already exists.

## Limits

| Item | Value |
|---|---|
| Profiles per room | up to 15 |
| Restreams running at once in a call | up to 5 |
| `name` length | 1 to 128 characters |
| `stream_key` length | 1 to 512 characters |
| Starting and stopping a restream via API | not available |
| Adding and deleting a single profile via API | not available |

> **Warning:**
>
> **The API returns `stream_key` in plain text.** A stream key lets anyone broadcast to your platform account, so call these endpoints only from your backend. Do not expose an API token in a browser or mobile application, and do not show raw API responses to end users.

## Handle common errors

| HTTP | Code | When it happens |
|---|---|---|
| `400` | `400289` | The room creation request contains more than 15 profiles |
| `400` | `400400` | `name`, `url`, or `stream_key` fails validation |
| `404` | `404404` | The room or the profile was not found in your workspace |
| `409` | `409298` | The profile is streaming and cannot be changed |

The error body format and the `invalid_params` fields are described in the [general API guidelines](https://docs.kinescope.com/developer-guides/api-general-rules/).

`404404` is easy to hit with a valid `restream_id` and the wrong `room_id`: a profile is looked up strictly inside the room you name and inside your workspace.

## What's next?

After you set up restream profiles:

1. **[Restreaming in Speak](https://docs.kinescope.com/speak-beta/restreaming-in-speak/)** — how the host starts destinations from the room and what a dropped stream looks like
2. **[Speak login link with the administrator role](https://docs.kinescope.com/developer-guides/speak-admin-login-link/)** — a JWT in the link so the host can start restreams right away
3. **[Speak API reference](https://docs.kinescope.com/api/speak/#v1-speak-rooms)** — rooms, participants, and calls with parameters and examples
4. **[General API Guidelines](https://docs.kinescope.com/developer-guides/api-general-rules/)** — authorization, response format, and rate limits

Questions? Write to the support chat in the Kinescope interface.
