Errors
The error format and every error code the FaceCard API returns.
Every error response is JSON with a correct HTTP status:
{ "error": { "code": "snake_case_code", "message": "Human readable text." } }Branch on code, not on message. The message can change. Some errors add fields next to code and message; those are listed below.
Codes
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_request | The request is malformed: a field is missing or has the wrong type, a descriptor does not have exactly 128 finite numbers, or a query value is out of range. | Fix the request. The message names the field. |
| 400 | invalid_json | The body is not valid JSON. | Send valid JSON with Content-Type: application/json. |
| 400 | consent_required | Enrollment without consent.accepted: true. | Show your privacy notice and get consent first. |
| 400 | challenge_invalid | The challenge is missing, expired, already used or belongs to another app. | Request a new challenge and retry. |
| 400 | invalid_code | The email change confirmation code is wrong or expired (15 minutes). | Start the email change again. |
| 401 | unauthorized | The credential is missing, invalid, revoked or expired. | Check the key or token. A user token lasts one hour. |
| 401 | no_match | No enrolled face matched clearly enough. | Let the user retry in better light. The response carries no candidate data. |
| 403 | forbidden | This credential cannot call this route, for example a publishable key on /v1/users. | Use the right credential for the route. |
| 403 | origin_not_allowed | The request Origin is not on the app's allow list. | Add the exact origin in the portal. |
| 403 | plan_limit_reached | You hit a plan count limit such as the number of apps or keys. Includes limit and used. | Remove something or upgrade your plan. |
| 404 | not_found | The user or resource does not exist in this app. | Check the id. |
| 409 | email_taken | The email already exists in this app. | Use a different email, or sign the user in instead. |
| 409 | face_already_enrolled | The face matches an existing user in this app. The response never names that user. | Sign in as the existing user. |
| 413 | payload_too_large | The body is over the limit (256 KB on /v1/enroll). | Send fewer descriptors. |
| 429 | plan_limit_reached | The team used all its auths for the month. Includes limit, used and resets_at. | Wait until resets_at or upgrade. |
| 429 | rate_limited | Too many requests. A Retry-After header gives the seconds to wait. | Back off and retry. |
| 500 | internal_error | A fault on our side. The response contains no details. | Retry. If it persists, contact support with the time of the call. |
| 503 | none | GET /health reports db: "down". | Retry later. |
Rate limit and plan limit examples
{
"error": {
"code": "plan_limit_reached",
"message": "Monthly auth limit reached.",
"limit": 1000,
"used": 1000,
"resets_at": "2026-11-01T00:00:00.000Z"
}
}plan_limit_reached uses two statuses. An exhausted monthly auth allowance is 429. A count limit (number of apps or keys per plan) is 403. See Plans and limits.
In the SDK
SDK calls throw FaceCardError. It has the same code and the HTTP status. See FaceCardError.