Skip to main content
The FaceMode REST API creates and manages streaming sessions (audio in, lip-synced avatar video out), avatars, and API keys. For the real-time audio channel itself, see the WebSocket protocol.

Base URL

All paths in this reference are relative to that base, for example POST https://api.facemode.io/api/sessions.

Authentication

Every request needs a FaceMode API key (fm_ followed by 48 hex characters). Two equivalent header formats are accepted:
Authorization: ApiKey fm_... and Authorization: Api-Key fm_... are also accepted. Create keys on the dashboard or via POST /api/keys. Keys can be revoked (PATCH /api/keys/{id}/revoke) or deleted (DELETE /api/keys/{id}).

Scopes

Each key carries a list of scopes. Requests authenticated with a key that lacks the required scope fail with 403 AUTH_INSUFFICIENT_SCOPE. New keys default to sessions:create, sessions:read, sessions:end, avatars:read, and usage:read. Pass an explicit scopes array at creation to grant more, or fewer.

Error envelope

All non-2xx responses share one shape:
Some errors add error.details (for example REQUEST_FIELD_UNKNOWN includes details.fields, the list of rejected fields). Scope failures also include top-level requiredScope and grantedScopes fields.

Common error codes

5xx responses always use curated codes such as SESSION_CREATE_FAILED or API_KEY_FETCH_FAILED, or the generic INTERNAL_ERROR; they never expose internal details.

Rate limits

Rate limits apply per API key across /api. Exceeding a limit returns 429 RATE_LIMIT_EXCEEDED with RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, and the matching X-RateLimit-* headers. One documented endpoint limit: avatar upload and update (POST/PUT /api/avatars) allow 5 requests per minute per key. Back off on 429 and retry after the reset window.

Idempotency and terminal states

  • Session IDs are server-generated; there are no client idempotency keys.
  • POST /api/sessions/{id}/end and DELETE /api/sessions/{id} may return 202 instead of 200. A 202 means the request was accepted but session cleanup is still pending (pending: true). Poll GET /api/sessions/{id} until session.status becomes ENDED (or the record disappears after delete).
  • Repeating POST /api/sessions/{id}/end while cleanup runs is safe and idempotent; once ended, further calls return SESSION_ALREADY_ENDED.
  • ingestion.wsToken is single-use and expires after 15 minutes. A dropped WebSocket or expired token requires POST /api/sessions/{id}/reconnect - the status endpoint never returns tokens.