Security model

What crosses the boundary, and what never does.

The identity seam

Only externalUserId + displayName cross to the platform. The platform stores no other end-user PII. externalUserId is your identifier for the user; the platform never sees your user table. Keep the mapping stable and one-directional: the platform stores the opaque id; you resolve it to a person.

Because room names are broadcast to every participant and rendered in every client, do not put personal identifiers in Room.name — use externalId/externalMetadata for correlation; those stay server-side.

Credential placement

The tenant API key and webhook secret live on your backend only; the browser only ever holds a single-participant, short-lived join token. See Authentication & credentials.

E2EE rooms are opaque server-side

The encryption key rides the URL fragment and never reaches the platform, so server-side features (recording, streaming, AI) cannot operate on encrypted rooms. See End-to-end encryption.

Join tokens travel in a query string

The join token reaches access logs and same-origin Referer headers. It is single-participant and short-lived (6h by default); treat a join URL as a secret and don't persist it.

Rooms carry their recording/AI configuration in the public room facts (GET /rooms/{id}/info and the token's metadata claim), and the hosted pre-join surfaces display a consent notice for recorded or AI-transcribed meetings. If you build your own client, render an equivalent notice.

On rooms configured with enablePreJoin: false (direct call entry, no preview page), the hosted surface shows the same disclosure as a persistent dismissible in-call notification at entry instead. Note that direct entry also means publishers join with camera and microphone on — if your integration needs an explicit consent step before any media is captured, keep the preview enabled or gate entry on your side first.

Not covered yet

Official SDKs beyond the generated OpenAPI client, and formal deprecation windows.

On this page