Documentation Index
Fetch the complete documentation index at: https://docs.agentbot.raveculture.xyz/llms.txt
Use this file to discover all available pages before exploring further.
Streaming API
Endpoints for live video and audio streaming via Mux, DJ session management, video generation, and streaming webhook processing.
Session lifecycle
A DJ session transitions through several statuses during its lifecycle. The active and live statuses are both treated as current sessions — meaning the session is considered in-progress and occupies a streaming slot for the wallet. Endpoints that check for an existing session, end sessions, or authorize session access treat both statuses uniformly.
| Status | Meaning |
|---|
active | Session created, waiting for or receiving an ingest connection |
live | Mux stream is active and playback is available to listeners |
ended | Session stopped by the DJ (replay assets deleted) |
archived | Session stopped by the DJ with replay retention (assets preserved, credits charged) |
auto-ended | Session expired (exceeded the 2-hour maximum) or cleaned up automatically |
List live streams
Returns all currently active Mux live streams. No authentication required.
The availability field reflects the station state:
live — Mux is healthy and at least one DJ is currently broadcasting.
idle — Mux is healthy but no DJs are live (for example, after the most recent set ended).
degraded — Mux credentials are missing or the Mux API returned an error. The response falls back to cached session data from the database.
When Mux is available, active streams are enriched with metadata from the corresponding DJ session record (matched by stream ID). This means fields like name, wallet, playbackId, and startedAt can resolve from session data even when Mux stream metadata is sparse or missing.
Response
{
"djs": [
{
"id": "aB1cD2eF3g",
"name": "DJ Rave",
"wallet": "0x1234...abcd",
"playbackId": "xYz789",
"streamKey": "sk-ab12-cd34-ef56",
"status": "active",
"startedAt": "2026-04-11T00:00:00.000Z",
"source": "mux",
"hlsUrl": "https://stream.mux.com/xYz789.m3u8",
"embedUrl": "https://stream.mux.com/xYz789.html"
}
],
"count": 1,
"primaryDj": {
"id": "aB1cD2eF3g",
"name": "DJ Rave",
"wallet": "0x1234...abcd",
"playbackId": "xYz789",
"streamKey": "sk-ab12-cd34-ef56",
"status": "active",
"startedAt": "2026-04-11T00:00:00.000Z",
"source": "mux",
"hlsUrl": "https://stream.mux.com/xYz789.m3u8",
"embedUrl": "https://stream.mux.com/xYz789.html"
},
"availability": "live",
"distribution": {
"origin": {
"status": "active",
"playbackId": "xYz789",
"hlsUrl": "https://stream.mux.com/xYz789.m3u8"
},
"firstParty": {
"status": "healthy",
"pageUrl": "https://basefm.space",
"note": null
},
"relays": [],
"requiredRelayStatus": "healthy",
"overallStatus": "healthy"
}
}
| Field | Type | Description |
|---|
djs[].id | string | Mux live stream ID |
djs[].name | string | DJ display name. Resolved from Mux stream metadata first, then from the DJ session record, and defaults to Anonymous DJ if neither is available. |
djs[].wallet | string | null | DJ wallet address. Resolved from Mux stream metadata first, then from the DJ session record. |
djs[].playbackId | string | null | Mux playback ID. Resolved from the Mux stream first, then from the DJ session record. |
djs[].streamKey | string | null | RTMP stream key. null when the DJ is served from the session cache. |
djs[].status | string | Stream status (active) |
djs[].startedAt | number | string | ISO 8601 string when a matching DJ session record exists, otherwise a Unix timestamp from the Mux stream. Session-cache entries always return an ISO 8601 string. |
djs[].source | string | Where the DJ data was resolved from: mux (live Mux API) or session-cache (database fallback) |
djs[].hlsUrl | string | null | HLS playlist URL for playback |
djs[].embedUrl | string | null | Embeddable player URL |
count | number | Number of live DJs |
primaryDj | object | null | The first DJ in the list, or null when no DJs are live |
availability | string | Stream availability and data source status. One of live (at least one DJ is broadcasting, served from Mux), idle (Mux is healthy but no DJ is currently live — for example, after a set ended), or degraded (Mux API or credentials are unavailable and the response is served from the session cache). |
distribution | object | Distribution state for the baseFM station. See distribution for the full shape. |
distribution.origin.status | string | Origin stream status. active (a DJ is broadcasting), idle (Mux is healthy but no DJ is live — the normal off-air state), or degraded (Mux API or credentials are unavailable). |
distribution.origin.playbackId | string | null | Mux playback ID for the origin stream |
distribution.origin.hlsUrl | string | null | HLS URL for the origin stream |
distribution.firstParty | object | Health status of the first-party baseFM page |
distribution.firstParty.status | string | One of pending, healthy, degraded, failed, or stopped |
distribution.relays | array | Configured relay destinations and their health |
distribution.requiredRelayStatus | string | Aggregated status of all required relays |
distribution.overallStatus | string | Overall distribution health across origin, first-party page, and relays |
When the response has "availability": "degraded", it may also include an error field with a human-readable description of why the Mux API could not be reached. An "availability": "idle" response is the normal off-air state and does not include an error field.
Errors
| Code | Description |
|---|
| 500 | Internal error and no cached sessions are available |
When Mux credentials are missing or the Mux API is unreachable, the endpoint returns cached live sessions from the database with "availability": "degraded" and HTTP status 200 (as long as cached data exists). A 500 is only returned when both the Mux API and the session cache are unavailable.
Create live stream
Creates a new Mux live stream. Access is granted if the caller meets any of the following conditions:
- The wallet holds at least 2,500,000 BASEFM tokens on the Base network, or
- The caller has an active community guest pass (available to Builder and Whale tier claimed holders via the community program), or
- The caller pays a $5 USDC session fee on Base by including a verifying
txHash in the request
Mux live stream creation only arms the stream — it is not actually live until OBS or ffmpeg connects to the RTMP ingest. Use the
Stream status endpoint to confirm ingest is up.
The wallet address used for BASEFM token verification must be a Coinbase Smart Wallet on Base. The platform enforces smartWalletOnly as its connector policy — injected wallets (such as MetaMask) are not supported for DJ streaming.
Request body
| Field | Type | Required | Description |
|---|
wallet | string | Yes | Wallet address of the DJ. Used for BASEFM token balance verification. |
name | string | No | DJ display name (default: DJ) |
city | string | No | City or location label for the DJ. Stored in session metadata and displayed on the dashboard. Trimmed and capped at 80 characters. |
txHash | string | Conditional | Transaction hash for the $5 USDC paid session. Required when the caller does not hold the BASEFM token threshold and does not have a community guest pass. The transfer must be to the platform wallet for the full fee from the request wallet. |
Response (200) — new stream
{
"success": true,
"stream": {
"id": "aB1cD2eF3g",
"name": "DJ Rave",
"wallet": "0x1234...abcd",
"streamKey": "sk-ab12-cd34-ef56",
"rtmpUrl": "rtmp://global-live.mux.com:5222/app",
"fullRtmpUrl": "rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56",
"playbackId": "xYz789",
"status": "created"
},
"session": {
"id": 42,
"wallet": "0x1234...abcd",
"maxDuration": 7200,
"remaining": 7200,
"expiresAt": "2026-04-11T02:00:00.000Z",
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
},
"ffmpeg": {
"command": "ffmpeg -re -i \"/path/to/set.mp3\" -c:a aac -b:a 128k -ar 44100 -ac 2 -f flv \"rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56\"",
"audioOnlyCommand": "ffmpeg -re -i \"/path/to/set.mp3\" -c:a aac -b:a 128k -ar 44100 -ac 2 -f flv \"rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56\"",
"playlistCommand": "ffmpeg -re -f concat -safe 0 -i \"/tmp/basefm-playlist.txt\" -c:a aac -b:a 128k -ar 44100 -ac 2 -f flv \"rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56\"",
"artworkCommand": "ffmpeg -re -loop 1 -i \"https://.../basefm-artwork.jpg\" -f lavfi -i anullsrc=... -c:v libx264 -preset veryfast -tune stillimage -pix_fmt yuv420p -vf \"scale=1280:720:...\" -g 60 -r 30 -b:v 3500k -maxrate 4500k -bufsize 7000k -c:a aac -b:a 256k -ar 44100 -ac 2 -f flv \"rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56\"",
"inputHint": "Default command is audio-only. Use playlistCommand for an ffmpeg concat playlist, or artworkCommand when the DJ explicitly wants a video track with the baseFM standby image."
}
}
Response (200) — recovery (existing session)
When the authenticated caller already owns a non-expired current session, the endpoint returns the existing stream control payload instead of creating a duplicate stream. This lets DJs recover after a refresh without losing their controls or accidentally creating a new stream.
{
"success": true,
"reconnected": true,
"stream": {
"id": "aB1cD2eF3g",
"name": "DJ Rave",
"wallet": "0x1234...abcd",
"streamKey": "sk-ab12-cd34-ef56",
"rtmpUrl": "rtmp://global-live.mux.com:5222/app",
"fullRtmpUrl": "rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56",
"playbackId": "xYz789",
"status": "active"
},
"ffmpeg": { "command": "...", "audioOnlyCommand": "...", "playlistCommand": "...", "artworkCommand": "...", "inputHint": "..." },
"session": {
"id": 42,
"wallet": "0x1234...abcd",
"maxDuration": 7200,
"remaining": 5400,
"expiresAt": "2026-04-11T02:00:00.000Z",
"accessToken": "eyJhbGciOiJIUzI1NiIs..."
}
}
| Field | Type | Description |
|---|
success | boolean | Always true |
reconnected | boolean | Present and true only on a recovery response, when the authenticated caller already owns a non-expired current session and is being handed the existing stream controls. |
stream.id | string | Mux live stream ID |
stream.name | string | DJ display name |
stream.wallet | string | Wallet for the session |
stream.streamKey | string | RTMP stream key |
stream.rtmpUrl | string | RTMP ingest server URL |
stream.fullRtmpUrl | string | Full RTMP URL including stream key (for OBS or ffmpeg) |
stream.playbackId | string | null | Mux playback ID for viewers |
stream.status | string | Mux or session status (e.g. created, active) |
session.id | number | DJ session record ID |
session.wallet | string | The wallet address used for the session. When access is granted via a community guest pass, this is the claimed Agentbot wallet (not the wallet sent in the request). |
session.maxDuration | number | Maximum session duration in seconds (7200 = 2 hours) |
session.remaining | number | Remaining seconds in the session |
session.expiresAt | string | ISO 8601 timestamp when the session expires |
session.accessToken | string | A signed session token that can be used to authenticate subsequent GET and DELETE requests on this session without requiring a full user session. Valid for the session duration plus a 1-hour grace period. |
ffmpeg.command | string | Default ffmpeg command (audio-only). Suitable for unattended agent streaming with no video track. |
ffmpeg.audioOnlyCommand | string | Audio-only ffmpeg command. Encodes a single audio file to the Mux RTMP endpoint. |
ffmpeg.playlistCommand | string | ffmpeg concat playlist command. Use for DJs who provide an ffmpeg concat playlist file. |
ffmpeg.artworkCommand | string | Artwork-and-video ffmpeg command. Use when the DJ explicitly wants a video track with the default baseFM standby image. |
ffmpeg.inputHint | string | Human-readable hint explaining when to use each command. |
The deprecated
stream.accessGrantedBy,
obsSettings,
streamType, and
playback response fields have been removed. Use the
Stream status endpoint to read playback URLs and Mux state during a session.
Errors
| Code | Description |
|---|
| 400 | Wallet address required, or USDC payment verification failed (the response error includes the verification reason) |
| 402 | Payment required. The caller does not hold the BASEFM token threshold and has no community guest pass, and no txHash was provided. The response includes fee (USDC) and a human-readable message. |
| 403 | Community guest pass only works with the claimed Agentbot wallet. Returned when the caller has a community pass but the wallet in the request does not match their claimed wallet address. The response includes the expected wallet. |
| 409 | active_session_exists — a non-expired current session already exists for this wallet and the authenticated caller does not own it. Owners receive the recovery response above instead. |
| 429 | cooldown_active — the wallet ended a previous session less than 24 hours ago and is in the cooldown window. Admin callers (authenticated with an email on the platform admin allowlist) bypass this check. |
| 500 | Mux is not configured or stream creation failed |
When access is granted via a community guest pass (not BASEFM tokens), the stream is created using the caller’s claimed Agentbot wallet address regardless of the wallet value in the request. The stream.wallet and session.wallet fields in the response reflect the claimed wallet.
Before checking for a blocking session, the endpoint automatically ends any expired current sessions for the wallet. This means that if a previous session exceeded the 2-hour maximum but was never explicitly stopped, it is cleaned up and the caller can start a new stream without manual intervention.
Admin cooldown bypass: When the request is made by an authenticated user whose email is on the platform admin allowlist, the 24-hour post-session cooldown is skipped. This lets admins run same-day stream tests. The bypass is tied to the authenticated session — it cannot be triggered from the request body or query parameters. All other access checks (BASEFM token balance, community guest pass, or USDC payment) still apply, and normal DJs remain rate-limited by the cooldown.
Check session status
Returns the active DJ session for the authenticated caller. Authentication is resolved from either a session token or a user session (see authentication below).
Authentication
The endpoint accepts two forms of authentication, checked in this order:
- Session token — pass the
accessToken returned by POST /api/basefm/streams as a sessionToken query parameter or an x-basefm-session header.
- User session — a standard authenticated session (cookie-based). The endpoint looks up the most recent active session for the authenticated user.
Query parameters
| Parameter | Type | Required | Description |
|---|
sessionToken | string | No | Session access token from POST /api/basefm/streams. Alternative to the x-basefm-session header. |
| Header | Required | Description |
|---|
x-basefm-session | No | Session access token. Alternative to the sessionToken query parameter. |
Response — active session
{
"active": true,
"stream": {
"id": "aB1cD2eF3g",
"name": "DJ Rave",
"wallet": "0x1234...abcd",
"streamKey": "sk-ab12-cd34-ef56",
"rtmpUrl": "rtmp://global-live.mux.com:5222/app",
"fullRtmpUrl": "rtmp://global-live.mux.com:5222/app/sk-ab12-cd34-ef56",
"playbackId": "xYz789",
"status": "active",
"accessGrantedBy": "basefm",
"ffmpeg": { "command": "...", "audioOnlyCommand": "...", "playlistCommand": "...", "artworkCommand": "...", "inputHint": "..." }
},
"ffmpeg": { "command": "...", "audioOnlyCommand": "...", "playlistCommand": "...", "artworkCommand": "...", "inputHint": "..." },
"session": {
"id": 42,
"wallet": "0x1234...abcd",
"djName": "DJ Rave",
"muxStreamId": "aB1cD2eF3g",
"playbackId": "xYz789",
"startedAt": "2026-04-11T00:00:00.000Z",
"elapsed": 1800,
"remaining": 5400,
"remainingMinutes": 90,
"expiresAt": "2026-04-11T02:00:00.000Z"
}
}
Response — no active session
{
"active": false,
"message": "No active session."
}
If the session has expired (exceeded the 2-hour maximum), it is automatically ended and the response returns active: false with a "Session expired." message.
| Field | Type | Description |
|---|
active | boolean | Whether the caller has an active session |
stream | object | The full stream control payload for the active session, mirroring the Create live stream response. Use this to recover stream controls after a refresh without creating a new stream. |
stream.id | string | Mux live stream ID |
stream.name | string | DJ display name |
stream.wallet | string | Wallet for the session |
stream.streamKey | string | null | RTMP stream key (resolved from Mux when reachable) |
stream.rtmpUrl | string | RTMP ingest server URL |
stream.fullRtmpUrl | string | null | Full RTMP URL including stream key, when the stream key is available |
stream.playbackId | string | null | Mux playback ID |
stream.status | string | Mux or session status (e.g. active, idle) |
stream.accessGrantedBy | string | null | How access was granted: basefm (token balance), community-pass (Builder/Whale community program perk), or paid (USDC session fee) |
stream.ffmpeg | object | The ffmpeg command templates for the session. See Create live stream for field shape. |
ffmpeg | object | Convenience copy of stream.ffmpeg at the top level. |
session.id | number | Session record ID |
session.wallet | string | Wallet address for the session |
session.djName | string | null | DJ display name |
session.muxStreamId | string | Mux live stream ID stored on the session |
session.playbackId | string | null | Mux playback ID |
session.startedAt | string | ISO 8601 timestamp when the session started |
session.elapsed | number | Seconds elapsed since the session started |
session.remaining | number | Seconds remaining before the session expires |
session.remainingMinutes | number | Minutes remaining (rounded down) |
session.expiresAt | string | ISO 8601 timestamp when the session expires |
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session token or user session |
| 403 | Forbidden — the session token does not match the session owner |
The wallet query parameter is no longer used. Session lookup is now based on the authenticated caller (session token or user session). Requests using the old ?wallet= parameter are ignored — you must provide authentication instead.
End session
DELETE /api/basefm/streams
Ends the authenticated caller’s current DJ session and retires the corresponding Mux live stream. Replay assets are deleted by default to avoid ongoing storage costs.
Uses the same authentication as the check session endpoint (session token or user session).
Query parameters
| Parameter | Type | Required | Description |
|---|
sessionToken | string | No | Session access token from POST /api/basefm/streams. Alternative to the x-basefm-session header. |
| Header | Required | Description |
|---|
x-basefm-session | No | Session access token. Alternative to the sessionToken query parameter. |
Response
{
"success": true,
"message": "Session ended"
}
If no active session exists, the endpoint still returns success: true with "message": "No session".
| Field | Type | Description |
|---|
success | boolean | Always true |
message | string | Human-readable status message describing the outcome. |
Deprecated: The archive request body field and the ended, muxStopped, archived, archiveCreditCost, deletedAssetIds, and retainedAssetIds response fields are no longer returned. The DJ archive flow is currently unavailable through this endpoint — replay assets are always cleaned up when a session ends.
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session token or user session |
| 403 | Forbidden — the session token does not match the session owner |
The wallet query parameter is no longer used. Session lookup is now based on the authenticated caller (session token or user session). Requests using the old ?wallet= parameter are ignored — you must provide authentication instead.
Stream status
GET /api/basefm/streams/status
Returns detailed stream health information for the authenticated caller’s active session, including the real-time Mux stream status, distribution state, and a health assessment. Uses the same authentication as the check session and end session endpoints.
Query parameters
| Parameter | Type | Required | Description |
|---|
sessionToken | string | No | Session access token from POST /api/basefm/streams. Alternative to the x-basefm-session header. |
| Header | Required | Description |
|---|
x-basefm-session | No | Session access token. Alternative to the sessionToken query parameter. |
Response — active session
{
"active": true,
"session": {
"id": 42,
"djName": "DJ Rave",
"muxStreamId": "aB1cD2eF3g",
"playbackId": "xYz789",
"status": "live"
},
"mux": {
"id": "aB1cD2eF3g",
"status": "active",
"playbackId": "xYz789",
"recentAssetIds": []
},
"distribution": {
"origin": {
"status": "active",
"playbackId": "xYz789",
"hlsUrl": "https://stream.mux.com/xYz789.m3u8"
},
"firstParty": {
"status": "healthy",
"pageUrl": "https://basefm.space",
"note": null
},
"relays": [],
"requiredRelayStatus": "healthy",
"overallStatus": "healthy"
},
"streamHealth": "good",
"pickupRecommended": false,
"message": "Mux is active and the station should be ready for listeners."
}
| Field | Type | Description |
|---|
active | boolean | Whether the caller has an active session |
session.id | number | Session record ID |
session.djName | string | null | DJ display name |
session.muxStreamId | string | Mux live stream ID |
session.playbackId | string | null | Mux playback ID stored in the session |
session.status | string | Session status (active or live) |
mux.id | string | Mux live stream ID (from the Mux API) |
mux.status | string | Real-time Mux stream status (active, idle, or disabled) |
mux.playbackId | string | null | Playback ID resolved from the Mux stream |
mux.recentAssetIds | array | IDs of recently created Mux assets for this stream |
distribution | object | Distribution state. See distribution for the full shape. |
streamHealth | string | Overall health assessment: good (Mux active with playback), waiting (Mux idle or playback not yet available), or bad (Mux is not reporting a healthy stream) |
pickupRecommended | boolean | true when the Mux stream is active but the session has not yet transitioned to live status, indicating a sync may be needed |
message | string | Human-readable explanation of the current stream health |
Response — no active session
{
"active": false,
"message": "No active stream session found"
}
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session token or user session |
| 403 | Forbidden — the session token does not match the session owner |
| 500 | Failed to read Mux status |
Sync stream status
POST /api/basefm/streams/status
Manually syncs the DJ session status with the Mux stream. If the Mux stream is active, the session is transitioned to live and the playback ID is updated. Uses the same authentication as the other session endpoints.
Query parameters
| Parameter | Type | Required | Description |
|---|
sessionToken | string | No | Session access token from POST /api/basefm/streams. Alternative to the x-basefm-session header. |
| Header | Required | Description |
|---|
x-basefm-session | No | Session access token. Alternative to the sessionToken query parameter. |
Response — synced
{
"success": true,
"synced": true,
"muxStatus": "active",
"playbackId": "xYz789",
"streamHealth": "good",
"message": "Mux stream is active. Session synced to live."
}
Response — not yet active
{
"success": false,
"synced": false,
"muxStatus": "idle",
"playbackId": null,
"streamHealth": "waiting",
"message": "Mux stream is not active yet. OBS ingest still needs to connect."
}
| Field | Type | Description |
|---|
success | boolean | Whether the sync completed successfully |
synced | boolean | Whether the session was actually updated |
muxStatus | string | Real-time Mux stream status |
playbackId | string | null | Mux playback ID |
streamHealth | string | Health assessment: good (active with playback), waiting (idle or no playback), or bad |
message | string | Human-readable status explanation |
Errors
| Code | Description |
|---|
| 401 | Unauthorized — no valid session token or user session |
| 403 | Forbidden — the session token does not match the session owner |
| 404 | No active stream session found |
| 500 | Failed to sync stream from Mux |
Get distribution state
GET /api/basefm/distribution
Returns the current distribution state for the baseFM station, including the origin stream health, first-party page status, and relay destination statuses. No authentication required.
The distribution.origin.status field reflects the live origin state:
active — Mux is healthy and a DJ is currently broadcasting.
idle — Mux is healthy but no DJ is live (for example, after a set ended). This is the normal off-air state and does not indicate a problem.
degraded — Mux credentials are missing or the Mux API returned an error. The response falls back to cached session data.
Response
{
"distribution": {
"origin": {
"status": "active",
"playbackId": "xYz789",
"hlsUrl": "https://stream.mux.com/xYz789.m3u8"
},
"firstParty": {
"status": "healthy",
"pageUrl": "https://basefm.space",
"note": null
},
"relays": [
{
"key": "basefm-space",
"name": "basefm.space",
"type": "hls-consumer",
"required": true,
"enabled": true,
"status": "healthy",
"viewerUrl": "https://basefm.space",
"probeUrl": "https://basefm.space",
"note": null,
"lastHealthyAt": "2026-04-11T01:00:00.000Z",
"lastErrorAt": null,
"lastErrorMessage": null
}
],
"requiredRelayStatus": "healthy",
"overallStatus": "healthy"
}
}
| Field | Type | Description |
|---|
distribution.origin.status | string | Origin stream status. active (a DJ is broadcasting), idle (Mux is healthy but no DJ is live — the normal off-air state), or degraded (Mux API or credentials are unavailable). |
distribution.origin.playbackId | string | null | Mux playback ID for the origin stream |
distribution.origin.hlsUrl | string | null | HLS URL for the origin stream |
distribution.firstParty.status | string | First-party page health: pending, healthy, degraded, failed, or stopped |
distribution.firstParty.pageUrl | string | URL of the first-party baseFM page |
distribution.firstParty.note | string | null | Contextual message about the first-party page status |
distribution.relays | array | List of configured relay destinations |
distribution.relays[].key | string | Unique relay identifier |
distribution.relays[].name | string | Display name |
distribution.relays[].type | string | Relay type: hls-consumer, rtmp, youtube, or custom |
distribution.relays[].required | boolean | Whether this relay is required for overall health |
distribution.relays[].enabled | boolean | Whether this relay is enabled |
distribution.relays[].status | string | Relay health: pending, healthy, degraded, failed, or stopped |
distribution.relays[].viewerUrl | string | null | URL where viewers can access this relay |
distribution.relays[].probeUrl | string | null | URL used for health probing |
distribution.relays[].note | string | null | Contextual message about relay status |
distribution.relays[].lastHealthyAt | string | null | ISO 8601 timestamp of the last successful health check |
distribution.relays[].lastErrorAt | string | null | ISO 8601 timestamp of the last health check failure |
distribution.relays[].lastErrorMessage | string | null | Error message from the last failed health check |
distribution.requiredRelayStatus | string | Aggregated status of all required relays |
distribution.overallStatus | string | Overall distribution health across origin, first-party page, and required relays |
Errors
| Code | Description |
|---|
| 500 | Internal error and no cached session data is available |
When Mux credentials are missing or the Mux API is unreachable, the endpoint returns a degraded distribution state based on cached session data with HTTP status 200 (as long as cached data exists). A 500 is only returned when both the Mux API and the session cache are unavailable.
Relay playback verification
When building the distribution state, the platform verifies that each relay destination is serving the current Agentbot playback ID. For relays with a configured probe URL, the platform fetches the relay’s live API and checks whether any of the returned streams include the current Mux playback ID.
- If the relay’s live API returns a stale or mismatched playback ID, the relay is marked
status: "degraded" with note: "Relay live API is not serving the current Agentbot playback id."
- If the relay’s live API includes the current playback ID, the relay stays
status: "healthy" with note: null.
This verification ensures that downstream consumers (such as basefm.space) are actually serving the same stream that Agentbot is broadcasting, and prevents the distribution state from reporting a relay as healthy when its content is stale.
List relay destinations
Returns all configured relay destinations for the baseFM station. No authentication required. Default relay destinations are created automatically if they do not exist.
Response
{
"relays": [
{
"key": "basefm-space",
"name": "basefm.space",
"type": "hls-consumer",
"required": true,
"enabled": true,
"status": "healthy",
"viewerUrl": "https://basefm.space",
"probeUrl": "https://basefm.space",
"note": null,
"lastHealthyAt": "2026-04-11T01:00:00.000Z",
"lastErrorAt": null,
"lastErrorMessage": null
}
]
}
See the distribution endpoint for full field descriptions of relay objects.
Errors
| Code | Description |
|---|
| 500 | Unable to load relay destinations |
Create or update relay destination
Creates a new relay destination or updates an existing one. Requires admin session authentication.
Request body
| Field | Type | Required | Description |
|---|
key | string | Yes | Unique relay identifier. Used as the upsert key. |
name | string | Yes | Display name for the relay |
type | string | No | Relay type: hls-consumer, rtmp, youtube, or custom (default: custom) |
viewerUrl | string | No | URL where viewers can access this relay |
probeUrl | string | No | URL used for health probing. Defaults to viewerUrl if not provided. |
required | boolean | No | Whether this relay is required for overall health (default: false) |
enabled | boolean | No | Whether this relay is enabled (default: true) |
Response
{
"relay": {
"id": 1,
"key": "youtube-main",
"name": "YouTube Live",
"type": "youtube",
"required": false,
"enabled": true,
"status": "pending",
"viewer_url": "https://youtube.com/live/abc123",
"probe_url": "https://youtube.com/live/abc123"
}
}
Errors
| Code | Description |
|---|
| 400 | key and name are required |
| 403 | Forbidden — admin session required |
| 500 | Unable to save relay destination |
| 503 | Relay persistence requires the baseFM relay database migration to be applied |
Probe relay destination
POST /api/basefm/relays/:relayKey/probe
Runs a health check against a relay destination’s probe URL and updates the relay’s health status in the database. Requires admin session authentication.
Path parameters
| Parameter | Type | Description |
|---|
relayKey | string | The unique key of the relay destination to probe |
Response
{
"ok": true,
"error": null,
"relay": {
"id": 1,
"key": "basefm-space",
"name": "basefm.space",
"type": "hls-consumer",
"required": true,
"enabled": true,
"status": "healthy",
"viewer_url": "https://basefm.space",
"probe_url": "https://basefm.space",
"last_healthy_at": "2026-04-11T01:00:00.000Z",
"last_error_at": null,
"last_error_message": null
}
}
| Field | Type | Description |
|---|
ok | boolean | Whether the probe succeeded |
error | string | null | Error message if the probe failed |
relay | object | Updated relay record after the probe |
Errors
| Code | Description |
|---|
| 400 | Relay has no probe target configured |
| 403 | Forbidden — admin session required |
| 404 | Relay not found |
| 500 | Unable to probe relay destination |
| 503 | Relay persistence requires the baseFM relay database migration to be applied |
Generate video
Generates a video and uploads it to blob storage. Requires session authentication. This endpoint has a 5-minute timeout.
Request body
| Field | Type | Required | Description |
|---|
type | string | Yes | Video type. One of: demo, marketing, screenshot, tutorial. |
agentName | string | For demo | Agent name for the demo video |
agentDescription | string | For demo | Agent description for the demo video |
productName | string | For marketing | Product name for the marketing video |
features | any | For marketing | Product features for the marketing video |
imageUrl | string | For screenshot | Image URL to animate |
description | string | For screenshot | Description for the animation |
topic | string | For tutorial | Tutorial topic |
steps | any | For tutorial | Tutorial steps |
Response
{
"url": "https://blob-storage-url/videos/1710806400000.mp4"
}
Errors
| Code | Description |
|---|
| 400 | Invalid video type |
| 401 | Unauthorized — session required |
| 500 | Video generation or upload failed |
Mux webhooks
Receives and processes Mux webhook events. This endpoint verifies the request signature using HMAC-SHA256 and rejects unsigned, expired, or tampered requests.
This endpoint is intended to be called by Mux only. You must configure the MUX_SIGNING_SECRET (or MUX_WEBHOOK_SECRET) environment variable for signature verification. When the signing secret is not configured, all requests are rejected.
| Header | Required | Description |
|---|
mux-signature | Yes | Mux webhook signature in the format t=<timestamp>,v1=<signature> |
Signature verification
The endpoint performs the following checks:
- Rejects requests missing the
mux-signature header (401)
- Rejects requests with a timestamp older than 5 minutes to prevent replay attacks (
403)
- Computes HMAC-SHA256 over
<timestamp>.<body> using the signing secret
- Performs a timing-safe comparison of the computed signature against the provided signature (
403 on mismatch)
Handled event types
| Event | Behavior |
|---|
video.asset.ready | Archives HD assets (1080p or higher) longer than 15 minutes. Shorter or lower-resolution assets are queued for deletion. |
video.live_stream.active | Marks the corresponding DJ session as live in the database and updates the playback ID. Triggers social amplification. |
video.live_stream.connected | Same behavior as video.live_stream.active. |
video.live_stream.idle | Transitions DJ sessions with status live back to active in the database. Triggers AI set summary generation. |
video.live_stream.disconnected | Same behavior as video.live_stream.idle. |
The webhook handler now syncs DJ session state to the database when live stream events are received. This enables the session cache used by the
GET /api/basefm/live endpoint for degraded-mode fallback.
Response
Errors
| Code | Description |
|---|
| 401 | Missing mux-signature header |
| 403 | Invalid signature or expired timestamp |
| 500 | Webhook processing failed |