Control-plane API reference
Every route below accepts X-API-Key: sk_live_... (or sk_test_... in a
test account). Publishable (pk_) keys are rejected on every route on this page.
Raw machine-readable source: openapi/confcall-control-plane.json
— contract-tested against the live controller auth attributes, so this table can't silently
overclaim or underclaim what a key can do.
Create a room
sk_ only POST /api/embed/rooms
curl -X POST https://meet.confcall.app/api/embed/rooms \
-H "X-API-Key: $CONFCALL_API_KEY" -H "Content-Type: application/json" \
-d '{"roomName": "Acme onboarding call"}'
# 201 { "roomId": "room-abc123", "embedUrl": "https://meet.confcall.app/embed/room/room-abc123" }
Creates an embed-flagged room owned by the key's account (not a human user).
Mint a join session
sk_ only POST /api/embed/sessions
curl -X POST https://meet.confcall.app/api/embed/sessions \
-H "X-API-Key: $CONFCALL_API_KEY" -H "Content-Type: application/json" \
-d '{"roomId": "room-abc123", "externalUserId": "acme-user-42", "role": "host", "ttlSeconds": 3600}'
# 200 { "token": "...", "expiresAt": "...", "embedUrl": "https://meet.confcall.app/embed/room/room-abc123" }
Mints a short-lived, room-scoped join token for one of your users. role is
host, co-host, or participant (default). A room that
doesn't exist, isn't an embed room, or isn't owned by your account all return the same
404, so this can't be used to probe other accounts' room ids. Feed token
into the widget's auth-token attribute — see Widget.
Room metadata & agent status
| Route | Auth | Notes |
|---|---|---|
GET /api/rooms/{roomId} |
public | Room name, share link, timestamps, and (if the caller owns/co-hosts it) co-organizers. |
GET /api/rooms/{roomId}/ai-agent/status |
public | Degrades to { agentPresent: false, available: false } rather than erroring. |
Room management (post-Phase-1)
These routes used to be JWT-only; an sk_ key for the room's owning account can now
call them directly, scoped by account ownership — a different account's key gets 403.
| Route | Auth | Scope | Notes |
|---|---|---|---|
DELETE /api/rooms/{roomId} |
JWT or sk_ | confcall:rooms |
Deletes the room record. Doesn't force-disconnect an in-progress live call — that's a SignalR hub RPC, not REST. |
POST /api/rooms/{roomId}/ai-agent/invite |
JWT or sk_ | confcall:rooms |
Body: { mode?, inlineConfig?: { language } }. 409 if the room is E2EE-enabled. |
POST /api/rooms/{roomId}/ai-agent/remove |
JWT or sk_ | confcall:rooms |
|
GET /api/rooms/{roomId}/artifacts |
JWT or sk_ | confcall:history |
Summarized session list for the room (not full transcripts). |
GET /api/artifacts/{sessionId} |
JWT or sk_ | confcall:history |
Full transcript, summary, decisions, follow-ups, action items for one session. |
GET /api/recordings?roomId= |
JWT or sk_ | — | Without roomId, lists every recording the caller/key's account can see. |
GET /api/recordings/{id}/stream |
JWT or sk_ | — | Pre-signed playback URL (S3/Azure) or a signed token for the legacy local-file endpoint. |
Still JWT-only
These two remain human-session-only — not yet callable with an sk_ key:
| Route | Auth | Use instead |
|---|---|---|
GET /api/recordings/{id} |
JWT only | GET /api/recordings?roomId= for the same fields via the list response. |
GET /api/recordings/{id}/download |
JWT only | GET /api/recordings/{id}/stream for a playable reference. |
Errors
Every route on this page returns JSON error bodies shaped { "error": "..." }. A
room/session/recording that exists but belongs to a different account returns 403;
one that doesn't exist at all returns 404 — the two are kept distinct except where
a route explicitly collapses them to avoid leaking existence (embed session minting, noted above).