API Reference
All endpoints are prefixed with /v1. Requests require the X-API-Key header (your API_SECRET).
Authentication
| Header | Value |
|---|---|
X-API-Key | Your server-side API_SECRET |
Health
GET /v1/health
Returns server status and enabled capabilities.
Response 200
{
"status": "ok",
"version": "0.1.0",
"capabilities": {
"transcriptionLive": false,
"transcriptionPost": false
}
}Token
POST /v1/token
Issue a LiveKit access token for a participant.
Body
| Field | Type | Required | Description |
|---|---|---|---|
roomId | string | Yes | Target room name |
userId | string | Yes | Unique participant identity |
name | string | Yes | Display name |
role | host | moderator | participant | viewer | Yes | Determines grants |
metadata | object | No | Arbitrary JSON attached to the participant |
Role grants
| Role | Publish | Subscribe | Mute others | Room admin |
|---|---|---|---|---|
host | Yes | Yes | Yes | Yes |
moderator | Yes | Yes | Yes | No |
participant | Yes | Yes | No | No |
viewer | No | Yes | No | No |
Response 200
{
"token": "<jwt>",
"url": "wss://your-livekit-host",
"expiresAt": "2025-01-01T12:30:00.000Z"
}POST /v1/token/refresh
Re-issue a token with the same body schema as POST /v1/token. Use this to extend a session before expiry.
Rooms
POST /v1/rooms
Create a new room.
Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Room name / ID |
maxParticipants | integer | No | Hard cap on participants |
emptyTimeout | integer | No | Seconds before empty room is closed |
metadata | string | No | Arbitrary string stored on the room |
Response 201 — LiveKit Room object.
GET /v1/rooms
List all active rooms.
Response 200
{ "rooms": [ /* LiveKit Room objects */ ] }GET /v1/rooms/:roomId
Get a single room. Returns 404 if not found.
DELETE /v1/rooms/:roomId
Close and delete a room. Returns 204.
Participants
GET /v1/rooms/:roomId/participants
List all participants in a room.
Response 200
{ "participants": [ /* LiveKit ParticipantInfo objects */ ] }DELETE /v1/rooms/:roomId/participants/:participantId
Remove (kick) a participant. Returns 204.
POST /v1/rooms/:roomId/participants/:participantId/mute
Server-side mute a participant's track.
Body
| Field | Type | Required | Description |
|---|---|---|---|
trackSid | string | Yes | SID of the track to mute |
Response 204
Recording
Recordings are stored as MP4 files in MinIO under the MINIO_BUCKET bucket.
POST /v1/rooms/:roomId/recording/start
Start a room composite recording. Idempotent — returns 200 with status: "already_recording" if already active.
Response 201
{
"egressId": "EG_xxx",
"roomId": "my-room",
"status": "recording",
"startedAt": "2025-01-01T12:00:00.000Z"
}POST /v1/rooms/:roomId/recording/stop
Stop the active recording. Returns 404 if no recording is running.
Response 200
{ "status": "stopped", "stoppedAt": "2025-01-01T12:30:00.000Z" }GET /v1/rooms/:roomId/recording
Get current recording status.
Response 200
{
"isRecording": true,
"egressId": "EG_xxx",
"startedAt": "2025-01-01T12:00:00.000Z"
}Ingress
Ingress lets external streams (RTMP / WHIP) publish into a room.
POST /v1/rooms/:roomId/ingress
Create an ingress endpoint.
Body
| Field | Type | Default | Description |
|---|---|---|---|
type | rtmp | whip | rtmp | Ingress protocol |
participantName | string | "Ingress Stream" | Display name in room |
participantIdentity | string | auto | Unique identity |
Response 201 — LiveKit IngressInfo object (includes url and streamKey).
GET /v1/rooms/:roomId/ingress
List active ingress endpoints for a room.
Response 200
{ "ingress": [ /* IngressInfo objects */ ] }DELETE /v1/rooms/:roomId/ingress/:ingressId
Delete an ingress endpoint. Returns 204.
Transcription (Live)
Requires the stt-live Docker Compose profile. Endpoints return 503 when the add-on is not running.
POST /v1/rooms/:roomId/transcription/start
Start live transcription for a room. Idempotent.
Body
| Field | Type | Default | Description |
|---|---|---|---|
language | string | "en-US" | BCP-47 language code |
participants | Record<sid, name> | — | Speaker identity map |
options.partialResults | boolean | — | Emit partial segments |
options.punctuation | boolean | — | Add punctuation |
options.profanityFilter | boolean | — | Filter profanity |
Response 201
{
"transcriptionId": "tr_abc123",
"roomId": "my-room",
"status": "active",
"language": "en-US",
"startedAt": "2025-01-01T12:00:00.000Z"
}POST /v1/rooms/:roomId/transcription/stop
Stop live transcription.
Response 200
{
"transcriptionId": "tr_abc123",
"status": "stopped",
"stoppedAt": "2025-01-01T12:30:00.000Z",
"segmentCount": 42
}GET /v1/rooms/:roomId/transcription
Retrieve transcription segments.
Query params
| Param | Description |
|---|---|
since | ISO timestamp — only return segments after this time |
format | Set to text for a plain-text formatted transcript |
PATCH /v1/rooms/:roomId/transcription/speakers
Update the speaker identity map mid-session.
Body
{ "participants": { "<trackSid>": "Alice" } }Response 204
Transcription (Post-call)
Requires the stt-post Docker Compose profile.
POST /v1/recordings/:recordingId/transcribe
Queue a post-call transcription job.
Body
| Field | Type | Default | Description |
|---|---|---|---|
language | string | "en-US" | BCP-47 language code |
participants | Record<sid, name> | — | Speaker map |
Response 202
{
"transcriptionId": "tr_abc123",
"recordingId": "EG_xxx",
"status": "queued",
"queuedAt": "2025-01-01T12:00:00.000Z"
}GET /v1/transcriptions
List all post-call transcriptions.
GET /v1/transcriptions/:transcriptionId
Get a single transcription by ID. Returns 404 if not found.
DELETE /v1/transcriptions/:transcriptionId
Delete a transcription and all its segments. Returns 204.
Webhooks
GET /v1/webhooks
List all registered webhooks. Secrets are masked as ***.
POST /v1/webhooks
Register a new webhook.
Body
| Field | Type | Required | Description |
|---|---|---|---|
url | string (URI) | Yes | Delivery endpoint |
events | string[] | No | Event filter (defaults to ["*"]) |
secret | string | No | HMAC signing secret (auto-generated if omitted) |
Available events
room_created room_closed participant_joined participant_left track_published recording_started recording_stopped ingress_started ingress_stopped transcription_completed *
Response 201 — Webhook config including the plaintext secret (only returned once).
GET /v1/webhooks/:webhookId
Get a single webhook. Secret masked.
PATCH /v1/webhooks/:webhookId
Update url and/or events.
DELETE /v1/webhooks/:webhookId
Delete a webhook. Returns 204.
POST /v1/webhooks/:webhookId/test
Send a test delivery to the webhook URL.
Response 200
{ "delivered": true, "statusCode": 200 }Error format
All error responses use RFC 9457 Problem Details:
{
"type": "https://rtcstack.dev/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "Room not found"
}
