VerifEye Face API

Overview

The VerifEye Face API is the single entry point for face onboarding, identification and analysis. One call takes an image, works out who is on it against your own face collection, and — when you ask for it — returns the embedding, age, gender, emotions and attention of every face it answers about.

The VerifEye Face API enables you to:

  • Onboard faces into a collection — every face is looked up and added when it is not already there
  • Identify faces against a collection — every face is looked up and nothing is ever added
  • Enrich each face with optional attributes: embedding, age, gender, emotions and attention
  • Answer about several faces on one image in a single call
  • Compare two embeddings directly, without a collection
  • Manage collections (create, retrieve, update, reset, delete)
  • Associate your own external keys (for example a customer or user identifier) with faces and look faces up by them

Base URLs

Region Base URL
EU https://verifeye-face-api-eu.realeyes.ai/v1/
US https://verifeye-face-api-us.realeyes.ai/v1/

Onboard or Identify?

The two face operations take the same request and return the same response. They differ in one thing only: whether a face the collection does not hold yet is added to it.

  Onboard Identify
Face already in the collection returns its faceId (faceStatus: Existing) returns its faceId (faceStatus: Existing)
Face not in the collection adds it and returns the new faceId (faceStatus: New) returns no faceId (faceStatus: NotExisting) — nothing is stored
Use it when you want to keep the face: sign-up, enrollment, duplicate detection, building up the population you will recognise later you only want to know who this is: authentication, re-verification, age or emotion analysis of an anonymous visitor

Rule of thumb: if you want to keep the faces, call Onboard. For everything else, call Identify.


Requests and responses

Images

Supply the image either as a base64-encoded JPEG/PNG (image.bytes) or as a URL (image.url). When both are set, bytes wins. Only http and https URLs are accepted, and the host must resolve to a public address — private, loopback and link-local addresses are rejected with 400.

See Image Requirements for resolution, file size and face-position guidance.

Optional attributes

Every attribute is off by default. Enable only what you need through the include object in the request body. The API returns the requested attributes for every face it answers about.

Option Adds to each face
include.embedding embedding — a 512-value face embedding
include.age age — the estimated age and its uncertainty
include.gender genderMale or Female
include.emotions emotions — confusion, contempt, disgust, happiness, empathy, surprise
include.attention attention — presence, eyes on screen, attention

Face detection restrictions

Faces that the API cannot process with sufficient confidence are omitted from the response — they do not appear in faces. This is more common when the image contains multiple faces.

So faces can hold fewer entries than the image holds faces. Those omitted faces are not counted in unprocessedFaceCount; that field counts the faces the call never looked at — the ones maxFaces left out, plus any beyond the detector’s own limit. Do not rely on the position of a face in faces to match the position of a face on the image; match on faces[].face.boundingBox instead.

Multiple faces

maxFaces (default 1, maximum 4) decides how many faces one call answers about. Faces are processed in order of dominance, and every face the limit left out is counted in unprocessedFaceCount.


API Endpoints

Onboard

Onboards the faces on the image into the collection: every face is looked up and, when it is not already there, added.

Endpoint: POST /v1/face/onboard

Authentication: API Key or Bearer Token

Request Body:

{
  "image": {
    "bytes": "base64-encoded-image-string",
    "url": null
  },
  "collectionId": "my-collection",
  "faceMatchThreshold": 80,
  "maxFaces": 1,
  "include": {
    "embedding": false,
    "age": true,
    "gender": true,
    "emotions": false,
    "attention": false
  }
}

Request Parameters:

Parameter Type Required Description
image object Yes The image to onboard
image.url string (nullable) No URL of a jpeg or png image. http/https only, and the host must resolve to a public address
image.bytes string (nullable) No Base 64 string encoded binary jpeg or png image. Takes precedence over image.url
collectionId string Yes The id of the collection to onboard into. Max length 512. May only contain letters, numbers, dashes (-), underscores (_) and dots (.)
faceMatchThreshold integer No Minimum confidence in the face match to accept. Default value: 80. Valid range: 0-100.

Threshold reference (computed using extensive in-the-wild datasets):
95 corresponds to FPR 1e-06
90 corresponds to FPR 1e-05
80 corresponds to FPR 1e-4
70 corresponds to FPR 1e-3
maxFaces integer No The maximum number of faces to answer about. Default value: 1. Valid range: 1-4. Faces beyond this limit are counted in unprocessedFaceCount
include object No Which optional attributes are returned for every face. All options default to false. Enable only the ones you read — see Optional attributes for the cost
include.embedding boolean No Return the face embedding
include.age boolean No Return the estimated age
include.gender boolean No Return the detected gender
include.emotions boolean No Return the detected emotions
include.attention boolean No Return the detected presence and attention

Response Example:

{
  "faces": [
    {
      "face": {
        "confidence": 0.9987,
        "boundingBox": {
          "x": 120,
          "y": 80,
          "width": 200,
          "height": 250
        }
      },
      "faceId": "face_abc123xyz789",
      "externalKey": "user-123",
      "embedding": null,
      "age": {
        "prediction": 31.4,
        "uncertainty": 0.42
      },
      "gender": "Female",
      "emotions": null,
      "attention": null,
      "faceStatus": "Existing"
    }
  ],
  "unprocessedFaceCount": 0
}

Response Fields:

Field Path Type Description
faces array One entry per face this call answered about. Empty when no face was detected on the image, or when none of the detected faces could be answered for. A face the API cannot answer for with confidence has no entry — see Face detection restrictions
faces[].face object Detected face information, always in the coordinates of the image you supplied
faces[].face.confidence number Face detection score with value range [0.0, 1.0] (higher is better)
faces[].face.boundingBox object Model for the bounding box of a detected face
faces[].face.boundingBox.x integer Horizontal position of the detected face bounding box
faces[].face.boundingBox.y integer Vertical position of the detected face bounding box
faces[].face.boundingBox.width integer Width of the detected face bounding box
faces[].face.boundingBox.height integer Height of the detected face bounding box
faces[].faceId string (nullable) The id of the face. Always present on onboard — a face that was not found is added, so it always has one
faces[].externalKey string (nullable) The external key associated with the face, when one has been set via the External Key endpoints. null otherwise. A newly added face never has one
faces[].embedding array (nullable) 512 values. Returned only when include.embedding is true
faces[].age object (nullable) Returned only when include.age is true
faces[].age.prediction number (nullable) Estimated age
faces[].age.uncertainty number (nullable) Uncertainty score with value range [0.0, infinity]. We recommend rejecting everything higher than 1.0 — values above it can indicate a problem with the image quality
faces[].gender string (nullable) Male or Female. Returned only when include.gender is true
faces[].emotions object (nullable) Returned only when include.emotions is true. A null property means that state could not be determined reliably
faces[].emotions.confusion boolean (nullable) Whether the person shows confusion
faces[].emotions.contempt boolean (nullable) Whether the person shows contempt
faces[].emotions.disgust boolean (nullable) Whether the person shows disgust
faces[].emotions.happy boolean (nullable) Whether the person shows happiness
faces[].emotions.empathy boolean (nullable) Whether the person shows empathy
faces[].emotions.surprise boolean (nullable) Whether the person shows surprise
faces[].attention object (nullable) Returned only when include.attention is true. A null property means that state could not be determined reliably
faces[].attention.presence boolean (nullable) Whether a person is present in the image
faces[].attention.eyesOnScreen boolean (nullable) Whether the person's eyes are on the screen
faces[].attention.attention boolean (nullable) Whether the person is attentive
faces[].faceStatus string Existing (matched a face already in the collection) or New (added by this call). Onboard never returns NotExisting — see Face status
unprocessedFaceCount integer Faces detected on the image but not answered about, because maxFaces limited how many are processed

Example Request:

curl -X POST "https://verifeye-face-api-eu.realeyes.ai/v1/face/onboard" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "image": {
      "bytes": "/9j/4AAQSkZJRgABAQEAYABgAAD..."
    },
    "collectionId": "my-collection",
    "include": {
      "age": true,
      "gender": true
    }
  }'

Response Codes:

  • 200 - The faces were onboarded
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found
  • 504 - The request did not complete within the time budget

Identify

Identifies the faces on the image against the collection: every face is looked up and never added.

Endpoint: POST /v1/face/identify

Authentication: API Key or Bearer Token

The request and the response are identical to Onboard, with one difference: a face the collection does not hold comes back with faceStatus: NotExisting and no faceId, and nothing is stored. Every attribute that was asked for is still returned for it — only the identity is missing.

Request Body:

{
  "image": {
    "bytes": "base64-encoded-image-string",
    "url": null
  },
  "collectionId": "my-collection",
  "faceMatchThreshold": 80,
  "maxFaces": 2,
  "include": {
    "embedding": false,
    "age": false,
    "gender": false,
    "emotions": true,
    "attention": true
  }
}

Response Example:

{
  "faces": [
    {
      "face": {
        "confidence": 0.9987,
        "boundingBox": {
          "x": 120,
          "y": 80,
          "width": 200,
          "height": 250
        }
      },
      "faceId": "face_abc123xyz789",
      "externalKey": "user-123",
      "embedding": null,
      "age": null,
      "gender": null,
      "emotions": {
        "confusion": false,
        "contempt": null,
        "disgust": false,
        "happy": true,
        "empathy": null,
        "surprise": false
      },
      "attention": {
        "presence": true,
        "eyesOnScreen": true,
        "attention": true
      },
      "faceStatus": "Existing"
    },
    {
      "face": {
        "confidence": 0.9721,
        "boundingBox": {
          "x": 430,
          "y": 96,
          "width": 180,
          "height": 224
        }
      },
      "faceId": null,
      "externalKey": null,
      "embedding": null,
      "age": null,
      "gender": null,
      "emotions": {
        "confusion": null,
        "contempt": null,
        "disgust": null,
        "happy": false,
        "empathy": null,
        "surprise": false
      },
      "attention": {
        "presence": true,
        "eyesOnScreen": false,
        "attention": false
      },
      "faceStatus": "NotExisting"
    }
  ],
  "unprocessedFaceCount": 1
}

Example Request:

curl -X POST "https://verifeye-face-api-eu.realeyes.ai/v1/face/identify" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "image": {
      "url": "https://example.com/photo.jpg"
    },
    "collectionId": "my-collection",
    "maxFaces": 2,
    "include": {
      "emotions": true,
      "attention": true
    }
  }'

Response Codes:

  • 200 - The faces were searched for; each one either matched or did not
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found
  • 504 - The request did not complete within the time budget

Face status

Every face carries a faceStatus saying what the collection had to say about it.

Value Meaning Returned by
Existing The face matched one already in the collection, and that faceId was returned Onboard, Identify
New The face was not in the collection and has now been added to it Onboard
NotExisting The collection held no face matching this one Identify

Onboard only ever returns Existing or New: a face it does not find is added, so it always comes back with a faceId. NotExisting is Identify's alone.

NotExisting means one thing: no match. The collection held nobody who looks like this person. Every requested attribute is still returned — only the identity is missing. This is the ordinary end of an identify.


Compare Embeddings

Compares two face embeddings and returns how similar they are. Both must be embeddings returned by Onboard or Identify via include.embedding. No collection is involved and nothing is stored.

Endpoint: POST /v1/face/compare-embeddings

Authentication: API Key or Bearer Token

Request Body:

{
  "embedding1": [0.0123, -0.0456, 0.0789],
  "embedding2": [0.0118, -0.0461, 0.0802]
}

Request Parameters:

Parameter Type Required Description
embedding1 array Yes The embedding to compare. Must hold exactly 512 values
embedding2 array Yes The embedding to compare it with. Must hold exactly 512 values

Response Example:

{
  "similarity": 87
}

Response Fields:

Name Type Description
similarity integer Similarity of the two embeddings with value range [1, 100] (higher is better). Reject anything below 70; 80 is the recommended operating point.

Threshold reference:
95 corresponds to FPR 1e-06
90 corresponds to FPR 1e-05
80 corresponds to FPR 1e-4
70 corresponds to FPR 1e-3

Example Request:

curl -X POST "https://verifeye-face-api-eu.realeyes.ai/v1/face/compare-embeddings" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "embedding1": [0.0123, -0.0456, 0.0789],
    "embedding2": [0.0118, -0.0461, 0.0802]
  }'

Response Codes:

  • 200 - The embeddings were compared
  • 400 - Validation failure (a missing embedding, or one that does not hold exactly 512 values)
  • 401 - Authentication failure

Delete Face

Deletes a single face from the collection. Deleting a face also removes any external key association it had.

Endpoint: DELETE /v1/face/delete

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The id of the collection the face belongs to
faceId string Yes The id of the face to be deleted. Max 64 characters

Response Example:

{}

Example Request:

curl -X DELETE "https://verifeye-face-api-eu.realeyes.ai/v1/face/delete?collectionId=my-collection&faceId=face_abc123xyz789" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - The face was deleted (idempotent)
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

Collection Management

A collection is the store of faces that onboard and identify work against. For a conceptual overview of what a face collection is and how it relates to a Verification, see Face collection.

Get Collection

Retrieves a single collection by its identifier.

Endpoint: GET /v1/collection/get

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The collection identifier

Response Example:

{
  "collection": {
    "collectionId": "my-collection",
    "description": "Collection for employee verification",
    "createdAt": "2026-01-15T10:30:00Z",
    "updatedAt": "2026-02-10T14:20:00Z"
  }
}

Response Fields:

Field Path Type Description
collection object The collection details
collection.collectionId string Identifier of the collection
collection.description string Optional textual description
collection.createdAt string UTC timestamp when the collection was created
collection.updatedAt string UTC timestamp when the collection was last updated

Example Request:

curl -X GET "https://verifeye-face-api-eu.realeyes.ai/v1/collection/get?collectionId=my-collection" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Collection found
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

Get All Collections

Lists all collections belonging to the authenticated account.

Endpoint: GET /v1/collection/get-all

Authentication: API Key or Bearer Token

Response Example:

{
  "collections": [
    {
      "collectionId": "my-collection",
      "description": "Collection for employee verification",
      "createdAt": "2026-01-15T10:30:00Z",
      "updatedAt": "2026-02-10T14:20:00Z"
    },
    {
      "collectionId": "employee-faces",
      "description": "",
      "createdAt": "2026-01-20T08:15:00Z",
      "updatedAt": "2026-01-20T08:15:00Z"
    }
  ]
}

Response Fields:

Field Path Type Description
collections array Collections returned for the account (may be empty)
collections[].collectionId string Identifier of the collection
collections[].description string Optional textual description of the collection
collections[].createdAt string UTC timestamp when the collection was created
collections[].updatedAt string UTC timestamp when the collection was last updated

Example Request:

curl -X GET "https://verifeye-face-api-eu.realeyes.ai/v1/collection/get-all" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Collections returned
  • 400 - Validation failure
  • 401 - Authentication failure

Create Collection

Creates a new collection.

Endpoint: POST /v1/collection/create

Authentication: API Key or Bearer Token

Request Body:

{
  "collectionId": "new-collection",
  "description": "Collection for customer verification"
}

Request Parameters:

Parameter Type Required Description
collectionId string Yes Unique identifier for the collection (max length 512). May only contain letters, numbers, dashes (-), underscores (_) and dots (.). Other characters are rejected with 400
description string No Optional description (max length 1024)

Response Example:

{
  "collectionId": "new-collection"
}

Response Fields:

Name Type Description
collectionId string Identifier of the created collection

Example Request:

curl -X POST "https://verifeye-face-api-eu.realeyes.ai/v1/collection/create" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "collectionId": "new-collection",
    "description": "Collection for customer verification"
  }'

Response Codes:

  • 200 - Collection created successfully
  • 400 - Validation failure
  • 401 - Authentication failure
  • 409 - Collection already exists

Update Collection

Updates an existing collection (only description is mutable).

Endpoint: PUT /v1/collection/update

Authentication: API Key or Bearer Token

Request Body:

{
  "collectionId": "my-collection",
  "description": "Updated description for my collection"
}

Request Parameters:

Parameter Type Required Description
collectionId string Yes Identifier of the collection to update
description string No New description (max length 1024)

Response Example:

{
  "collectionId": "my-collection"
}

Response Fields:

Name Type Description
collectionId string Identifier of the updated collection

Example Request:

curl -X PUT "https://verifeye-face-api-eu.realeyes.ai/v1/collection/update" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "collectionId": "my-collection",
    "description": "Updated description for my collection"
  }'

Response Codes:

  • 200 - Collection updated
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

Reset Collection

Removes all faces from a collection without deleting the collection itself. The collection (and its description) is preserved and remains usable; only the faces it contains — and any external key associations they had — are removed. This operation is irreversible.

Endpoint: DELETE /v1/collection/reset

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The collection identifier

Response Example:

{
  "collectionId": "my-collection"
}

Response Fields:

Name Type Description
collectionId string (nullable) Identifier of the collection that was reset

Example Request:

curl -X DELETE "https://verifeye-face-api-eu.realeyes.ai/v1/collection/reset?collectionId=my-collection" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Collection reset (all faces removed)
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

Delete Collection

Deletes a collection by its identifier.

Endpoint: DELETE /v1/collection/delete

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The collection identifier

Response Example:

{}

Example Request:

curl -X DELETE "https://verifeye-face-api-eu.realeyes.ai/v1/collection/delete?collectionId=my-collection" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Collection deleted (idempotent)
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

External Key Management

External keys let you attach your own identifier (for example a customer id, user id or reference number) to a face. A face has at most one external key, while an external key maps to a single face by default — or to several faces when duplicates are explicitly allowed.

External keys are managed independently of onboarding: Onboard never sets one. After onboarding a face, call set to associate a key with it. Once associated, the key comes back on every onboard and identify result that matches the face.

Set External Key

Associates an external key with a face. Creates a new association or updates the existing one (a face's key is replaced when set again with a different key).

Endpoint: PUT /v1/external-key/set

Authentication: API Key or Bearer Token

Request Body:

{
  "collectionId": "my-collection",
  "faceId": "face_abc123xyz789",
  "externalKey": "user-123",
  "allowDuplicates": false
}

Request Parameters:

Parameter Type Required Description
collectionId string Yes The id of the collection the face belongs to
faceId string Yes The id of the face to associate the external key with. Max 64 characters
externalKey string Yes The external key to associate. See External key format
allowDuplicates boolean No When false (default), the request is rejected with 400 if the external key is already associated with a different face. When true, the key may map to multiple faces

Response Example:

{
  "faceId": "face_abc123xyz789",
  "externalKey": "user-123",
  "result": "Created"
}

Response Fields:

Field Path Type Description
faceId string The id of the face the external key was associated with
externalKey string The external key that was associated
result string Whether the association was newly created or updated. Possible values: Created, Updated

Example Request:

curl -X PUT "https://verifeye-face-api-eu.realeyes.ai/v1/external-key/set" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE" \
  -H "Content-Type: application/json" \
  -d '{
    "collectionId": "my-collection",
    "faceId": "face_abc123xyz789",
    "externalKey": "user-123",
    "allowDuplicates": false
  }'

Response Codes:

  • 200 - Association created or updated
  • 400 - Validation failure, or the external key is already associated with a different face and allowDuplicates is false
  • 401 - Authentication failure
  • 404 - Collection not found

Get Face IDs by External Key

Returns all face ids associated with the specified external key in a collection.

Endpoint: GET /v1/external-key/get-face-ids

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The collection identifier
externalKey string Yes The external key to look up. See External key format

Response Example:

{
  "faceIds": ["face_abc123xyz789"]
}

Response Fields:

Field Path Type Description
faceIds array The ids of the faces associated with the external key. Empty when none are associated. Contains more than one id only when the key was set with allowDuplicates: true

Example Request:

curl -X GET "https://verifeye-face-api-eu.realeyes.ai/v1/external-key/get-face-ids?collectionId=my-collection&externalKey=user-123" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Lookup succeeded
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

Delete External Key Association

Removes external key associations. When faceId is omitted, every face associated with the external key is unlinked; otherwise only the single pair is removed. This only removes the key association — it does not delete the face itself (use Delete Face for that).

Endpoint: DELETE /v1/external-key/delete

Authentication: API Key or Bearer Token

Query Parameters:

Parameter Type Required Description
collectionId string Yes The collection identifier
externalKey string Yes The external key whose associations are removed. See External key format
faceId string No When omitted, all pairs for the external key are removed. When provided, only that single pair is removed

Response Example:

{}

Example Request:

curl -X DELETE "https://verifeye-face-api-eu.realeyes.ai/v1/external-key/delete?collectionId=my-collection&externalKey=user-123" \
  -H "Authorization: ApiKey API-KEY-FROM-DEV-CONSOLE"

Response Codes:

  • 200 - Associations removed (idempotent)
  • 400 - Validation failure
  • 401 - Authentication failure
  • 404 - Collection not found

External Key Format

  • Maximum length: 512 characters.
  • Allowed characters: printable ASCII only (0x210x7E). Spaces, control characters and Unicode characters are rejected with 400.
  • Recommended encoding: keep keys ASCII. If you need to embed richer identifiers (Unicode names, or values containing spaces or slashes), base64url-encode them on your side and store the encoded value (e.g. matching ^[A-Za-z0-9_-]{1,512}$).
  • Why ASCII only: external keys are used as lookup keys and may appear in URLs and logs. Restricting to printable ASCII avoids URL-encoding ambiguity and Unicode normalization collisions, where two visually identical keys are not byte-for-byte equal and therefore do not match.

Use Cases

For end-to-end scenarios using this API — including registering a face and checking it later — see the Use Cases page.


Health Check

Check the API health status.

Endpoint: GET /v1/healthz

Authentication: None required

Response Example:

2026-08-04T10:30:45Z

Response Fields:

Name Type Description
(response body) string The server UTC time in ISO 8601 format

Example Request:

curl -X GET "https://verifeye-face-api-eu.realeyes.ai/v1/healthz"

Response Codes:

  • 200 - API is healthy

Common Response Codes

Code Description
200 Success
400 Bad Request - Invalid parameters
401 Unauthorized - Missing or invalid authentication
403 Forbidden - Valid authentication but account not found or insufficient permissions
404 Not Found - Resource not found
500 Internal Server Error
504 Gateway Timeout - The request did not complete within the time budget

Model validation failures (400) return a JSON body (ProblemDetails) with an errors object listing the invalid fields. A 401 returns {"message":"Unauthorized"} — no errors object. Some request-level errors — a missing image, an unfetchable image URL — return a plain-text message instead. Do not assume a single error shape; branch on Content-Type and on the status code.


Swagger Documentation

Interactive API documentation is available via Swagger UI:

The OpenAPI specification is available at:


Last updated: 2026-08-04