This guide builds two common coordination flows with gatherings:
a scheduled appointment that emails calendar invites and auto-starts a transcribed
session, and a matchmaking lobby with ready-up.
Scheduled appointment
Create an appointment, invite people (they receive an email with an .ics calendar
attachment), and let Cortex start the session automatically at the scheduled time.
import { CortexClient } from "@4players/odin-cortex";
const client = new CortexClient({
baseUrl: "https://ots.odin.4players.io",
apiKey: process.env.CORTEX_API_KEY,
});
const project = client.project("your-project-id");
const appointment = await project.gatherings.create({
name: "Quarterly Review",
type: "appointment",
startsAt: "2026-07-01T15:00:00Z",
endsAt: "2026-07-01T16:00:00Z",
autoStartSession: true,
});
await appointment.sendInvitations({ participantIds: ["p2", "p3"] });
appointment.watchInvitations().onSnapshot((invites, changes) => {
for (const c of changes) {
console.log(`invitation ${c.id}: ${c.data.status}`);
}
});
When startsAt arrives, the session starts and the bot joins — you’ll receive
gathering.started and session.created events. To
email a recap when it ends, see
Email a meeting summary.
Matchmaking lobby
A lobby gathers players, waits for everyone to mark ready, then starts.
const lobby = await project.gatherings.create({
name: "Ranked 5v5",
type: "lobby",
});
await lobby.addMember({ participantId: "p1" });
await lobby.addMember({ participantId: "p2" });
lobby.watchMembers().onSnapshot((members) => {
const ready = members.filter((m) => m.status === "ready");
console.log(`${ready.length}/${members.length} ready`);
});
await lobby.start({ roomId: "ranked-5v5-room" });
Join by code
Share a gathering’s join code so people can join without an explicit invitation. Codes are
case-insensitive and hyphen-tolerant:
const result = await project.gatherings.joinByCode({
joinCode: "ABCD1234",
participantId: "p9",
});
React with events or functions
Every step emits events — gathering.member.joined, gathering.member.ready,
gathering.invitation.accepted, gathering.started, gathering.ended. Subscribe over
webhooks or handle them in a
function:
exports.onGatheringMemberReady = async (event, ctx) => {
};