# LiveKit rooms

Audio and video rooms through the livekit/* endpoints - token minting, room lifecycle, and reserve-then-settle billing.

Audio/video - voice chat, video calls, watch-together rooms, real-time transcription - runs on LiveKit through the `livekit/*` integration endpoints. The SDK's role is deliberately narrow: it is a **room-lifecycle proxy** - create, list, and delete rooms; mint access tokens. There is no `useMediaRoom` hook and no media Durable Object class. The client-side WebRTC plumbing is yours to wire with LiveKit's own SDK.

Skip this page for text-only collaborative apps - live sync, presence, and messaging need none of it.

## Install the client SDK yourself

The LiveKit JS SDK is not bundled with `deepspace`. Install it as your own dependency:

```bash
npm i livekit-client
# or, for React-shaped components:
npm i @livekit/components-react @livekit/components-styles livekit-client
```

DeepSpace handles the auth and room-lifecycle proxy; connecting, publishing tracks, and rendering participants belong to the LiveKit client API - use LiveKit's own documentation for that surface.

## The five endpoints

Billing varies by endpoint - read this table before touching `src/integrations.ts`:

| Endpoint                 | Required inputs                                                                                           | Returns                                                                                                     | Billing                                                                                                                                                                                                                                                                                                              |
| ------------------------ | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `livekit/generate-token` | `roomName` (optional `displayName`, `ttlSeconds` 60-86400, default 3600)                                  | `{ token, url, roomName }`                                                                                  | **Free.** The room auto-creates when the first participant connects. No participant or duration caps.                                                                                                                                                                                                                |
| `livekit/create-room`    | `roomName` (optional `maxParticipants` 1-100 default 10, `durationMinutes` 1-1440 default 60, `metadata`) | `{ roomSid, roomName, roomSessionId, adminToken, livekitUrl, expiresAt, maxParticipants, durationMinutes }` | **Billable.** Reserves the worst case - `maxParticipants × durationMinutes × $0.0005 × 1.3` (the raw LiveKit rate times the platform's 1.3× markup) - at create time, then settles down to actual usage when you call `livekit/settle-room`. The reservation cost is derived server-side; a client cannot supply it. |
| `livekit/settle-room`    | `roomSessionId` (from `create-room`), `participantMinutes` (≥ 0)                                          | `{ ok, billedParticipantMinutes }`                                                                          | **Free.** Bills the reported participant-minutes clamped to the reservation cap and refunds the remainder. Idempotent per `roomSessionId`; creator-only.                                                                                                                                                             |
| `livekit/delete-room`    | `roomName`                                                                                                | `{ deleted, roomName }`                                                                                     | **Free.** Does **not** settle billing, has **no creator check**, and under the scaffold's default `developer` billing no sign-in requirement either.                                                                                                                                                                 |
| `livekit/list-rooms`     | (none)                                                                                                    | `{ rooms: [...] }` (LiveKit's Twirp `ListRooms` shape)                                                      | **Free.**                                                                                                                                                                                                                                                                                                            |

## Two flows - pick by whether you need caps

### Ad-hoc flow (free) - the default

For small group calls, low-stakes voice chat, "drop into a room" UX:

Mint a token

```ts
const r = await integration.post('livekit/generate-token', { roomName, displayName })
if (!r.success) throw new Error(r.error)
const { token, url } = r.data as { token: string; url: string; roomName: string }
```

Connect with the LiveKit client

Pass `token` and `url` to `livekit-client` (or `@livekit/components-react`). The room auto-materializes when the first participant connects and disposes itself when empty.

No `create-room` call, no billing. This is the right flow unless you need participant or duration limits.

### Billable flow - rooms with quotas

For paid features, large meetings, and time-limited sessions:

Create the room (reserves the worst case)

Call `integration.post('livekit/create-room', { roomName, maxParticipants, durationMinutes })`. This reserves `maxParticipants × durationMinutes × $0.0005 × 1.3` (raw rate times the platform markup) up front and returns an `adminToken` for the creator plus a `roomSessionId`. **Persist the `roomSessionId`** - settlement needs it.

Mint per-user tokens

Use `livekit/generate-token` for each participant - free; the room itself is the billed object.

Settle when the session ends

Call `livekit/settle-room` with the saved `roomSessionId` and the actual `participantMinutes`. Billing settles down to actual usage and the unused reservation is refunded. Optionally call `livekit/delete-room` to tear the room down.

## Settlement rules

* **Always call `settle-room` when a session ends.** Skip it and the full worst-case reservation is billed: a platform cron settles abandoned cloud rooms at exactly their reservation - the cap the caller authorized at create time.
* Settlement is **creator-only**: `create-room` records the owner, and no other caller can settle (or under-report) someone else's room.
* Settlement is **idempotent** per `roomSessionId` - a double settle, or a settle racing the cron, nets one charge.
* Reported minutes are **clamped to the reservation cap** - a client can never bill past what was reserved; under-reporting only reduces the charge.
* **Self-hosted LiveKit is never metered.** With a `LIVEKIT_URL` outside `*.livekit.cloud`, the reservation is voided to zero at create time.

**`delete-room` is not settlement, and it is not access-controlled.** Deleting a room does not release the `create-room` reservation - only `settle-room` does that. And any caller can delete any room by name: the platform performs no creator check, and because LiveKit is not pre-listed in `src/integrations.ts`, the scaffold's default `developer` billing applies - whose sign-in gate never fires - so even an anonymous visitor reaches it. Application-layer gating is mandatory: gate the "End meeting" control on the creator, e.g. `useUser().user?.id === room.createdBy`, or store the creator in your own collection and check it before calling.

## Auth-gate the token-minting page

A leaked `generate-token` token grants room access until `ttlSeconds` expires (default one hour) - there is no revocation. Always auth-gate the page or component that mints tokens: wrap it in `useAuth().isSignedIn`, or place it behind `<AuthGate>` when the whole page is gated. For the billable flow, gate `create-room` - under the default `developer` billing an anonymous visitor can commit billable reservations against the app owner - and gate `delete-room` per the warning above. `settle-room` needs no extra gating - it is creator-only and refund-only.

## Next steps

* [External APIs](/guides/external-apis) - the `integration.post(...)` client, billing modes, and discovery.
* [Integrations reference](/sdk-reference/client/integrations) - envelope types and request options.
* [Realtime rooms](/sdk-reference/worker/rooms) - the SDK's own Durable Object rooms, which handle data sync, not media.
