Base URL
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 with403 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: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}/endandDELETE /api/sessions/{id}may return202instead of200. A202means the request was accepted but session cleanup is still pending (pending: true). PollGET /api/sessions/{id}untilsession.statusbecomesENDED(or the record disappears after delete).- Repeating
POST /api/sessions/{id}/endwhile cleanup runs is safe and idempotent; once ended, further calls returnSESSION_ALREADY_ENDED. ingestion.wsTokenis single-use and expires after 15 minutes. A dropped WebSocket or expired token requiresPOST /api/sessions/{id}/reconnect- the status endpoint never returns tokens.