Skip to Content
APISessions

Sessions

A session is a room where several people record at the same time, each on their own device. Use it when you need a conversation; use a capture request when you need one person answering on their own time.

Open a room

curl -X POST https://capturly.app/api/v1/sessions \ -H "Authorization: Bearer cap_live_..."
{ "session": { "id": "ses_...", "roomCode": "ABC123", "status": "open", "maxGuests": 7 }, "joinUrl": "https://capturly.app/record/join/ABC123?invite=…", "hostUrl": "https://capturly.app/record/host/ABC123?invite=…", "inviteToken": "…", "hostToken": "…" }

joinUrl is for guests, who need no account — the invite token in it is the whole credential. hostUrl is for the host, who does need one: the invite admits guests, and the host is recognised by their signed-in session rather than by the link.

The tokens are returned once and expire in 24 hours, matching the session’s own cutoff for issuing them.

The host of record is the member who created the API key. A room needs an accountable person, for the plan its guests record under and for whoever can end it, and a key is not one. If that member leaves the organization, session creation fails with key_owner_not_a_member rather than silently recording against their personal account.

Status

Three states, not the two the underlying row stores:

StatusMeaning
openAccepting joins
closedEnded by the host or the API
expiredNever closed, but past its 24-hour cutoff

An open session past its cutoff accepts nobody. Reporting it as open would let you hand out a join link that silently fails, so it reports expired.

End it

curl -X POST https://capturly.app/api/v1/sessions/{id}/end \ -H "Authorization: Bearer cap_live_..."

This closes the room and promotes whatever was uploaded into a library recording. It is idempotent, and ending an already-closed session re-attempts promotion rather than refusing — uploads can land after a close, and a first promotion can fail mid-copy. That makes it the recovery call as well as the close call, which is why it answers 200 with alreadyClosed rather than a conflict.

promotion says what happened, because recordingId: null alone cannot tell “nobody recorded” from “the copy failed and the tracks are still waiting”:

promotionMeaning
createdThe tracks were promoted. savedCount is how many files landed.
existingAlready promoted by an earlier call. savedCount is 0.
emptyNobody recorded. There is nothing to collect.
failedThe copy did not finish. The tracks are still held; call this again.

A promotion failure does not fail the request: your intent was to close the room, the cleanup job retries, and a 500 here would invite a retry loop that closes nothing new. failed carries an errorId to quote to support, and calling end again is the retry — it is idempotent, so a later success comes back as created or existing.

Endpoints

MethodPath
GET/v1/sessions
POST/v1/sessions
GET/v1/sessions/{id}
POST/v1/sessions/{id}/end

Sessions are keyed on their id, not the room code. The code is a short, human-typed handle; the id is what /v1/recordings reports as sessionId, so the two surfaces join on the same value.

Participant identity stays inside. You get a stable per-session participantId and whether someone was a guest, not their user id or the credential they joined with.

Last updated on