Feature request: auto-close room when only 1 participant remains for N seconds

Hey team,

Currently LiveKit supports empty_timeout, departure_timeout, and max_duration for room lifecycle management, but there’s no built-in way to handle the case where a room is left with exactly one remaining participant for an extended period — e.g. the other side never joined, or someone left and forgot to hang up, leaving the call running (and billing/streaming) indefinitely.

What I’d like: a solo_timeout (or similar) option on CreateRoomRequest — starts a timer when participant count drops to exactly 1, cancels if someone rejoins, and closes the room (same mechanism as max_duration) if it’s still solo when the timer expires.

What I’m doing today as a workaround: a webhook service that tracks participant_joined/participant_left, keeps per-room counts, and runs a timer that calls DeleteRoom if the count is still 1 when it fires. Works, but it’s a decent chunk of extra infra (needs to survive restarts, work across multiple server instances, etc.) for what feels like a natural sibling to the existing timeout options.

Has this come up before / is there a simpler built-in way I’m missing? And if not, would this be a reasonable feature request to file on GitHub as well?

Thanks!

@Nikhil_Taneja, No built-in solo timeout exists. CreateRoomRequest has only empty_timeout, departure_timeout, and max_participants (proto). empty_timeout needs zero participants, departure_timeout is the post-leave grace, and max_duration is a blanket cap, so a room stuck at exactly one never trips any of them. Your read is right.

You already filed the correct request: livekit/livekit #4753. That is the right repo, because the field belongs on the server-enforced room config, so keep your use case there. No duplicate needed.

Until then, your webhook plus DeleteRoom is the right approach. A compact version:

import { WebhookReceiver, RoomServiceClient } from 'livekit-server-sdk';

const receiver = new WebhookReceiver(API_KEY, API_SECRET);
const rooms = new RoomServiceClient(LIVEKIT_URL, API_KEY, API_SECRET);
const timers = new Map<string, NodeJS.Timeout>();

app.post('/webhook', express.raw({ type: 'application/webhook+json' }), async (req, res) => {
  const ev = await receiver.receive(req.body, req.get('Authorization'));
  const name = ev.room?.name;
  if (name) {
    clearTimeout(timers.get(name));           // any change cancels a pending close
    if (ev.room.numParticipants === 1)        // solo now, start the countdown
      timers.set(name, setTimeout(() => rooms.deleteRoom(name), 30_000));
  }
  res.sendStatus(200);
});

Store the “solo since” time in Redis, not an in-memory Map, because the timer must survive restarts and work across instances. That durability cost is what a built-in solo_timeout would remove.

(Note: no example of the feature itself exists, since the feature does not exist; the snippet is a compact version of the OP’s own workaround. Symbols verified this turn: WebhookReceiver/receive + application/webhook+json (webhooks-events.md L70-81); event.room/event.event (livekit_webhook.proto L29-31); Room.num_participants → numParticipants (livekit_models.proto L57); deleteRoom = DeleteRoom RPC (livekit_room.proto L38).)

I think it has, once, maybe twice, but it is not something I see a lot (the advice is to use the webhook approach exactly as you described)

Perhaps the API should accept an arbitrary number of participants, rather than just 1, but I’ll let engineering respond to your issue: