API reference
Every HTTP endpoint and signalling event. Base URL for the hosted service is https://cloud.unitythink.com (control-plane on port 6080 when self-hosting).
Authentication
Three modes. The /v1/* customer API uses your API key; the /dashboard/* console API uses a session token; webhooks are signed by LiveKit.
1# API key (server → Socrits): customer API2x-api-key: sk_live_xxxxxxxx # or Authorization: Bearer sk_live_xxxxxxxx34# Session token (developer console): dashboard API5Authorization: Bearer <SESSION_JWT> # from POST /auth/loginAuth API
Body { email, password (min 8), name } → { token, account: { id, email } }. 409 if the email exists.
Body { email, password } → { token, account }. 401 on bad credentials. The token is a 7-day session JWT for the Dashboard API.
Dashboard API
All require Authorization: Bearer <SESSION_JWT>.
| Endpoint | Body | Returns |
|---|---|---|
| GET /dashboard/projects | — | { projects: [...] } |
| POST /dashboard/projects | { name } | { project } |
| GET /dashboard/projects/:id/keys | — | { keys: [...] } (hash hidden) |
| POST /dashboard/projects/:id/keys | { label } | { id, apiKey, prefix, note } — key shown once |
| POST /dashboard/keys/:id/revoke | — | { ok: true } |
| GET /dashboard/projects/:id/usage | — | { month, pricing, participantMinutes, sessions, rooms, cost } |
| POST /dashboard/projects/:id/session-token | { userId, userName } | { token } |
| POST /dashboard/projects/:id/demo-token | { room, identity } | { token, url, room } |
Customer API (/v1)
All require your API key (x-api-key).
Tokens
| Endpoint | Body | Returns |
|---|---|---|
| POST /v1/token | { room, identity } | { token, url, room } |
| POST /v1/session | { userId, userName } | { token, userId, userName } |
Recording & streaming
| Endpoint | Body | Returns |
|---|---|---|
| POST /v1/recordings/start | { room } | { egressId, status } |
| POST /v1/recordings/stop | { egressId } | { egressId, status } |
| GET /v1/recordings | — | { recordings: [{ egressId, room, status }] } |
| POST /v1/streams/start | { room, rtmpUrl } | { egressId, status } (stop via recordings/stop) |
| POST /v1/ingress | { room, name } | { ingressId, url, streamKey } |
| GET /v1/ingress | — | { ingress: [...] } |
| DELETE /v1/ingress/:id | — | { ok: true } |
502 { error, detail }; validation errors return 400.Signalling socket
Socket.IO at path /signaling. Connect with auth: { token } (a session token from /v1/session). The client SDKs wrap all of this — you rarely call it raw.
Client → server (with ack)
| Event | Payload | Ack |
|---|---|---|
| call:invite | { toUserId, mode, data? } | { ok, callId, room } |
| call:accept | { callId } | { ok, callId, room, mode, token, url } |
| call:reject | { callId } | { ok } |
| call:cancel | { callId } | { ok } |
| call:data | { toUserId, callId, data } | { ok } |
| device:register | { token, platform } | { ok } |
| chat:send | { toUserId?, conversationId?, text, clientMsgId? } | { ok, conversationId, message } |
| chat:history | { conversationId, before?, limit? } | { ok, messages } |
| chat:conversations | {} | { ok, conversations } |
| chat:read / chat:typing | { conversationId, ... } | { ok } |
| chat:createGroup | { name, members } | { ok, conversationId, participants } |
Server → client
| Event | Payload |
|---|---|
| call:incoming | { callId, room, mode, from: { userId, userName }, data } |
| call:accepted | { callId, room, mode, token, url } (to caller) |
| call:rejected | { callId } |
| call:cancelled | { callId, reason } |
| call:timeout | { callId } |
| call:data | { from, callId, data } |
| chat:message | { conversationId, message } |
| chat:read / chat:typing | { conversationId, userId, ... } |
device:register / the SDK’s registerDevice).Common errors
| Status | Body | Meaning |
|---|---|---|
| 400 | { error: 'room and identity are required' } | Missing required fields |
| 401 | { error: 'missing_api_key' | 'invalid_api_key' } | Bad/absent API key |
| 401 | { error: 'unauthorized' } | Bad/expired session token |
| 401 | { error: 'invalid_webhook' } | Webhook signature failed |
| 409 | { error: 'email already registered — log in instead' } | Duplicate signup |
| 502 | { error: 'egress_failed' | 'ingress_failed', detail } | Media/recording upstream error |