Meetings
Rooms have a schedule window, an invite code, visibility (public or private), capacity, and host/moderator roles.
Creating a meeting
Create room
↓
Validate title, type, schedule, visibility
↓
Check host plan limits (duration / participants)
↓
Insert room + host membership (+ allowlist if private)
↓
Ready — share invite or open /room/[roomId]Create always stores type: "meeting". The schema also has a webinar enum value, but the create UI does not expose it. Status is derived against startTime / endTime (waiting / active / ended).
Joining
Open invite or room URL
↓
Meeting exists and not ended
↓
Within schedule (before endTime)
↓
Visibility / allowlist / guest rules
↓
Capacity soft-check (listParticipants)
↓
Mint LiveKit JWT
↓
Browser connects to LiveKitSoft capacity uses LiveKit listParticipants before token issue. Concurrent joins can race; LiveKit maxParticipants (product default 50) is the hard SFU bound.
Public vs private
Public
Anyone with a valid invite can join under normal schedule and capacity rules — including guests (signed cookie, no account).
Private
Only authenticated users whose account email is on the room’s allowlist (or the host) may join. Checks are server-side against the session email — typing an email in the UI is not proof of access. Guests cannot join private rooms. Unauthorized users never receive a LiveKit token.
Schedule and end
Join and token mint reject at/after endTime. The meeting UI warns at 10 and 5 minutes before end. Hosts can end/delete from the dashboard. Ending in Postgres does not instantly kick every LiveKit peer.
Roles
| Role | Notes |
|---|---|
| Host | Room owner. Gets LiveKit roomAdmin. Full moderation. |
| Moderator | Promoted member. Mute / camera off / remove via server RoomService APIs (not raw roomAdmin on the client). |
| Participant | Authenticated member without elevated grants. |
| Guest | Public rooms only. Identity guest:{roomId}:{guestId}. |