Error Codes

All error responses follow a consistent JSON structure. The error.code field contains a machine-readable string identifier that you should use in your error handling logic. Never rely on the message field, which may change.

Error response format

{
  "success": false,
  "error": {
    "code": "FACE_NOT_FOUND",
    "message": "No face found with the given ID in this collection."
  },
  "request_id": "req_01j8...",
  "timestamp": "2024-01-15T11:22:33Z"
}

HTTP status codes

StatusMeaning
200 OKRequest succeeded.
201 CreatedResource created successfully.
400 Bad RequestMissing or invalid parameters.
401 UnauthorizedMissing or invalid authentication credentials.
403 ForbiddenValid credentials but insufficient permissions or IP blocked.
404 Not FoundRequested resource does not exist.
409 ConflictResource already exists (e.g. duplicate external_id).
422 Unprocessable EntityValidation passed but business logic rejected the request.
429 Too Many RequestsRate limit exceeded.
500 Internal Server ErrorUnexpected server error. Contact support with request_id.

Authentication errors

CodeHTTPDescription
INVALID_CREDENTIALS401Email or password is incorrect.
TOKEN_EXPIRED401JWT access token has expired. Refresh it.
TOKEN_INVALID401JWT token is malformed or tampered.
INVALID_API_KEY401API key does not exist or has been revoked.
IP_NOT_ALLOWED403Request source IP is not in the key's whitelist.
REQUIRES_2FA403Account has 2FA enabled; OTP code required.
EMAIL_NOT_VERIFIED403Email address has not been verified.

Face & recognition errors

CodeHTTPDescription
FACE_NOT_FOUND404No face with the given face_id or external_id in this collection.
FACE_NOT_DETECTED422No face was detected in the submitted image.
MULTIPLE_FACES422More than one face detected; submit a single-face image.
FACE_QUALITY_TOO_LOW422Quality score below threshold (blur, occlusion, extreme pose).
LIVENESS_CHECK_FAILED422Image failed anti-spoofing check (print/replay/mask detected).
DUPLICATE_EXTERNAL_ID409An active face with this external_id already exists in the collection.

Collection & API key errors

CodeHTTPDescription
COLLECTION_NOT_FOUND404Collection does not exist or belongs to a different org.
COLLECTION_LIMIT_REACHED403Plan collection limit reached. Upgrade to create more.
FACE_LIMIT_REACHED403Collection face limit reached for your current plan.
API_KEY_LIMIT_REACHED403API key limit per member reached for your current plan.
RATE_LIMIT_EXCEEDED429API key RPM limit exceeded. See Retry-After header.

Validation errors

When multiple fields fail validation, the error includes a details array:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed.",
    "details": [
      {"field": "name", "message": "name is required"},
      {"field": "collection_id", "message": "collection_id must be a valid UUID"}
    ]
  }
}

Handling rate limits

When a 429 is returned, check the Retry-After header for the number of seconds to wait before retrying:

async function identifyWithRetry(formData, apiKey, retries = 3) {
  for (let i = 0; i < retries; i++) {
    const res = await fetch(
      'https://your-domain.com/api/v1/collections/COLLECTION_ID/identify',
      { method: 'POST', headers: { 'X-API-Key': apiKey }, body: formData }
    )
    if (res.status === 429) {
      const wait = parseInt(res.headers.get('Retry-After') ?? '5', 10)
      await new Promise(r => setTimeout(r, wait * 1000))
      continue
    }
    const data = await res.json()
    if (!data.success) throw new Error(data.error.code)
    return data
  }
  throw new Error('Max retries exceeded')
}