Skip to main content

Sessions & Gatherings

Cortex has two “meeting-like” entities that are easy to confuse. They are orthogonal — you can use either, both, or neither.

  • A Session is an active transcription of one ODIN Voice room.
  • A Gathering is a coordination space where people organize before and optionally during a session.

Sessions

A session is bound to a specific ODIN room via its externalRoomId. While a session is running, the bot is in the room, capturing audio and producing messages (transcribed utterances).

Key facts:

  • You need a session whenever you want transcripts. No session → no messages → no annotations.
  • A session has a status (active / ended), an optional title, the externalRoomId it transcribes, the gateway the bot joins, and an optional idleTimeout that auto-ends it after a period of silence.
  • A session can exist completely on its own — a game server that already has a room just creates a session and starts transcription. No gathering required.
const session = await project.sessions.create({
title: "Ranked Match #4821",
externalRoomId: "match-4821",
idleTimeout: 120,
});
await session.start(); // bot joins, transcription begins

See the Transcription feature page for the full lifecycle.

Gatherings

A gathering is backend coordination: invitations, join codes, member “ready” states and optional scheduling. It is not a 3D world, avatars or proximity voice — it’s rows in a table and endpoints on the API. There are three types:

TypeForHighlights
lobbyGame matchmaking & partiesPlayers join, mark ready, host starts — optionally auto-creating a session.
appointmentScheduled meetingsstartsAt / endsAt, email invitations with .ics calendar attachments, optional autoStartSession.
openAlways-on social spacesDrop-in / drop-out with a join code; can be listed or unlisted.

A gathering can optionally start a session (set autoStartSession, or call start()), which is how a scheduled appointment turns into a live, transcribed call.

const gathering = await project.gatherings.create({ name: "Daily Standup" });
await gathering.sendInvitations({ participantIds: ["p2", "p3"] });
// ...later, when it's time:
await gathering.start({ roomId: "standup-room" });

See the Gatherings feature page for members, invitations and join codes.

The three-way membership model

Because participants in a room can be anonymous (especially in ODIN Rooms), a gathering tracks identity in three independent layers. They may all match for one person, or only some:

LayerQuestion it answersSource of truth for
Invitation (gathering_invitations)“Who was invited?” (by email and/or user id)Sending the post-meeting summary email
Membership (gathering_members)“Who is currently in the gathering?” (invitedjoinedreadyleft)In-gathering state, ready-up
Session attendance“Who was actually in the room?”Transcript attribution

Keep this in mind: an email-only invitee can join anonymously and speak, producing a transcript that isn’t linked back to their invitation. That’s expected — the invitation layer is what lets you still email them a summary afterwards.

When to use which

You want to…Use
Transcribe a room you already haveA session (no gathering)
Run a matchmaking lobby with “ready up”A lobby gathering, optionally auto-starting a session
Schedule a meeting with calendar invitesAn appointment gathering with autoStartSession
Run a persistent drop-in hangoutAn open gathering
Get transcriptsAlways a session (directly or via autoStartSession)