Waiting rooms

Getting a parked participant in once they are admitted.

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. While parked, the join response's joinUrl points at the …/join?status=waiting park screen.

A host admits a parked participant with POST /rooms/{roomId}/participants/{participantId}/approve (tenant key, from your backend), and that response returns the participant's token directly — the simplest path is to relay it to the waiting client.

There is intentionally no public "am I admitted yet?" endpoint: a parked participant holds no credential to authenticate one with, and an endpoint keyed by participant id would turn that id into a capability. Three supported patterns, all backend-side:

Preferred — react to the webhook

Subscribe to participant.joined (Webhooks) and re-call /rooms/{id}/join once the admission lands.

Poll the admission endpoint

GET /rooms/{roomId}/participants/{participantId}/admission (tenant key, permission participants.read; the participantId comes from the parked join response) returns { "status": "waiting" | "admitted" | "denied" }. Once admitted, the response includes fresh token, livekitUrl, and joinUrl — re-navigate the participant's tab and you're done. This is a cheap read: it never re-opens an ended room and never re-notifies hosts, so it is the safe thing to leave on a timer.

Fallback — poll /join

Re-calling /rooms/{id}/join with the same externalUserId is idempotent and safe: an already-admitted participant is not sent back to the waiting room, and repeat calls do not re-notify hosts. Poll every 3 seconds and open /join as soon as token !== null.

Two limits to respect when polling:

  • The rate limit is per API key across your whole tenant, not per room — a 3-second poll is 20 requests/minute, so on FREE (60/min) about three concurrent polls exhaust your budget; back off on 429.
  • /join (unlike the admission endpoint) re-opens an ENDED room, so a poll left running after the host ends the meeting will resurrect it and resume billing. Always bound the loop (5 minutes is a sane ceiling) and stop on room.finished.

The park screen

While the participant waits, you can park their browser on …/join?status=waiting (no token), which shows a "waiting for the host" screen. That page never polls on its own — your backend owns the poll and re-navigates the tab to …/join?token=…&url=… once it has a token.

On this page