packages/docs/rest/stream.md
| Method | Path | Description |
|---|---|---|
| POST | /api/stream/live | Start streaming (destination-managed) |
| POST | /api/stream/offline | Stop the active stream |
| GET | /api/stream/status | Current stream health and configuration |
| POST | /api/stream/start | Start streaming (direct RTMP) |
| POST | /api/stream/stop | Stop FFmpeg process |
| POST | /api/stream/frame | Pipe a raw image frame to FFmpeg |
| POST | /api/stream/volume | Set audio volume |
| POST | /api/stream/mute | Mute audio |
| POST | /api/stream/unmute | Unmute audio |
| GET | /api/streaming/destinations | List configured streaming destinations |
| POST | /api/streaming/destination | Set active streaming destination |
| GET | /api/stream/overlay-layout | Get overlay layout |
| POST | /api/stream/overlay-layout | Save overlay layout |
| GET | /api/stream/voice | Get voice (TTS) configuration |
| POST | /api/stream/voice | Save voice settings |
| POST | /api/stream/voice/speak | Manually trigger TTS on live stream |
| GET | /api/stream/settings | Get visual stream settings |
| POST | /api/stream/settings | Save visual stream settings |
POST /api/stream/live
Starts streaming using the configured destination adapter. Fetches RTMP credentials automatically from the active destination plugin.
Response:
{
"ok": true,
"live": true,
"rtmpUrl": "rtmp://live.twitch.tv/app",
"inputMode": "pipe",
"audioSource": "tts",
"destination": "twitch"
}
POST /api/stream/offline
Stops the active stream and notifies the destination plugin.
Response:
{ "ok": true, "live": false }
GET /api/stream/status
Returns current stream health and configuration.
Response:
{
"ok": true,
"running": true,
"ffmpegAlive": true,
"uptime": 3742,
"frameCount": 112260,
"volume": 80,
"muted": false,
"audioSource": "tts",
"inputMode": "pipe",
"destination": "twitch"
}
POST /api/stream/start
Backward-compatible explicit RTMP start with full parameter control. Prefer POST /api/stream/live for destination-managed starts.
Request body:
{
"rtmpUrl": "rtmp://live.twitch.tv/app",
"rtmpKey": "live_abc123",
"inputMode": "testsrc",
"resolution": "1280x720",
"bitrate": "2500k",
"framerate": 30
}
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
rtmpUrl | string | yes | — | Must start with rtmp:// or rtmps:// |
rtmpKey | string | yes | — | Stream key |
inputMode | string | no | "testsrc" | "testsrc", "avfoundation", or "pipe" |
resolution | string | no | "1280x720" | Must match /^\d{3,4}x\d{3,4}$/ |
bitrate | string | no | "2500k" | Must match /^\d+k$/ |
framerate | number | no | 30 | Integer 1–60 |
Response:
{ "ok": true, "message": "Stream started" }
POST /api/stream/stop
Stops the active FFmpeg process and returns session uptime.
Response:
{ "ok": true, "uptime": 3742 }
uptime is in seconds.
POST /api/stream/frame
Pipes a raw JPEG/image frame buffer to FFmpeg. Used in pipe capture mode (desktop runtime page capture).
Request: Raw binary body (max 2 MB).
Response: 200 with empty body.
Errors: 400 empty body; 503 stream not running.
GET /api/stream/source
Returns the current stream capture source configuration.
Response:
{
"source": {
"type": "stream-tab",
"url": "https://example.com"
}
}
| Field | Type | Description |
|---|---|---|
source.type | string | Source type: "stream-tab", "screen", "custom", etc. |
source.url | string|undefined | Custom URL when applicable |
POST /api/stream/source
Switch the stream capture source.
Request body:
{
"sourceType": "stream-tab",
"customUrl": "https://example.com"
}
| Field | Type | Required | Description |
|---|---|---|---|
sourceType | string | no | Source type (default: "stream-tab") |
customUrl | string | no | Custom URL for the source |
Response:
{
"ok": true,
"source": {
"type": "stream-tab"
}
}
POST /api/stream/volume
Request body:
{ "volume": 80 }
volume is an integer 0–100.
Response:
{ "ok": true, "volume": 80, "muted": false }
POST /api/stream/mute
Response:
{ "ok": true, "muted": true, "volume": 80 }
POST /api/stream/unmute
Response:
{ "ok": true, "muted": false, "volume": 80 }
GET /api/streaming/destinations
Returns all configured streaming destinations from the multi-destination registry. Each destination indicates whether it is the currently active target.
Response:
{
"ok": true,
"destinations": [
{ "id": "twitch", "name": "Twitch", "active": true },
{ "id": "youtube", "name": "YouTube", "active": false },
{ "id": "pumpfun", "name": "pump.fun", "active": false },
{ "id": "x", "name": "X/Twitter", "active": false }
]
}
| Field | Type | Description |
|---|---|---|
destinations[].id | string | Unique destination identifier |
destinations[].name | string | Human-readable destination name |
destinations[].active | boolean | Whether this destination is currently selected for streaming |
The active destination defaults to the first registered destination when no explicit selection has been made.
POST /api/streaming/destination
Switches the active streaming destination at runtime. The selected destination is used for subsequent POST /api/stream/live calls to resolve RTMP credentials automatically.
Request body:
{ "destinationId": "twitch" }
| Field | Type | Required | Description |
|---|---|---|---|
destinationId | string | yes | ID of the destination to activate (must match a registered destination) |
Response (200):
{
"ok": true,
"destination": { "id": "twitch", "name": "Twitch" }
}
Errors:
| Status | Body | Condition |
|---|---|---|
400 | { "error": "destinationId is required" } | Missing or empty destinationId |
404 | { "error": "Unknown destination: <id>" } | No registered destination matches the provided ID |
500 | { "error": "<message>" } | Unexpected server error |
GET /api/stream/overlay-layout
Query params:
| Param | Type | Required | Description |
|---|---|---|---|
destination | string | no | Destination ID for per-destination layouts |
Response:
{
"ok": true,
"layout": {
"version": 1,
"widgets": [...]
},
"destinationId": "twitch"
}
POST /api/stream/overlay-layout
Request body:
{
"layout": {
"version": 1,
"widgets": [...]
}
}
Response:
{ "ok": true }
GET /api/stream/voice
Returns voice configuration and current speaking status.
Response:
{
"ok": true,
"enabled": true,
"autoSpeak": true,
"provider": "elevenlabs",
"configuredProvider": "elevenlabs",
"hasApiKey": true,
"isSpeaking": false,
"isAttached": true
}
POST /api/stream/voice
Request body:
{
"enabled": true,
"autoSpeak": true,
"provider": "elevenlabs"
}
Response:
{ "ok": true }
POST /api/stream/voice/speak
Manually trigger TTS on the live stream.
Request body:
{ "text": "Hello, stream!" }
| Constraint | Value |
|---|---|
| Max text length | 2000 characters |
| Rate limit | One at a time (429 if speaking) |
| Requires | Stream must be live, TTS bridge attached |
Response:
{ "ok": true }
Errors: 400 text missing/too long; 429 already speaking; 503 bridge not attached.
GET /api/stream/source
Returns the current stream input source configuration.
Response:
{
"source": {
"type": "stream-tab",
"url": null
}
}
POST /api/stream/source
Switch the stream input source.
Request body:
{
"sourceType": "custom-url",
"customUrl": "https://example.com/stream"
}
| Field | Type | Required | Description |
|---|---|---|---|
sourceType | string | no | Source type (default: "stream-tab") |
customUrl | string | no | Custom URL for the source (only used with custom-url type) |
Response:
{
"ok": true,
"source": {
"type": "custom-url",
"url": "https://example.com/stream"
}
}
GET /api/stream/settings
Response:
{
"ok": true,
"settings": {
"theme": "eliza",
"avatarIndex": 0
}
}
POST /api/stream/settings
Request body:
{
"settings": {
"theme": "eliza",
"avatarIndex": 2
}
}
Response:
{ "ok": true }