Quickstart

Create a room, mint a join token, and open a meeting — three API calls.

Before you start

Your tenant, API key, and webhook secret are issued to you once. Configure your backend:

PLATFORM_API_BASE_URL=https://<platform-host>/api/v1
PLATFORM_API_KEY=vcp_live_…
PLATFORM_WEBHOOK_SECRET=whsec_…

Point your webhook endpoint at a public URL and register it yourself with PATCH /tenant/webhook (your x-api-key, permission tenant.manage). That's the whole handshake.

Never expose the tenant API key to a browser. The key stays on your backend. Your frontend talks to your backend; your backend talks to the platform. A leaked tenant key can create rooms and run up media cost on your account.

All three steps below are server-to-server with the tenant key, except the final browser redirect.

1. Create a room

POST /api/v1/rooms
x-api-key: vcp_live_…
Content-Type: application/json

{ "name": "Morning Consultation", "createdByExternalId": "your-user-42" }

createdByExternalId is required — your own identifier for the user creating the room. Returns a room; keep room.id.

Store room.id, not a URL. It is the durable handle: put it on your appointment/class/session row. Join URLs are minted per participant at click time (step 2) and carry a short-lived token, so a stored URL will be dead by the next session.

Because the room name is broadcast and rendered in every client, do not put patient names, record numbers, or other personal identifiers in Room.name. Use externalId/externalMetadata for correlation instead — those stay server-side.

2. Mint a join token

Once per participant, from your backend:

POST /api/v1/rooms/{room.id}/join
x-api-key: vcp_live_…
Content-Type: application/json

{ "externalUserId": "your-user-42", "displayName": "Dr. Jane" }

The response is six fields, and the token can be null:

{
  "token": "<livekit-jwt>",
  "livekitUrl": "wss://…",
  "joinUrl": "https://<meet-web-host>/join?token=…&url=…",
  "waitingForApproval": false,
  "participant": { "…": "…" },
  "room": { "…": "…" }
}

joinUrl is a ready-to-open link to the hosted call UI — redirect the participant to it (or embed it) and you are done; no URL assembly on your side. The role (host vs. guest) is encoded in each participant's short-lived token, so joinUrl is per-participant.

Always check token. If a room has a waiting room enabled, an ATTENDEE/VIEWER join returns token: null and waitingForApproval: true — the participant must be admitted before a token exists. See Waiting rooms.

Side effects of join worth knowing: it re-opens an ENDED room, and it enforces the room lock and the per-room participant cap. Join tokens default to a 6-hour TTL.

3. Open the meeting

Redirect the participant to the joinUrl (or compose it yourself):

https://<meet-web-host>/join?token=<livekit-jwt>&url=<livekit-wss-url>

For an end-to-end-encrypted room, append the E2EE key in the URL fragment — see End-to-end encryption.

The hosted page renders the full in-call experience: video, audio, screen share, and chat (chat authenticates with the participant token, so it needs no API key and no session). Recording, streaming, and moderation are backend-mediated: drive them from your backend with the tenant key. Captions follow the room's AI opt-in (enableAiFeatures, and never on an E2EE room).

Building your own client instead? Use the LiveKit client SDK directly with the same token + livekitUrl. The hosted page is the supported default; it is also the only surface supported for iframe embedding — see Embedding.

What the token carries

Beyond the LiveKit grants, the token's metadata claim carries non-PII facts so a client can render the right UI before it connects: metaVersion, roomId, roomName, externalUserId, role, externalRole, and the flags enableE2EE, enableRecording, autoRecord, enableAiFeatures, enableChat, enableWaitingRoom. Every one of them is already readable without a credential from GET /rooms/{id}/info, so nothing new is disclosed.

Three properties matter if you build your own client:

  • Treat a missing metaVersion as a token minted before room facts existed and fall back to your own defaults. Treat unknown fields as additive.
  • Participant metadata is broadcast to every participant in the room. Do not expect it to be private, and do not add your own confidential fields to it.
  • Participants may rewrite their own metadata, so a peer's metadata is advisory. Read your own token's claims for anything that gates behaviour, and remember the LiveKit grants are the only enforcement.

Next steps

On this page