Skip to main content

Gatherings

A gathering is a coordination space — somewhere people are invited, gather, mark themselves ready, and optionally kick off a transcribed session. It’s pure backend: tables and API endpoints, not a 3D world.

There are three types, each tuned to a different scenario.

The three types

Lobby

For game matchmaking and parties. Players join, mark themselves ready, and the host starts the gathering — which can auto-create a session so the bot joins the moment play begins.

const lobby = await project.gatherings.create({ name: "Ranked 5v5", type: "lobby" });
await lobby.addMember({ participantId: "p1" });
// players mark ready, then:
await lobby.start({ roomId: "ranked-5v5-room" });

Appointment

For scheduled meetings. An appointment has startsAt / endsAt, sends email invitations with .ics calendar attachments, and can autoStartSession so it becomes a live, transcribed call at the scheduled time.

const appt = await project.gatherings.create({
name: "Quarterly Review",
type: "appointment",
startsAt: "2026-07-01T15:00:00Z",
endsAt: "2026-07-01T16:00:00Z",
autoStartSession: true,
});
await appt.sendInvitations({ participantIds: ["p2", "p3"] });

Open

Always-on, drop-in social spaces. People come and go using a join code; the space can be listed (public) or unlisted.

Members & invitations

A gathering separates who was invited from who is currently in it:

  • Invitations (gathering_invitations) are the source of truth for reaching people by email — including invitees who never sign in. This is what you’d use to email a summary afterwards.
  • Members (gathering_members) track current state with a simple lifecycle: invitedjoinedreadyleft, plus a role (owner / member).
const gathering = await project.gatherings.get(gatheringId);

await gathering.addMember({ participantId: "p1" });
const members = await gathering.listMembers();

// React to changes live:
gathering.watchMembers().onSnapshot((members, changes) => { /* ... */ });
gathering.watchInvitations().onSnapshot((invites) => { /* ... */ });

See Sessions & Gatherings for why invitation, membership and session attendance are tracked separately.

Join codes

Every gathering has a short join code people can use to join — handy for sharing a lobby or open space. Codes are alphanumeric and normalized (case-insensitive, hyphens optional), so ABCD-1234 and abcd1234 resolve to the same gathering.

const result = await project.gatherings.joinByCode({
joinCode: "ABCD1234",
participantId: "p1",
});

Email invitations

When you invite people by email, Cortex sends a branded invitation. For appointments it includes a standards-compliant .ics attachment so the event lands in the recipient’s calendar, with a recording notice when autoStartSession is enabled.

Email is best-effort

Invitation emails are sent fire-and-forget — a delivery problem for one recipient never blocks creating the gathering or inviting others.

Lifecycle events

Gatherings emit a rich set of domain events you can react to over webhooks, SSE or functions — for example:

  • gathering.created, gathering.started, gathering.ended, gathering.cancelled
  • gathering.member.joined, gathering.member.ready, gathering.member.left
  • gathering.invitation.sent, gathering.invitation.accepted, gathering.invitation.declined