Skip to main content

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.
StatusMeaning
activeSession created, waiting for or receiving an ingest connection
liveMux stream is active and playback is available to listeners
endedSession stopped by the DJ (replay assets deleted)
archivedSession stopped by the DJ with replay retention (assets preserved, credits charged)
auto-endedSession expired (exceeded the 2-hour maximum) or cleaned up automatically

List live streams

GET /api/basefm/live
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"
  }
}
FieldTypeDescription
djs[].idstringMux live stream ID
djs[].namestringDJ display name. Resolved from Mux stream metadata first, then from the DJ session record, and defaults to Anonymous DJ if neither is available.
djs[].walletstring | nullDJ wallet address. Resolved from Mux stream metadata first, then from the DJ session record.
djs[].playbackIdstring | nullMux playback ID. Resolved from the Mux stream first, then from the DJ session record.
djs[].streamKeystring | nullRTMP stream key. null when the DJ is served from the session cache.
djs[].statusstringStream status (active)
djs[].startedAtnumber | stringISO 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[].sourcestringWhere the DJ data was resolved from: mux (live Mux API) or session-cache (database fallback)
djs[].hlsUrlstring | nullHLS playlist URL for playback
djs[].embedUrlstring | nullEmbeddable player URL
countnumberNumber of live DJs
primaryDjobject | nullThe first DJ in the list, or null when no DJs are live
availabilitystringStream 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).
distributionobjectDistribution state for the baseFM station. See distribution for the full shape.
distribution.origin.statusstringOrigin 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.playbackIdstring | nullMux playback ID for the origin stream
distribution.origin.hlsUrlstring | nullHLS URL for the origin stream
distribution.firstPartyobjectHealth status of the first-party baseFM page
distribution.firstParty.statusstringOne of pending, healthy, degraded, failed, or stopped
distribution.relaysarrayConfigured relay destinations and their health
distribution.requiredRelayStatusstringAggregated status of all required relays
distribution.overallStatusstringOverall 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

CodeDescription
500Internal 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

POST /api/basefm/streams
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

FieldTypeRequiredDescription
walletstringYesWallet address of the DJ. Used for BASEFM token balance verification.
namestringNoDJ display name (default: DJ)
citystringNoCity or location label for the DJ. Stored in session metadata and displayed on the dashboard. Trimmed and capped at 80 characters.
txHashstringConditionalTransaction 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..."
  }
}
FieldTypeDescription
successbooleanAlways true
reconnectedbooleanPresent 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.idstringMux live stream ID
stream.namestringDJ display name
stream.walletstringWallet for the session
stream.streamKeystringRTMP stream key
stream.rtmpUrlstringRTMP ingest server URL
stream.fullRtmpUrlstringFull RTMP URL including stream key (for OBS or ffmpeg)
stream.playbackIdstring | nullMux playback ID for viewers
stream.statusstringMux or session status (e.g. created, active)
session.idnumberDJ session record ID
session.walletstringThe 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.maxDurationnumberMaximum session duration in seconds (7200 = 2 hours)
session.remainingnumberRemaining seconds in the session
session.expiresAtstringISO 8601 timestamp when the session expires
session.accessTokenstringA 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.commandstringDefault ffmpeg command (audio-only). Suitable for unattended agent streaming with no video track.
ffmpeg.audioOnlyCommandstringAudio-only ffmpeg command. Encodes a single audio file to the Mux RTMP endpoint.
ffmpeg.playlistCommandstringffmpeg concat playlist command. Use for DJs who provide an ffmpeg concat playlist file.
ffmpeg.artworkCommandstringArtwork-and-video ffmpeg command. Use when the DJ explicitly wants a video track with the default baseFM standby image.
ffmpeg.inputHintstringHuman-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

CodeDescription
400Wallet address required, or USDC payment verification failed (the response error includes the verification reason)
402Payment 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.
403Community 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.
409active_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.
429cooldown_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.
500Mux 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

GET /api/basefm/streams
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:
  1. Session token — pass the accessToken returned by POST /api/basefm/streams as a sessionToken query parameter or an x-basefm-session header.
  2. User session — a standard authenticated session (cookie-based). The endpoint looks up the most recent active session for the authenticated user.

Query parameters

ParameterTypeRequiredDescription
sessionTokenstringNoSession access token from POST /api/basefm/streams. Alternative to the x-basefm-session header.

Headers

HeaderRequiredDescription
x-basefm-sessionNoSession 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.
FieldTypeDescription
activebooleanWhether the caller has an active session
streamobjectThe 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.idstringMux live stream ID
stream.namestringDJ display name
stream.walletstringWallet for the session
stream.streamKeystring | nullRTMP stream key (resolved from Mux when reachable)
stream.rtmpUrlstringRTMP ingest server URL
stream.fullRtmpUrlstring | nullFull RTMP URL including stream key, when the stream key is available
stream.playbackIdstring | nullMux playback ID
stream.statusstringMux or session status (e.g. active, idle)
stream.accessGrantedBystring | nullHow access was granted: basefm (token balance), community-pass (Builder/Whale community program perk), or paid (USDC session fee)
stream.ffmpegobjectThe ffmpeg command templates for the session. See Create live stream for field shape.
ffmpegobjectConvenience copy of stream.ffmpeg at the top level.
session.idnumberSession record ID
session.walletstringWallet address for the session
session.djNamestring | nullDJ display name
session.muxStreamIdstringMux live stream ID stored on the session
session.playbackIdstring | nullMux playback ID
session.startedAtstringISO 8601 timestamp when the session started
session.elapsednumberSeconds elapsed since the session started
session.remainingnumberSeconds remaining before the session expires
session.remainingMinutesnumberMinutes remaining (rounded down)
session.expiresAtstringISO 8601 timestamp when the session expires

Errors

CodeDescription
401Unauthorized — no valid session token or user session
403Forbidden — 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

ParameterTypeRequiredDescription
sessionTokenstringNoSession access token from POST /api/basefm/streams. Alternative to the x-basefm-session header.

Headers

HeaderRequiredDescription
x-basefm-sessionNoSession 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".
FieldTypeDescription
successbooleanAlways true
messagestringHuman-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

CodeDescription
401Unauthorized — no valid session token or user session
403Forbidden — 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

ParameterTypeRequiredDescription
sessionTokenstringNoSession access token from POST /api/basefm/streams. Alternative to the x-basefm-session header.

Headers

HeaderRequiredDescription
x-basefm-sessionNoSession 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."
}
FieldTypeDescription
activebooleanWhether the caller has an active session
session.idnumberSession record ID
session.djNamestring | nullDJ display name
session.muxStreamIdstringMux live stream ID
session.playbackIdstring | nullMux playback ID stored in the session
session.statusstringSession status (active or live)
mux.idstringMux live stream ID (from the Mux API)
mux.statusstringReal-time Mux stream status (active, idle, or disabled)
mux.playbackIdstring | nullPlayback ID resolved from the Mux stream
mux.recentAssetIdsarrayIDs of recently created Mux assets for this stream
distributionobjectDistribution state. See distribution for the full shape.
streamHealthstringOverall health assessment: good (Mux active with playback), waiting (Mux idle or playback not yet available), or bad (Mux is not reporting a healthy stream)
pickupRecommendedbooleantrue when the Mux stream is active but the session has not yet transitioned to live status, indicating a sync may be needed
messagestringHuman-readable explanation of the current stream health

Response — no active session

{
  "active": false,
  "message": "No active stream session found"
}

Errors

CodeDescription
401Unauthorized — no valid session token or user session
403Forbidden — the session token does not match the session owner
500Failed 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

ParameterTypeRequiredDescription
sessionTokenstringNoSession access token from POST /api/basefm/streams. Alternative to the x-basefm-session header.

Headers

HeaderRequiredDescription
x-basefm-sessionNoSession 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."
}
FieldTypeDescription
successbooleanWhether the sync completed successfully
syncedbooleanWhether the session was actually updated
muxStatusstringReal-time Mux stream status
playbackIdstring | nullMux playback ID
streamHealthstringHealth assessment: good (active with playback), waiting (idle or no playback), or bad
messagestringHuman-readable status explanation

Errors

CodeDescription
401Unauthorized — no valid session token or user session
403Forbidden — the session token does not match the session owner
404No active stream session found
500Failed 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"
  }
}
FieldTypeDescription
distribution.origin.statusstringOrigin 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.playbackIdstring | nullMux playback ID for the origin stream
distribution.origin.hlsUrlstring | nullHLS URL for the origin stream
distribution.firstParty.statusstringFirst-party page health: pending, healthy, degraded, failed, or stopped
distribution.firstParty.pageUrlstringURL of the first-party baseFM page
distribution.firstParty.notestring | nullContextual message about the first-party page status
distribution.relaysarrayList of configured relay destinations
distribution.relays[].keystringUnique relay identifier
distribution.relays[].namestringDisplay name
distribution.relays[].typestringRelay type: hls-consumer, rtmp, youtube, or custom
distribution.relays[].requiredbooleanWhether this relay is required for overall health
distribution.relays[].enabledbooleanWhether this relay is enabled
distribution.relays[].statusstringRelay health: pending, healthy, degraded, failed, or stopped
distribution.relays[].viewerUrlstring | nullURL where viewers can access this relay
distribution.relays[].probeUrlstring | nullURL used for health probing
distribution.relays[].notestring | nullContextual message about relay status
distribution.relays[].lastHealthyAtstring | nullISO 8601 timestamp of the last successful health check
distribution.relays[].lastErrorAtstring | nullISO 8601 timestamp of the last health check failure
distribution.relays[].lastErrorMessagestring | nullError message from the last failed health check
distribution.requiredRelayStatusstringAggregated status of all required relays
distribution.overallStatusstringOverall distribution health across origin, first-party page, and required relays

Errors

CodeDescription
500Internal 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

GET /api/basefm/relays
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

CodeDescription
500Unable to load relay destinations

Create or update relay destination

POST /api/basefm/relays
Creates a new relay destination or updates an existing one. Requires admin session authentication.

Request body

FieldTypeRequiredDescription
keystringYesUnique relay identifier. Used as the upsert key.
namestringYesDisplay name for the relay
typestringNoRelay type: hls-consumer, rtmp, youtube, or custom (default: custom)
viewerUrlstringNoURL where viewers can access this relay
probeUrlstringNoURL used for health probing. Defaults to viewerUrl if not provided.
requiredbooleanNoWhether this relay is required for overall health (default: false)
enabledbooleanNoWhether 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

CodeDescription
400key and name are required
403Forbidden — admin session required
500Unable to save relay destination
503Relay 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

ParameterTypeDescription
relayKeystringThe 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
  }
}
FieldTypeDescription
okbooleanWhether the probe succeeded
errorstring | nullError message if the probe failed
relayobjectUpdated relay record after the probe

Errors

CodeDescription
400Relay has no probe target configured
403Forbidden — admin session required
404Relay not found
500Unable to probe relay destination
503Relay persistence requires the baseFM relay database migration to be applied

Generate video

POST /api/generate-video
Generates a video and uploads it to blob storage. Requires session authentication. This endpoint has a 5-minute timeout.

Request body

FieldTypeRequiredDescription
typestringYesVideo type. One of: demo, marketing, screenshot, tutorial.
agentNamestringFor demoAgent name for the demo video
agentDescriptionstringFor demoAgent description for the demo video
productNamestringFor marketingProduct name for the marketing video
featuresanyFor marketingProduct features for the marketing video
imageUrlstringFor screenshotImage URL to animate
descriptionstringFor screenshotDescription for the animation
topicstringFor tutorialTutorial topic
stepsanyFor tutorialTutorial steps

Response

{
  "url": "https://blob-storage-url/videos/1710806400000.mp4"
}

Errors

CodeDescription
400Invalid video type
401Unauthorized — session required
500Video generation or upload failed

Mux webhooks

POST /api/webhooks/mux
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.

Headers

HeaderRequiredDescription
mux-signatureYesMux webhook signature in the format t=<timestamp>,v1=<signature>

Signature verification

The endpoint performs the following checks:
  1. Rejects requests missing the mux-signature header (401)
  2. Rejects requests with a timestamp older than 5 minutes to prevent replay attacks (403)
  3. Computes HMAC-SHA256 over <timestamp>.<body> using the signing secret
  4. Performs a timing-safe comparison of the computed signature against the provided signature (403 on mismatch)

Handled event types

EventBehavior
video.asset.readyArchives HD assets (1080p or higher) longer than 15 minutes. Shorter or lower-resolution assets are queued for deletion.
video.live_stream.activeMarks the corresponding DJ session as live in the database and updates the playback ID. Triggers social amplification.
video.live_stream.connectedSame behavior as video.live_stream.active.
video.live_stream.idleTransitions DJ sessions with status live back to active in the database. Triggers AI set summary generation.
video.live_stream.disconnectedSame 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

{
  "received": true
}

Errors

CodeDescription
401Missing mux-signature header
403Invalid signature or expired timestamp
500Webhook processing failed