Sessions
A session is a single realtime avatar run — the headline Protoface resource. This guide covers the full lifecycle from your code. For the interactive reference, see the API Reference.
Create a session
Section titled “Create a session”POST /v1/sessions validates your request, allocates a sess_... ID, queues a
worker job, and returns 201 with status: "created". It returns in under
300 ms — placement onto a worker happens asynchronously.
import { ProtofaceClient } from "@protoface/sdk";
const client = new ProtofaceClient({ apiKey: process.env.PROTOFACE_API_KEY! });
const session = await client.sessions.createLivekit({ avatarId: "av_demo", url: process.env.LIVEKIT_URL!, roomName: "demo-room", workerToken, // minted with your LiveKit key/secret quality: "mock", maxDurationSeconds: 600, idleTimeoutSeconds: 30, metadata: { user_id: "u_123" },});curl https://api.protoface.com/v1/sessions \ -H "Authorization: Bearer $PROTOFACE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "avatar_id": "av_demo", "transport": { "type": "livekit", "url": "wss://my-app.livekit.cloud", "room_name": "demo-room", "worker_token": "eyJ..." }, "quality": "mock", "max_duration_seconds": 600, "idle_timeout_seconds": 30, "metadata": { "user_id": "u_123" } }'Request fields
Section titled “Request fields”| Field | Required | Notes |
| ---------------------- | -------- | -------------------------------------------------------------- |
| avatar_id | yes | An av_... ID. List with GET /v1/avatars. |
| transport | yes | A TransportConfig. See Transports. |
| quality | no | mock | lite | standard | pro. v0 renders mock. |
| max_duration_seconds | no | Hard cap; worker terminates on hit. Default 600, max 3600. |
| idle_timeout_seconds | no | End after this long with no audio. Default 30. |
| metadata | no | Free-form JSON, ≤ 8 KB, echoed on reads and webhooks. |
See Limits & quotas for the exact bounds and reserved
metadata keys.
Session statuses
Section titled “Session statuses”| Status | Meaning |
| ---------- | ------------------------------------------------------------- |
| created | Accepted and queued. |
| queued | Worker job written; awaiting claim. |
| starting | Worker claimed the job and is initializing. |
| running | Avatar is publishing video. |
| ending | Draining toward a clean stop. |
| ended | Clean termination. |
| failed | Terminal failure — see failure.code / failure.message. |
| canceled | Ended before it ran (via end). |
The session object also carries timing fields you can use to measure the
realtime targets: created_at,
started_at, first_frame_at, ended_at, failed_at, plus a live usage
roll-up ({ billable_seconds, frames }).
Retrieve a session
Section titled “Retrieve a session”GET /v1/sessions/{session_id} returns the full session. The usage block
reflects the latest worker heartbeat and may lag by one heartbeat interval.
const s = await client.sessions.get("sess_01HXY...");console.log(s.status, s.first_frame_at, s.usage.billable_seconds);curl https://api.protoface.com/v1/sessions/sess_01HXY... \ -H "Authorization: Bearer $PROTOFACE_API_KEY"List sessions
Section titled “List sessions”GET /v1/sessions returns sessions newest-first with cursor pagination.
const page = await client.sessions.list({ status: ["running", "starting"], limit: 20,});for (const s of page.data) console.log(s.id, s.status);if (page.hasMore) { const next = await client.sessions.list({ startingAfter: page.nextCursor! });}curl "https://api.protoface.com/v1/sessions?status=running&status=starting&limit=20" \ -H "Authorization: Bearer $PROTOFACE_API_KEY"Filters
Section titled “Filters”status— repeatable; multiple values are OR’d. Also accepts the group aliasesactive(=created | queued | starting | running | ending) andterminal(=ended | failed | canceled).avatar_id— single value.created_after,created_before— ISO-8601 timestamps.limit,starting_after— cursor pagination.
List responses use the standard envelope:
{ "object": "list", "data": [ /* sessions */ ], "has_more": true, "next_cursor": "sess_01HXY..."}End a session
Section titled “End a session”POST /v1/sessions/{session_id}/end is idempotent. Its effect depends on the
current status:
| Current status | Effect |
| ------------------------ | ---------------------------------------------------------- |
| created, queued | Transition to canceled; pending job is canceled. |
| starting | Cancel signal sent; becomes canceled after ack. |
| running | Transition to ending; worker drains then ended. |
| ending | No-op; returns current session. |
| ended/failed/canceled | No-op; returns current session with 200. |
It returns the session in all cases, or 404 not_found if the ID doesn’t exist
or isn’t accessible to your key.
await client.sessions.end("sess_01HXY...");curl -X POST https://api.protoface.com/v1/sessions/sess_01HXY.../end \ -H "Authorization: Bearer $PROTOFACE_API_KEY"Idempotent creates
Section titled “Idempotent creates”POST /v1/sessions accepts an optional Idempotency-Key header. Send a unique
key per logical create and you get safe retries: the same key + same body
within 24 hours returns the original response; the same key + a different
body returns 409 conflict. Omit the header and the request simply executes
once.
await client.sessions.create(request, { idempotencyKey: crypto.randomUUID() });curl https://api.protoface.com/v1/sessions \ -H "Authorization: Bearer $PROTOFACE_API_KEY" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d '{ "avatar_id": "av_demo", "transport": { "type": "livekit", "url": "wss://...", "room_name": "demo-room", "worker_token": "eyJ..." } }'Handling errors
Section titled “Handling errors”Every non-2xx response uses the uniform error envelope.
Switch on the stable error.code, never the message. The SDK throws typed
subclasses (RateLimitError, QuotaExceededError, …) and retries 429/503
for you. The POST /v1/sessions rate limit is 10 requests/min/key —
see Limits & quotas.