Skip to main content

Scheduling a gathering

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");

// 1. Create the appointment
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, // becomes a transcribed call at start time
});

// 2. Invite participants (by id) — emails with calendar invites go out
await appointment.sendInvitations({ participantIds: ["p2", "p3"] });

// 3. Watch invitation responses come in live
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",
});

// Players join (e.g. via your matchmaker)
await lobby.addMember({ participantId: "p1" });
await lobby.addMember({ participantId: "p2" });

// Track readiness live; start when everyone is ready
lobby.watchMembers().onSnapshot((members) => {
const ready = members.filter((m) => m.status === "ready");
console.log(`${ready.length}/${members.length} ready`);
});

// When your "everyone ready" condition is met:
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) => {
// e.g. notify the host that another player is ready
};