> For the complete documentation index, see [llms.txt](https://docs.firework.com/firework-for-developers/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.firework.com/firework-for-developers/api/livestreams.md).

# Livestreams

### 1. Overview <a href="#id-1.-overview" id="id-1.-overview"></a>

The Firework Livestream API lets you manage livestreams end to end. The public API supports the following workflows:

* **Create**: Schedule a single livestream through a public API endpoint
* **Read**: Fetch livestream details, current status, schedule, and thumbnail image
* **Update**: Modify mutable livestream metadata, schedule, trailer, and tagged products
* **Write**: Pin or unpin products in real time and force-end live sessions when needed
* **Export**: Download post-event interaction results and livestream comments

Use these endpoints to create interactive commerce moments while keeping operational control of every broadcast.

**Base URL**: `<https://api.firework.com>`

### 2. Authentication <a href="#id-2.-authentication" id="id-2.-authentication"></a>

The Firework Livestream API uses OAuth 2.0 for authentication. Before using this API, you must obtain an access token.

**Authentication Method:**

* **Client Credentials**: OAuth 2.0 Client Credentials flow for server-to-server authentication (machine-to-machine)

> 📖 **Documentation:**
>
> * [Client Credentials OAuth](https://docs.firework.com/firework-for-developers/api/authentication) - Server-to-server authentication for OAuth apps

***

### 3. Endpoint Summary <a href="#id-3.-endpoint-summary" id="id-3.-endpoint-summary"></a>

| Endpoint                                                                                | Scope               | Notes                                          |
| --------------------------------------------------------------------------------------- | ------------------- | ---------------------------------------------- |
| `POST /api/v1/live_streams`                                                             | `livestreams:write` | Create a single scheduled livestream           |
| `GET /api/v1/live_streams/{live_stream_id}`                                             | `livestreams:read`  | Get livestream info and export interaction IDs |
| `PATCH /api/v1/live_streams/{live_stream_id}`                                           | `livestreams:write` | Update mutable livestream fields               |
| `POST /api/v1/live_streams/{id}/pin_product`                                            | `livestreams:write` | Pin products during livestream                 |
| `POST /api/v1/live_streams/{id}/unpin_product`                                          | `livestreams:write` | Unpin products during livestream               |
| `PATCH /api/v1/live_streams/{id}/end`                                                   | `livestreams:write` | Force end an active livestream                 |
| `GET /api/v1/live_streams/{live_stream_id}/interactions/{interaction_id}/responses_csv` | `livestreams:read`  | Download interaction response results as CSV   |
| `GET /api/v1/live_streams/{live_stream_id}/comments_csv`                                | `livestreams:read`  | Download livestream comments as CSV            |

***

### 4. Create Livestream <a href="#id-4.-create-livestream" id="id-4.-create-livestream"></a>

Schedule a single livestream through the public API.

> **Scope of v1**: This endpoint is intentionally single-create only. If you need to create many livestreams, call this endpoint multiple times and respect the documented rate limit. A bulk/job-based create API is not part of this version of the spec.

**Endpoint**: `POST /api/v1/live_streams`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:write` (for OAuth apps)\
**Rate Limit**: 20 create requests per 5 minutes per channel\
**Content Type**: `application/json`\
**Status**: Implemented

> **Scope note**: Creating the livestream requires `livestreams:write`. If you want to upload a custom trailer and pass it as `s3_key`, the upload step uses the public video upload APIs and requires `videos:write`.

#### 4.1. Request Headers <a href="#id-4.1.-request-headers" id="id-4.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |
| `Content-Type`  | Must be `application/json`            | ✅        |

#### 4.2. Request Body <a href="#id-4.2.-request-body" id="id-4.2.-request-body"></a>

| Field                         | Type          | Required | Default                  | Description                                                                           |
| ----------------------------- | ------------- | -------- | ------------------------ | ------------------------------------------------------------------------------------- |
| `caption`                     | string        | ✅        | None                     | Livestream title shown to viewers                                                     |
| `channel_id`                  | string        | ✅        | None                     | Encoded channel ID where the livestream will be created                               |
| `description`                 | string        | ❌        | None                     | Livestream description                                                                |
| `scheduled_at`                | string        | ✅        | None                     | ISO 8601 datetime when the livestream is scheduled to start                           |
| `time_zone`                   | string        | ❌        | None                     | IANA timezone identifier for the scheduled event, for example `America/New_York`      |
| `stream_source`               | string        | ✅        | None                     | Source of the livestream. Supported values for v1: `"fw"` and `"external"`            |
| `s3_key`                      | string        | ❌        | Firework default trailer | Pre-uploaded trailer key to use while the livestream is idle                          |
| `business_store_id`           | string        | ❌        | Use first one            | Encoded business store ID used to resolve `product_ids`                               |
| `product_ids`                 | string\[]     | ❌        | `[]`                     | Initial products to tag to the livestream trailer/video                               |
| `access_code`                 | string / null | ❌        | `null`                   | Access code used to lock the video/livestream. Use `null` to leave it unlocked        |
| `announcement`                | string / null | ❌        | `null`                   | Announcement banner text shown to viewers                                             |
| `auto_response`               | string        | ❌        | `"disabled"`             | Chat auto-response mode. Supported values: `"always_on"`, `"moderated"`, `"disabled"` |
| `chat_enabled`                | boolean       | ❌        | `true`                   | Whether live chat is enabled                                                          |
| `chat_in_replay_enabled`      | boolean       | ❌        | `true`                   | Whether chat messages are displayed during replay                                     |
| `chat_moderation_enabled`     | boolean       | ❌        | `false`                  | Whether chat moderation is enabled                                                    |
| `hearts_count_enabled`        | boolean       | ❌        | `false`                  | Whether like count is shown                                                           |
| `replay_enabled`              | boolean       | ❌        | `true`                   | Whether replay is available after the livestream ends                                 |
| `replay_interactions_enabled` | boolean       | ❌        | `true`                   | Whether replay interactions are enabled                                               |
| `replay_messaging_enabled`    | boolean       | ❌        | `false`                  | Whether viewers can post messages during replay                                       |
| `trailer_messaging_enabled`   | boolean       | ❌        | `false`                  | Whether viewers can post messages while watching the trailer                          |
| `transcription_enabled`       | boolean       | ❌        | `false`                  | Whether live transcription is enabled, when supported by the provider/source          |
| `viewers_count_enabled`       | boolean       | ❌        | `true`                   | Whether viewer count is shown                                                         |
| `viewers_count_mode`          | string        | ❌        | `"accumulated"`          | Viewer-count mode. Supported values: `"accumulated"` and `"concurrent"`               |

**Request Rules:**

* If `s3_key` is omitted, Firework uses the default livestream trailer.
* If you want a custom trailer, upload it first using the public upload APIs documented in Firework Video API - Public, then pass the returned `key` value as `s3_key`.
* `product_ids` follows the same identifier rules as `POST /api/v1/videos`.
* `scheduled_at` should be provided in UTC ISO 8601 format.
* `stream_source` is required at create time. It can only be updated before the livestream enters the 5-minute warmup window.
* `stream_source` can only be assigned when the livestream is scheduled more than 5 minutes in the future.
* Livestream instance settings do not update channel/feed-level display settings or AI copilot setup.

**How to get** `s3_key`:

1. Call `POST /api/v1/upload_signatures` for a single-request upload, or `POST /api/v1/upload_multipart/signatures` followed by `POST /api/v1/upload_multipart/complete` for multipart upload.
2. Upload the trailer file directly to S3 using the returned signature data.
3. Take the `key` from the upload API response.
4. Pass that exact value as `s3_key` in `POST /api/v1/live_streams`.

#### 4.3. Create Livestream Response <a href="#id-4.3.-create-livestream-response" id="id-4.3.-create-livestream-response"></a>

**Success Response**: `201 Created`

| Field                         | Type    | Nullable | Description                                                            |
| ----------------------------- | ------- | -------- | ---------------------------------------------------------------------- |
| `id`                          | string  | ❌        | Unique encoded identifier of the livestream                            |
| `provider`                    | string  | ❌        | Streaming provider selected for the livestream                         |
| `status`                      | string  | ❌        | Initial livestream state. Expected value: `idle`                       |
| `event_name`                  | string  | ❌        | Name of the livestream event                                           |
| `event_description`           | string  | ✅        | Description of the livestream event                                    |
| `thumbnail_url`               | string  | ✅        | URL of the livestream thumbnail image                                  |
| `has_access_code`             | boolean | ❌        | Whether the video/livestream is locked by an access code               |
| `announcement`                | string  | ✅        | Announcement banner text                                               |
| `auto_response`               | string  | ❌        | Chat auto-response mode                                                |
| `chat_enabled`                | boolean | ❌        | Whether live chat is enabled                                           |
| `chat_in_replay_enabled`      | boolean | ❌        | Whether chat messages are displayed during replay                      |
| `chat_moderation_enabled`     | boolean | ❌        | Whether chat moderation is enabled                                     |
| `hearts_count_enabled`        | boolean | ❌        | Whether like count is shown                                            |
| `replay_enabled`              | boolean | ❌        | Whether replay is available after the livestream ends                  |
| `replay_interactions_enabled` | boolean | ❌        | Whether replay interactions are enabled                                |
| `replay_messaging_enabled`    | boolean | ❌        | Whether viewers can post messages during replay                        |
| `trailer_messaging_enabled`   | boolean | ❌        | Whether viewers can post messages while watching the trailer           |
| `transcription_enabled`       | boolean | ❌        | Whether live transcription is enabled                                  |
| `viewers_count_enabled`       | boolean | ❌        | Whether viewer count is shown                                          |
| `viewers_count_mode`          | string  | ❌        | Viewer-count mode: `"accumulated"` or `"concurrent"`                   |
| `scheduled_at`                | string  | ✅        | ISO 8601 datetime when livestream is scheduled to start                |
| `started_at`                  | string  | ✅        | ISO 8601 datetime when livestream actually started                     |
| `ended_at`                    | string  | ✅        | ISO 8601 datetime when livestream ended                                |
| `stream_key`                  | string  | ✅        | Publish stream key. Returned for `stream_source: "external"` on create |
| `stream_url`                  | string  | ✅        | Publish RTMP URL. Returned for `stream_source: "external"` on create   |

> **Note**: The create response mostly matches `GET /api/v1/live_streams/{live_stream_id}`. It additionally includes `provider`, and for `stream_source: "external"` it also returns `stream_key` and `stream_url` so clients can publish to the scheduled livestream.

#### 4.4. Create Livestream Error Responses <a href="#id-4.4.-create-livestream-error-responses" id="id-4.4.-create-livestream-error-responses"></a>

| Status Code                | Description                                                                                                                |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`          | Invalid request body, malformed datetime, unsupported `stream_source`, invalid `s3_key`, or maintenance window restriction |
| `401 Unauthorized`         | Invalid or missing authentication token                                                                                    |
| `402 Payment Required`     | Live events are not enabled for the business                                                                               |
| `403 Forbidden`            | The authenticated user or OAuth app does not have access to the channel                                                    |
| `404 Not Found`            | Channel not found                                                                                                          |
| `422 Unprocessable Entity` | Domain validation error from changesets, for example livestream scheduling constraints                                     |
| `429 Too Many Requests`    | Rate limit exceeded (20 create requests per 5 minutes per channel)                                                         |

**Error Response Format**:

Simple request/auth errors return:

`{ "error": "Error Message" }`

Validation errors from changesets return:

`{ "errors": { "field_name": ["validation message"] } }`

#### 4.5. Examples <a href="#id-4.5.-examples" id="id-4.5.-examples"></a>

**Example 1: Create Livestream with Default Trailer**

```
curl -X \
 POST "<https://api.firework.com/api/v1/live_streams>" \ -H \
 "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H \
 "Content-Type: application/json" \ -d '{ "channel_id": "X6Xqd6W", "caption": "Summer Sale Livestream", "description": "Join us for our scheduled commerce event", "scheduled_at": "2026-07-20T10:00:00.000000Z", "stream_source": "fw", "time_zone": "America/New_York" }'
```

**Example 2: Create Livestream with Custom Trailer**

```
curl -X \
 POST "<https://api.firework.com/api/v1/live_streams>" \ -H \
 "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H \
 "Content-Type: application/json" \ -d '{ "channel_id": "X6Xqd6W", "caption": "Bajaj Daily Livestream", "scheduled_at": "2026-07-20T12:30:00.000000Z", "stream_source": "external", "s3_key": "medias/business/AbCdEfG/channel/HiJkLmN/videos/public/original/1738152000-abcdefgh-trailer.mp4", "product_ids": ["GeEk8y", "ABC123"] }'
```

**Example 3: Create Livestream with Display Settings**

```
curl -X \
 POST "<https://api.firework.com/api/v1/live_streams>" \ -H \
 "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H \
 "Content-Type: application/json" \ -d '{ "channel_id": "X6Xqd6W", "caption": "Private VIP Livestream", "scheduled_at": "2026-07-20T12:30:00.000000Z", "stream_source": "fw", "access_code": "VIP123", "announcement": "Doors open in 5 minutes", "replay_enabled": true, "viewers_count_enabled": true, "viewers_count_mode": "accumulated", "hearts_count_enabled": false, "transcription_enabled": true }'
```

**Example Response**

```
{
    "id": "616dOp",
    "provider": "ivs",
    "status": "idle",
    "event_name": "Summer Sale Livestream",
    "event_description": "Join us for our scheduled commerce event",
    "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
    "has_access_code": false,
    "announcement": null,
    "auto_response": "disabled",
    "chat_enabled": true,
    "chat_in_replay_enabled": true,
    "chat_moderation_enabled": false,
    "hearts_count_enabled": false,
    "replay_enabled": true,
    "replay_interactions_enabled": true,
    "replay_messaging_enabled": false,
    "trailer_messaging_enabled": false,
    "transcription_enabled": false,
    "viewers_count_enabled": true,
    "viewers_count_mode": "accumulated",
    "scheduled_at": "2026-07-20T10:00:00.000000Z",
    "started_at": null,
    "ended_at": null,
    "stream_key": null,
    "stream_url": null
}
```

***

### 5. Get Livestream Info <a href="#id-5.-get-livestream-info" id="id-5.-get-livestream-info"></a>

Retrieve information about a specific livestream room including its current status, event details, scheduling information, and thumbnail image.

> **Note**: This is the endpoint for fetching livestream info.

**Endpoint**: `GET /api/v1/live_streams/{live_stream_id}`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:read` (for OAuth apps)

#### 5.1. Request Headers <a href="#id-5.1.-request-headers" id="id-5.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |

#### 5.2. Path Parameters <a href="#id-5.2.-path-parameters" id="id-5.2.-path-parameters"></a>

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

#### 5.3. Get Livestream Response <a href="#id-5.3.-get-livestream-response" id="id-5.3.-get-livestream-response"></a>

**Success Response**: `200 OK`

| Field                         | Type      | Nullable | Description                                                  |
| ----------------------------- | --------- | -------- | ------------------------------------------------------------ |
| `id`                          | string    | ❌        | Unique encoded identifier of the livestream                  |
| `status`                      | string    | ❌        | Current livestream state (see Status Values below)           |
| `event_name`                  | string    | ❌        | Name of the livestream event                                 |
| `event_description`           | string    | ✅        | Description of the livestream event                          |
| `thumbnail_url`               | string    | ✅        | URL of the livestream thumbnail image                        |
| `has_access_code`             | boolean   | ❌        | Whether the video/livestream is locked by an access code     |
| `announcement`                | string    | ✅        | Announcement banner text                                     |
| `auto_response`               | string    | ❌        | Chat auto-response mode                                      |
| `chat_enabled`                | boolean   | ❌        | Whether live chat is enabled                                 |
| `chat_in_replay_enabled`      | boolean   | ❌        | Whether chat messages are displayed during replay            |
| `chat_moderation_enabled`     | boolean   | ❌        | Whether chat moderation is enabled                           |
| `hearts_count_enabled`        | boolean   | ❌        | Whether like count is shown                                  |
| `replay_enabled`              | boolean   | ❌        | Whether replay is available after the livestream ends        |
| `replay_interactions_enabled` | boolean   | ❌        | Whether replay interactions are enabled                      |
| `replay_messaging_enabled`    | boolean   | ❌        | Whether viewers can post messages during replay              |
| `trailer_messaging_enabled`   | boolean   | ❌        | Whether viewers can post messages while watching the trailer |
| `transcription_enabled`       | boolean   | ❌        | Whether live transcription is enabled                        |
| `viewers_count_enabled`       | boolean   | ❌        | Whether viewer count is shown                                |
| `viewers_count_mode`          | string    | ❌        | Viewer-count mode: `"accumulated"` or `"concurrent"`         |
| `scheduled_at`                | string    | ✅        | ISO 8601 datetime when livestream is scheduled to start      |
| `started_at`                  | string    | ✅        | ISO 8601 datetime when livestream actually started           |
| `ended_at`                    | string    | ✅        | ISO 8601 datetime when livestream ended                      |
| `interactions`                | object\[] | ❌        | Non-deleted livestream interactions                          |

Each `interactions` item includes:

| Field                        | Type           | Description                                   |
| ---------------------------- | -------------- | --------------------------------------------- |
| `id`                         | string         | Interaction ID                                |
| `interaction_type`           | string         | Interaction type                              |
| `prompt`                     | string / null  | Prompt shown to viewers, when applicable      |
| `interactive_url`            | string / null  | URL used by link-style interactions           |
| `terms_and_conditions_url`   | string / null  | Terms URL configured for the interaction      |
| `terms_acceptance_required`  | boolean        | Whether terms acceptance is required          |
| `giveaway_celebration_emoji` | string         | Giveaway celebration emoji                    |
| `giveaway_description`       | string / null  | Giveaway description                          |
| `giveaway_winners_count`     | integer / null | Configured winner count                       |
| `collect_name`               | boolean        | Whether the interaction collects viewer name  |
| `collect_email`              | boolean        | Whether the interaction collects viewer email |
| `collect_phone`              | boolean        | Whether the interaction collects viewer phone |
| `sort_id`                    | integer / null | Display order                                 |
| `options`                    | object\[]      | Interaction options                           |
| `option_sets`                | object\[]      | Trivia/question option sets                   |
| `video_id`                   | string / null  | Video ID when linked to a video               |
| `live_stream_id`             | string         | Livestream ID                                 |
| `tally`                      | object / null  | Aggregate counts, when applicable             |
| `interaction_results`        | object\[]      | Always empty; use CSV export for result rows  |

The `interactions` array follows the livestream detail interaction shape and is used to discover `interaction_id` values for `responses_csv`. CSV URLs are not returned in this response.

**Status Values**

| Status      | Description                        |
| ----------- | ---------------------------------- |
| `idle`      | Livestream not yet started         |
| `active`    | Livestream currently in progress   |
| `paused`    | Livestream paused by host          |
| `replay`    | Livestream ended, replay available |
| `completed` | Replay window ended                |
| `expired`   | Livestream never started           |

#### 5.4. Get Livestream Error Responses <a href="#id-5.4.-get-livestream-error-responses" id="id-5.4.-get-livestream-error-responses"></a>

| Status Code        | Description                                                                  |
| ------------------ | ---------------------------------------------------------------------------- |
| `400 Bad Request`  | Invalid request format or malformed livestream ID                            |
| `401 Unauthorized` | Invalid or missing authentication token, or access denied to this livestream |
| `404 Not Found`    | Livestream not found                                                         |

**Error Response Format**:

`{ "error": "Error Message" }`

#### 5.5. Examples <a href="#id-5.5.-examples" id="id-5.5.-examples"></a>

**CURL Request**

```
curl
  -X GET
  "<https://api.firework.com/api/v1/live_streams/616dOp>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
```

**Example Response (Scheduled Livestream)**

```
{
  "id": "616dOp",
  "status": "idle",
  "event_name": "Product Launch Livestream",
  "event_description": "Join us for our exciting new product reveal!",
  "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
  "has_access_code": false,
  "announcement": null,
  "auto_response": "disabled",
  "chat_enabled": true,
  "chat_in_replay_enabled": true,
  "chat_moderation_enabled": false,
  "hearts_count_enabled": false,
  "replay_enabled": true,
  "replay_interactions_enabled": true,
  "replay_messaging_enabled": false,
  "trailer_messaging_enabled": false,
  "transcription_enabled": false,
  "viewers_count_enabled": true,
  "viewers_count_mode": "accumulated",
  "scheduled_at": "2025-03-19T09:41:00.000000Z",
  "started_at": null,
  "ended_at": null,
  "interactions": []
}
```

**Example Response (Active Livestream)**

```
{
  "id": "616dOp",
  "status": "active",
  "event_name": "Product Launch Livestream",
  "event_description": "Join us for our exciting new product reveal!",
  "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
  "has_access_code": false,
  "announcement": null,
  "auto_response": "disabled",
  "chat_enabled": true,
  "chat_in_replay_enabled": true,
  "chat_moderation_enabled": false,
  "hearts_count_enabled": false,
  "replay_enabled": true,
  "replay_interactions_enabled": true,
  "replay_messaging_enabled": false,
  "trailer_messaging_enabled": false,
  "transcription_enabled": false,
  "viewers_count_enabled": true,
  "viewers_count_mode": "accumulated",
  "scheduled_at": "2025-03-19T09:41:00.000000Z",
  "started_at": "2025-03-19T09:42:15.000000Z",
  "ended_at": null,
  "interactions": []
}
```

**Example Response (Completed Livestream)**

```
{
  "id": "616dOp",
  "status": "replay",
  "event_name": "Product Launch Livestream",
  "event_description": "Join us for our exciting new product reveal!",
  "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
  "has_access_code": false,
  "announcement": null,
  "auto_response": "disabled",
  "chat_enabled": true,
  "chat_in_replay_enabled": true,
  "chat_moderation_enabled": false,
  "hearts_count_enabled": false,
  "replay_enabled": true,
  "replay_interactions_enabled": true,
  "replay_messaging_enabled": false,
  "trailer_messaging_enabled": false,
  "transcription_enabled": false,
  "viewers_count_enabled": true,
  "viewers_count_mode": "accumulated",
  "scheduled_at": "2025-03-19T09:41:00.000000Z",
  "started_at": "2025-03-19T09:42:15.000000Z",
  "ended_at": "2025-03-19T11:30:00.000000Z",
  "interactions": [
    {
      "id": "7YDaNq",
      "interaction_type": "giveaway",
      "prompt": "Enter your email for the launch giveaway",
      "interactive_url": null,
      "terms_and_conditions_url": null,
      "terms_acceptance_required": false,
      "giveaway_celebration_emoji": "🎉",
      "giveaway_description": null,
      "giveaway_winners_count": null,
      "collect_name": false,
      "collect_email": true,
      "collect_phone": false,
      "sort_id": 1,
      "options": [],
      "option_sets": [],
      "video_id": null,
      "live_stream_id": "616dOp",
      "tally": null,
      "interaction_results": []
    },
    {
      "id": "2wK9Qa",
      "interaction_type": "poll",
      "prompt": "Which product should we demo next?",
      "interactive_url": null,
      "terms_and_conditions_url": null,
      "terms_acceptance_required": false,
      "giveaway_celebration_emoji": "🎉",
      "giveaway_description": null,
      "giveaway_winners_count": null,
      "collect_name": false,
      "collect_email": false,
      "collect_phone": false,
      "sort_id": 2,
      "options": [
        {
          "text": "Vacuum",
          "confetti": null,
          "is_correct": null,
          "entry_condition": null,
          "keyword": null
        },
        {
          "text": "Air purifier",
          "confetti": null,
          "is_correct": null,
          "entry_condition": null,
          "keyword": null
        }
      ],
      "option_sets": [],
      "video_id": null,
      "live_stream_id": "616dOp",
      "tally": {
        "Vacuum": 257,
        "Air purifier": 166
      },
      "interaction_results": []
    }
  ]
}
```

***

### 6. Update Livestream <a href="#id-6.-update-livestream" id="id-6.-update-livestream"></a>

Update mutable fields on an existing livestream.

> **Scope of v1**: This endpoint updates one livestream at a time. It is intended for metadata, display/settings, schedule, source, trailer, and tagged-product updates. It does not update live session state, streaming provider configuration, channel/feed-level settings, AI copilot setup, or real-time pinned products.

**Endpoint**: `PATCH /api/v1/live_streams/{live_stream_id}`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:write` (for OAuth apps)\
**Content Type**: `application/json`\
**Status**: Implemented

> **Scope note**: Updating `s3_key` requires a trailer file that was already uploaded through the public video upload APIs. The upload step requires `videos:write`.

#### 6.1. Request Headers <a href="#id-6.1.-request-headers" id="id-6.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |
| `Content-Type`  | Must be `application/json`            | ✅        |

#### 6.2. Path Parameters <a href="#id-6.2.-path-parameters" id="id-6.2.-path-parameters"></a>

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

#### 6.3. Request Body <a href="#id-6.3.-request-body" id="id-6.3.-request-body"></a>

All fields are optional, but the request body must include at least one supported field.

| Field                         | Type          | Required | Before 5-minute warmup | After 5-minute warmup | Description                                                                                      |
| ----------------------------- | ------------- | -------- | ---------------------- | --------------------- | ------------------------------------------------------------------------------------------------ |
| `caption`                     | string        | ❌        | ✅                      | ✅                     | New livestream title shown to viewers                                                            |
| `description`                 | string / null | ❌        | ✅                      | ✅                     | New livestream description. Use `null` to clear the description                                  |
| `access_code`                 | string / null | ❌        | ✅                      | ✅                     | Access code used to lock the video/livestream. Use `null` to unlock                              |
| `announcement`                | string / null | ❌        | ✅                      | ✅                     | Announcement banner text shown to viewers. Use `null` or an empty string to clear it             |
| `auto_response`               | string        | ❌        | ✅                      | ✅                     | Chat auto-response mode. Supported values: `"always_on"`, `"moderated"`, `"disabled"`            |
| `chat_enabled`                | boolean       | ❌        | ✅                      | ✅                     | Whether live chat is enabled                                                                     |
| `chat_in_replay_enabled`      | boolean       | ❌        | ✅                      | ✅                     | Whether chat messages are displayed during replay                                                |
| `chat_moderation_enabled`     | boolean       | ❌        | ✅                      | ✅                     | Whether chat moderation is enabled                                                               |
| `hearts_count_enabled`        | boolean       | ❌        | ✅                      | ✅                     | Whether like count is shown                                                                      |
| `replay_enabled`              | boolean       | ❌        | ✅                      | ✅                     | Whether replay is available after the livestream ends                                            |
| `replay_interactions_enabled` | boolean       | ❌        | ✅                      | ✅                     | Whether replay interactions are enabled                                                          |
| `replay_messaging_enabled`    | boolean       | ❌        | ✅                      | ✅                     | Whether viewers can post messages during replay                                                  |
| `trailer_messaging_enabled`   | boolean       | ❌        | ✅                      | ✅                     | Whether viewers can post messages while watching the trailer                                     |
| `transcription_enabled`       | boolean       | ❌        | ✅                      | ✅                     | Whether live transcription is enabled, when supported by the provider/source                     |
| `viewers_count_enabled`       | boolean       | ❌        | ✅                      | ✅                     | Whether viewer count is shown                                                                    |
| `viewers_count_mode`          | string        | ❌        | ✅                      | ✅                     | Viewer-count mode. Supported values: `"accumulated"` and `"concurrent"`                          |
| `scheduled_at`                | string        | ❌        | ✅                      | ❌                     | New UTC ISO 8601 datetime when the livestream is scheduled to start                              |
| `time_zone`                   | string / null | ❌        | ✅                      | ❌                     | IANA timezone identifier for the scheduled event, for example `America/New_York`                 |
| `stream_source`               | string        | ❌        | ✅                      | ❌                     | Source of the livestream. Supported values for v1: `"fw"` and `"external"`                       |
| `s3_key`                      | string        | ❌        | ✅                      | ❌                     | Pre-uploaded trailer key to replace the idle trailer                                             |
| `business_store_id`           | string        | ❌        | ✅                      | ❌                     | Encoded business store ID used to resolve `product_ids`                                          |
| `product_ids`                 | string\[]     | ❌        | ✅                      | ❌                     | Full replacement list of products tagged to the livestream trailer/video. Use `[]` to remove all |

**Request Rules:**

* This is a partial update. Omitted fields keep their current values.
* The 5-minute warmup window starts 5 minutes before `scheduled_at`.
* `caption`, `description`, `access_code`, and livestream instance settings can be updated before or after the livestream enters the warmup window.
* `scheduled_at`, `time_zone`, `stream_source`, `s3_key`, `business_store_id`, and `product_ids` can only be updated before the livestream enters the 5-minute warmup window.
* Livestream instance settings do not update channel/feed-level display settings or AI copilot setup.
* `scheduled_at` must be provided in UTC ISO 8601 format and must satisfy the same scheduling and maintenance-window restrictions as create.
* `stream_source` can only be changed when the resulting scheduled time is more than 5 minutes in the future.
* `s3_key` must be a valid uploaded video key owned by the target channel.
* `product_ids` replaces the full tagged-product list for the livestream trailer/video. Use the pin and unpin endpoints to change real-time highlighted products during an active livestream.
* `product_ids` follows the same identifier rules as `POST /api/v1/videos` and `POST /api/v1/live_streams`.
* `channel_id`, `provider`, `stream_key`, `stream_url`, `status`, `started_at`, and `ended_at` cannot be updated through this endpoint.

#### 6.4. Update Livestream Response <a href="#id-6.4.-update-livestream-response" id="id-6.4.-update-livestream-response"></a>

**Success Response**: `200 OK`

The response matches `GET /api/v1/live_streams/{live_stream_id}`. If the resulting livestream uses `stream_source: "external"`, the response also includes publish credentials.

| Field                         | Type    | Nullable | Description                                                                                 |
| ----------------------------- | ------- | -------- | ------------------------------------------------------------------------------------------- |
| `id`                          | string  | ❌        | Unique encoded identifier of the livestream                                                 |
| `status`                      | string  | ❌        | Current livestream state                                                                    |
| `event_name`                  | string  | ❌        | Name of the livestream event                                                                |
| `event_description`           | string  | ✅        | Description of the livestream event                                                         |
| `thumbnail_url`               | string  | ✅        | URL of the livestream thumbnail image                                                       |
| `has_access_code`             | boolean | ❌        | Whether the video/livestream is locked by an access code                                    |
| `announcement`                | string  | ✅        | Announcement banner text                                                                    |
| `auto_response`               | string  | ❌        | Chat auto-response mode                                                                     |
| `chat_enabled`                | boolean | ❌        | Whether live chat is enabled                                                                |
| `chat_in_replay_enabled`      | boolean | ❌        | Whether chat messages are displayed during replay                                           |
| `chat_moderation_enabled`     | boolean | ❌        | Whether chat moderation is enabled                                                          |
| `hearts_count_enabled`        | boolean | ❌        | Whether like count is shown                                                                 |
| `replay_enabled`              | boolean | ❌        | Whether replay is available after the livestream ends                                       |
| `replay_interactions_enabled` | boolean | ❌        | Whether replay interactions are enabled                                                     |
| `replay_messaging_enabled`    | boolean | ❌        | Whether viewers can post messages during replay                                             |
| `trailer_messaging_enabled`   | boolean | ❌        | Whether viewers can post messages while watching the trailer                                |
| `transcription_enabled`       | boolean | ❌        | Whether live transcription is enabled                                                       |
| `viewers_count_enabled`       | boolean | ❌        | Whether viewer count is shown                                                               |
| `viewers_count_mode`          | string  | ❌        | Viewer-count mode: `"accumulated"` or `"concurrent"`                                        |
| `scheduled_at`                | string  | ✅        | ISO 8601 datetime when livestream is scheduled to start                                     |
| `started_at`                  | string  | ✅        | ISO 8601 datetime when livestream actually started                                          |
| `ended_at`                    | string  | ✅        | ISO 8601 datetime when livestream ended                                                     |
| `stream_key`                  | string  | ✅        | Publish stream key. Returned when the resulting livestream uses `stream_source: "external"` |
| `stream_url`                  | string  | ✅        | Publish RTMP URL. Returned when the resulting livestream uses `stream_source: "external"`   |

#### 6.5. Update Livestream Error Responses <a href="#id-6.5.-update-livestream-error-responses" id="id-6.5.-update-livestream-error-responses"></a>

| Status Code                | Description                                                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `400 Bad Request`          | Invalid request body, no supported fields, malformed datetime, invalid `s3_key`, immutable field, or maintenance block |
| `401 Unauthorized`         | Invalid or missing authentication token                                                                                |
| `403 Forbidden`            | The authenticated user or OAuth app does not have access to the livestream                                             |
| `404 Not Found`            | Livestream not found                                                                                                   |
| `422 Unprocessable Entity` | Domain validation error, invalid products, or an update attempted outside the allowed livestream lifecycle             |

**Error Response Format**:

Simple request/auth errors return:

`{ "error": "Error Message" }`

Validation errors from changesets return:

`{ "errors": { "field_name": ["validation message"] } }`

#### 6.6. Examples <a href="#id-6.6.-examples" id="id-6.6.-examples"></a>

**Example 1: Update Livestream Metadata and Schedule**

```
curl
  -X PATCH
  "<https://api.firework.com/api/v1/live_streams/616dOp>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "caption": "Summer Sale Livestream - Encore", "description": "Updated schedule and event details", "scheduled_at": "2026-07-21T10:00:00.000000Z", "time_zone": "America/New_York" }'
```

**Example 2: Update Stream Source, Trailer, and Tagged Products**

```
curl
  -X PATCH
  "<https://api.firework.com/api/v1/live_streams/616dOp>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "stream_source": "external", "s3_key": "medias/business/AbCdEfG/channel/HiJkLmN/videos/public/original/1738152000-abcdefgh-trailer-v2.mp4", "business_store_id": "QwErTy", "product_ids": ["GeEk8y", "ABC123"] }'
```

**Example 3: Update Display Settings**

```
curl
  -X PATCH
  "<https://api.firework.com/api/v1/live_streams/616dOp>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "replay_enabled": true, "viewers_count_enabled": true, "viewers_count_mode": "accumulated", "hearts_count_enabled": false, "transcription_enabled": true, "access_code": "VIP123", "announcement": "Doors open in 5 minutes" }'
```

**Example Response (Metadata and Schedule Update)**

```
{
  "id": "616dOp",
  "status": "idle",
  "event_name": "Summer Sale Livestream - Encore",
  "event_description": "Updated schedule and event details",
  "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
  "has_access_code": false,
  "announcement": null,
  "auto_response": "disabled",
  "chat_enabled": true,
  "chat_in_replay_enabled": true,
  "chat_moderation_enabled": false,
  "hearts_count_enabled": false,
  "replay_enabled": true,
  "replay_interactions_enabled": true,
  "replay_messaging_enabled": false,
  "trailer_messaging_enabled": false,
  "transcription_enabled": false,
  "viewers_count_enabled": true,
  "viewers_count_mode": "accumulated",
  "scheduled_at": "2026-07-21T10:00:00.000000Z",
  "started_at": null,
  "ended_at": null
}
```

**Example Response (External Stream Source Update)**

```
{
  "id": "616dOp",
  "status": "idle",
  "event_name": "Summer Sale Livestream",
  "event_description": "Join us for our scheduled commerce event",
  "thumbnail_url": "<https://cdn.firework.com/live_streams/616dOp/thumbnail.jpg>",
  "has_access_code": false,
  "announcement": null,
  "auto_response": "disabled",
  "chat_enabled": true,
  "chat_in_replay_enabled": true,
  "chat_moderation_enabled": false,
  "hearts_count_enabled": false,
  "replay_enabled": true,
  "replay_interactions_enabled": true,
  "replay_messaging_enabled": false,
  "trailer_messaging_enabled": false,
  "transcription_enabled": false,
  "viewers_count_enabled": true,
  "viewers_count_mode": "accumulated",
  "scheduled_at": "2026-07-20T10:00:00.000000Z",
  "started_at": null,
  "ended_at": null,
  "stream_key": "sk_live_abc123",
  "stream_url": "rtmps://global-live.mux.com:443/app"
}
```

#### 6.7. Error Responses <a href="#id-6.7.-error-responses" id="id-6.7.-error-responses"></a>

```
// 400 Bad Request - No supported fields 
{"error": "At least one supported field is required" }

// 400 Bad Request - Immutable field 
{ "error": "status cannot be updated" } 

// 422 Unprocessable Entity - Livestream is too close to start 
{ "error": "scheduled_at, time_zone, stream_source, s3_key, business_store_id, product_ids can only be updated before the warmup window" } 

// 422 Unprocessable Entity - stream_source update too close to start 
{ "error": "stream_source cannot be changed less than 300 seconds prior to the livestream" }
```

***

### 7. Pin Products to Livestream <a href="#id-7.-pin-products-to-livestream" id="id-7.-pin-products-to-livestream"></a>

Pin (highlight) one or more products during an active livestream. The pinned products will be prominently displayed to all viewers watching the livestream in real-time.

**Endpoint**: `POST /api/v1/live_streams/{live_stream_id}/pin_product`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:write` (for OAuth apps)\
**Content Type**: `application/json`

> **Important**: This endpoint can only be used when the livestream status is `active` or `paused`. Products cannot be pinned to livestreams in `idle`, `replay`, `completed`, or `expired` status.

> **Note**: The system supports up to 3 products pinned simultaneously(override).

#### 7.1. Request Headers <a href="#id-7.1.-request-headers" id="id-7.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |
| `Content-Type`  | Must be `application/json`            | ✅        |

#### 7.2. Path Parameters <a href="#id-7.2.-path-parameters" id="id-7.2.-path-parameters"></a>

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

#### 7.3. Request Body <a href="#id-7.3.-request-body" id="id-7.3.-request-body"></a>

| Parameter     | Type      | Required | Description                                              |
| ------------- | --------- | -------- | -------------------------------------------------------- |
| `product_ids` | string\[] | ✅        | Array of Firework product IDs or product unit IDs to pin |

> **Note**: Both Firework product IDs and product unit IDs are supported. You can obtain these IDs from the Firework product catalog or product management APIs. The array must contain at least 1 item and maximum 3 items per request.

#### 7.4. Pin Products Response <a href="#id-7.4.-pin-products-response" id="id-7.4.-pin-products-response"></a>

**Success Response**: `200 OK`

| Field                | Type      | Description                                           |
| -------------------- | --------- | ----------------------------------------------------- |
| `pinned_product_ids` | string\[] | Array of product IDs that were pinned in this request |

> **Note**: Upon receiving this success response, all products have already been broadcasted to all viewers. There is no delay - the pins are live immediately.

#### 7.5. Pin Products Error Responses <a href="#id-7.5.-pin-products-error-responses" id="id-7.5.-pin-products-error-responses"></a>

| Status Code                | Description                                                                  |
| -------------------------- | ---------------------------------------------------------------------------- |
| `400 Bad Request`          | Invalid request format, missing product\_ids, or malformed IDs               |
| `401 Unauthorized`         | Invalid or missing authentication token, or access denied to this livestream |
| `404 Not Found`            | Livestream not found or no valid products found                              |
| `422 Unprocessable Entity` | Livestream is not active (must be in `active` or `paused` status)            |

#### 7.6. Example Requests <a href="#id-7.6.-example-requests" id="id-7.6.-example-requests"></a>

**Example 1: Pin Products**

```
curl
  -X POST
  "<https://api.firework.com/api/v1/live_streams/616dOp/pin_product>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "product_ids": ["GeEk8y", "ABC123", "XYZ789"] }'
```

**Success Response (200 OK)**

```
{
  "pinned_product_ids": ["GeEk8y", "ABC123", "XYZ789"]
}
```

**Example 2: Pin Product Unit (Variant/SKU)**

```
curl
  -X POST
  "<https://api.firework.com/api/v1/live_streams/616dOp/pin_product>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "product_ids": ["082YOm"] }'
```

**Success Response (200 OK)**

```
{ 
    "pinned_product_ids": ["082YOm"] 
}
```

> **Note**: In this example, `082YOm` is a product unit ID representing a specific variant (e.g., "Black / Standard"). Pinning a product unit highlights that specific variant during the livestream.

#### 7.7. Error Responses <a href="#id-7.7.-error-responses" id="id-7.7.-error-responses"></a>

```
// 422 Unprocessable Entity - Livestream not active 
{ "error": "Livestream must be active or paused to pin products" } 

// 404 Not Found - Livestream not found 
{ "error": "Livestream not found" } 

// 400 Bad Request - Missing product_ids 
{ "error": "product_ids is required" } 

// 400 Bad Request - Empty array 
{ "error": "product_ids cannot be empty" } 

// 400 Bad Request - More than 3 products 
{ "error": "Maximum 3 products allowed per request" }
```

***

### 8. Unpin Products from Livestream <a href="#id-8.-unpin-products-from-livestream" id="id-8.-unpin-products-from-livestream"></a>

Remove one or more pinned (highlighted) products from a livestream. This clears the product highlights for all viewers.

**Endpoint**: `POST /api/v1/live_streams/{live_stream_id}/unpin_product`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:write` (for OAuth apps)\
**Content Type**: `application/json`

#### 8.1. Request Headers <a href="#id-8.1.-request-headers" id="id-8.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |
| `Content-Type`  | Must be `application/json`            | ✅        |

#### 8.2. Path Parameters <a href="#id-8.2.-path-parameters" id="id-8.2.-path-parameters"></a>

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

#### 8.3. Request Body <a href="#id-8.3.-request-body" id="id-8.3.-request-body"></a>

| Parameter     | Type      | Required | Description                                                |
| ------------- | --------- | -------- | ---------------------------------------------------------- |
| `product_ids` | string\[] | ✅        | Array of Firework product IDs or product unit IDs to unpin |

> **Note**: Provide the Firework product IDs or product unit IDs (variant/SKU IDs) you want to remove from the highlighted set. The array must contain at least 1 item and maximum 3 items per request.

#### 8.4. Unpin Products Response <a href="#id-8.4.-unpin-products-response" id="id-8.4.-unpin-products-response"></a>

**Success Response**: `200 OK`

| Field                  | Type      | Description                                             |
| ---------------------- | --------- | ------------------------------------------------------- |
| `unpinned_product_ids` | string\[] | Array of product IDs that were unpinned in this request |

#### 8.5. Unpin Products Error Responses <a href="#id-8.5.-unpin-products-error-responses" id="id-8.5.-unpin-products-error-responses"></a>

| Status Code        | Description                                                                  |
| ------------------ | ---------------------------------------------------------------------------- |
| `400 Bad Request`  | Invalid request format or malformed livestream ID                            |
| `401 Unauthorized` | Invalid or missing authentication token, or access denied to this livestream |
| `404 Not Found`    | Livestream not found or no valid products found                              |

#### 8.6. Example Requests <a href="#id-8.6.-example-requests" id="id-8.6.-example-requests"></a>

**Example 1: Unpin Products**

```
curl
  -X POST
  "<https://api.firework.com/api/v1/live_streams/616dOp/unpin_product>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "product_ids": ["GeEk8y", "ABC123", "XYZ789"] }'
```

**Success Response (200 OK)**

```
{
  "unpinned_product_ids": ["GeEk8y", "ABC123", "XYZ789"]
}
```

**Example 2: Unpin Product Unit (Variant/SKU)**

```
curl
  -X POST
  "<https://api.firework.com/api/v1/live_streams/616dOp/unpin_product>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
  -d '{ "product_ids": ["082YOm"] }'
```

**Success Response (200 OK)**

```
{
  "unpinned_product_ids": ["082YOm"]
}
```

#### 8.7. Error Responses <a href="#id-8.7.-error-responses" id="id-8.7.-error-responses"></a>

```
// 404 Not Found - Livestream not found 
{ "error": "Livestream not found" } 

// 404 Not Found - No valid products found 
{ "error": "No valid products found to unpin" } 

// 400 Bad Request - Missing product_ids 
{ "error": "product_ids is required" } 

// 400 Bad Request - Empty array 
{ "error": "product_ids cannot be empty" }
```

***

### 9. End Livestream <a href="#id-9.-end-livestream" id="id-9.-end-livestream"></a>

End an active or paused livestream. The livestream status will transition to `replay`.

**Endpoint**: `PATCH /api/v1/live_streams/{live_stream_id}/end`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:write` (for OAuth apps)\
**Content Type**: `application/json`

> **Important**: This action is irreversible. Once ended, the livestream cannot return to `active` status.

#### 9.1. Request Headers <a href="#id-9.1.-request-headers" id="id-9.1.-request-headers"></a>

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |
| `Content-Type`  | Must be `application/json`            | ✅        |

#### 9.2. Path Parameters <a href="#id-9.2.-path-parameters" id="id-9.2.-path-parameters"></a>

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

#### 9.3. Request Body <a href="#id-9.3.-request-body" id="id-9.3.-request-body"></a>

No request body is required.

#### 9.4. End Livestream Response <a href="#id-9.4.-end-livestream-response" id="id-9.4.-end-livestream-response"></a>

**Success Response**: `200 OK`

| Field            | Type   | Description                                  |
| ---------------- | ------ | -------------------------------------------- |
| `live_stream_id` | string | The livestream that was ended                |
| `status`         | string | Always `"ended"` once the operation succeeds |

#### 9.5. End Livestream Error Responses <a href="#id-9.5.-end-livestream-error-responses" id="id-9.5.-end-livestream-error-responses"></a>

| Status Code        | Description                                                                  |
| ------------------ | ---------------------------------------------------------------------------- |
| `400 Bad Request`  | Livestream already ended or in a non-active state                            |
| `401 Unauthorized` | Invalid or missing authentication token, or access denied to this livestream |
| `404 Not Found`    | Livestream not found                                                         |

#### 9.6. Examples <a href="#id-9.6.-examples" id="id-9.6.-examples"></a>

**Example 1: End an Active Livestream**

**CURL Request**

```
curl
  -X PATCH
  "<https://api.firework.com/api/v1/live_streams/616dOp/end>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  -H "Content-Type: application/json"
```

**Success Response (200 OK)**

```
{
  "live_stream_id": "616dOp",
  "status": "ended"
}
```

**Error Response Examples**

```
// 404 Not Found - Livestream not found 
{ "error": "Livestream not found" } 

// 400 Bad Request - Livestream already ended 
{ "error": "Livestream must be active or paused to end" }
```

### 10. Export Livestream Engagement Results <a href="#id-10.-export-livestream-engagement-results" id="id-10.-export-livestream-engagement-results"></a>

Export interaction results and livestream comments after a livestream has ended. These endpoints mirror the result-download use case that is currently available in CMS, while keeping interaction setup and moderation workflows in CMS.

**Status**: Implemented\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:read` (for OAuth apps)

> **Scope note**: Engagement exports can contain viewer-entered data such as emails, names, answers, and comments. Grant `livestreams:read` only to OAuth apps that are allowed to export post-event livestream result data.

> **Availability note**: CSV result downloads are available only after the livestream has ended. If the livestream is still scheduled, idle, active, paused, or otherwise not ended, the CSV endpoints return `409 Conflict`.

#### 10.1. Recommended Workflow <a href="#id-10.1.-recommended-workflow" id="id-10.1.-recommended-workflow"></a>

1. Create and manage the livestream through the existing livestream APIs.
2. End the livestream through CMS or `PATCH /api/v1/live_streams/{live_stream_id}/end`.
3. Call `GET /api/v1/live_streams/{live_stream_id}` and read the `interactions` array.
4. Download each interaction result file by calling `responses_csv` with the livestream ID and interaction ID.
5. Download chat/comment history by calling `comments_csv` with the livestream ID when comment data is needed.

#### 10.2. Download Interaction Responses CSV <a href="#id-10.2.-download-interaction-responses-csv" id="id-10.2.-download-interaction-responses-csv"></a>

Download stored response rows for a single livestream interaction.

**Endpoint**: `GET /api/v1/live_streams/{live_stream_id}/interactions/{interaction_id}/responses_csv`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:read`\
**Response Content Type**: `text/csv`

**Request Headers**

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |

**Path Parameters**

| Parameter        | Type   | Required | Description                              |
| ---------------- | ------ | -------- | ---------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream  |
| `interaction_id` | string | ✅        | The unique identifier of the interaction |

**CSV Columns**

| Column               | Description                                                     |
| -------------------- | --------------------------------------------------------------- |
| `id`                 | Interaction response row ID                                     |
| `interaction_id`     | Interaction ID                                                  |
| `interaction_type`   | Interaction type                                                |
| `prompt`             | Interaction prompt                                              |
| `response`           | Viewer response value, such as poll choice or question answer   |
| `email`              | Viewer email, when collected                                    |
| `name`               | Viewer name, when collected                                     |
| `guest_id`           | Guest identifier                                                |
| `user_id`            | Registered user identifier, when available                      |
| `country`            | Viewer country, when available                                  |
| `terms_accepted`     | Whether the viewer accepted terms for the interaction           |
| `winner`             | Whether the row is marked as a giveaway winner, when applicable |
| `is_correct`         | Whether the trivia answer is correct, when applicable           |
| `live_stream_status` | Whether the response was submitted during `active` or `replay`  |
| `submitted_at`       | ISO 8601 timestamp when the response was submitted              |

**Example**

```
curl
  -X GET
  "<https://api.firework.com/api/v1/live_streams/616dOp/interactions/7YDaNq/responses_csv>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  id,interaction_id,interaction_type,prompt,response,email,name,guest_id,user_id,country,terms_accepted,winner,is_correct,live_stream_status,submitted_at
  81NmQp,7YDaNq,giveaway,Enter your email for the launch giveaway,,viewer@example.com,Jamie,guest_123,,US,true,true,,active,2026-05-11T14:25:08Z
```

#### 10.3. Download Livestream Comments CSV <a href="#id-10.3.-download-livestream-comments-csv" id="id-10.3.-download-livestream-comments-csv"></a>

Download chat/comment history for an ended livestream. Use this endpoint for comment exports and for reviewing comment-giveaway source messages.

**Endpoint**: `GET /api/v1/live_streams/{live_stream_id}/comments_csv`\
**Authentication**: Bearer token required (OAuth 2.0 Client Credentials)\
**Required Scope**: `livestreams:read`\
**Response Content Type**: `text/csv`

**Request Headers**

| Name            | Description                           | Required |
| --------------- | ------------------------------------- | -------- |
| `Authorization` | Bearer token: `Bearer {ACCESS_TOKEN}` | ✅        |

**Path Parameters**

| Parameter        | Type   | Required | Description                             |
| ---------------- | ------ | -------- | --------------------------------------- |
| `live_stream_id` | string | ✅        | The unique identifier of the livestream |

**CSV Columns**

| Column                      | Description                                            |
| --------------------------- | ------------------------------------------------------ |
| `message_id`                | Livestream chat message ID                             |
| `elapsed_time`              | Elapsed livestream time when the message was sent      |
| `user_type`                 | Sender type                                            |
| `private_thread_owner_id`   | Private-thread owner ID, when applicable               |
| `guest_id`                  | Guest identifier                                       |
| `user_id`                   | Registered user identifier, when available             |
| `username`                  | Display name                                           |
| `title`                     | Livestream title                                       |
| `original_text`             | Original comment text                                  |
| `reply_to_message_id`       | Parent message ID, when this message is a reply        |
| `reply_to_message_text`     | Parent message text, when available                    |
| `reply_to_message_username` | Parent message sender, when available                  |
| `inserted_at`               | ISO 8601 timestamp when the message was created        |
| `status`                    | Message moderation status                              |
| `live_stream_status`        | Whether the message was sent during `live` or `replay` |

**Example**

```
curl
  -X GET
  "<https://api.firework.com/api/v1/live_streams/616dOp/comments_csv>"
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
  message_id,elapsed_time,user_type,private_thread_owner_id,guest_id,user_id,username,title,original_text,reply_to_message_id,reply_to_message_text,reply_to_message_username,inserted_at,status,live_stream_status
  msg_123,00:04:18,viewer,,guest_123,,Jamie,Spring Launch,Love this product!,,,,2026-05-11T14:18:42Z,approved,live
```

#### 10.4. Error Responses <a href="#id-10.4.-error-responses" id="id-10.4.-error-responses"></a>

| Status Code        | Description                                                                   |
| ------------------ | ----------------------------------------------------------------------------- |
| `400 Bad Request`  | Invalid request, unsupported interaction type, or malformed ID                |
| `401 Unauthorized` | Invalid or missing authentication token                                       |
| `403 Forbidden`    | Missing `livestreams:read` scope or access denied to this business livestream |
| `404 Not Found`    | Livestream not found, or interaction not found on the specified livestream    |
| `409 Conflict`     | Livestream has not ended yet, so result exports are not available             |

**Error Response Examples**

```
// 403 Forbidden - Missing livestreams:read scope 
{ "error": "insufficient_scope", "required_scope": "livestreams:read" } 

// 404 Not Found - Interaction does not belong to livestream 
{ "error": "Interaction not found" } 

// 409 Conflict - Livestream has not ended yet 
{ "error": "Livestream engagement results are available only after the livestream has ended" }
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.firework.com/firework-for-developers/api/livestreams.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
