---
icon: verified
label: VerifEye Face API
order: 800
---

# 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](/redirect/concepts/#face-collection), and — when you ask for it — returns the 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
- **Compare** the faces on an image against one face you name — a 1:1 check that answers "is this person here?"
- Enrich each face with optional attributes: age, gender, emotions and attention
- Answer about several faces on one image in a single call
- 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/` |

---

## Choosing the right call

Three calls do the face work. They take the same image, the same optional attributes and the same `maxFaces`; what separates them is **what each face on the image is matched against**.

| | [Onboard](#onboard) | [Identify](#identify) | [Compare](#compare) |
|---|---|---|---|
| **Question it answers** | "Register this person — or tell me they are already registered" | "Who is this?" | "Is **this particular person** here?" |
| **Matching mode** | register + deduplicate | **1:N** identification | **1:1** verification |
| **Matched against** | the whole collection | the whole collection | the **one** face you name by `faceId` |
| **How the collection is used** | searched, and written to when nothing matches | searched only | one face read by its id — never searched |
| **You supply** | `collectionId` | `collectionId` | `collectionId` **and** `faceId` |
| **Each face comes back with** | `faceId` + `faceStatus` (`New` or `Existing`) | `faceId` + `faceStatus` (`Existing` or `NotExisting`) | `similarity` + `isMatch` |
| **Use it when** | signing somebody up, enrolling, detecting duplicates, building the population you will recognise later | you do not know who the person is and want the collection to tell you | you already know who they claim to be and only need that claim checked |

**Rule of thumb:** **Onboard** to put a person on file. Then **Identify** when you do not know who they are, or **Compare** when you do and only need it confirmed.

### The order of calls

A collection starts empty, so **Onboard always comes first** — there is nothing for Identify or Compare to match against until a face is on file. Onboard is normally a one-off per person; Identify and Compare are the calls you make repeatedly afterwards.

```mermaid
sequenceDiagram
    participant A as Your application
    participant F as VerifEye Face API
    Note over A,F: Once per person
    A->>F: POST /v1/face/onboard (image, collectionId)
    F-->>A: faceId + faceStatus (New or Existing)
    Note over A: Store the faceId against your own user record
    Note over A,F: Every time afterwards
    alt You do not know who this is — 1:N
        A->>F: POST /v1/face/identify (image, collectionId)
        F-->>A: faceId of the match, or faceStatus NotExisting
    else You know who they claim to be — 1:1
        A->>F: POST /v1/face/compare (image, collectionId, faceId)
        F-->>A: similarity + isMatch per face
    end
```

!!!warning Keep the `faceId` Onboard gives you
Compare needs a `faceId`, and Onboard is the only call that issues one. Store it against your own user record at sign-up — or attach your own identifier to the face with an [external key](#external-key-management) and resolve it later with [Get Face IDs by External Key](#get-face-ids-by-external-key).
!!!

---

## 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](/verifeye-apis/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.age` | `age` — the estimated age and its uncertainty |
| `include.gender` | `gender` — `Male` or `Female` |
| `include.emotions` | `emotions` — confusion, contempt, disgust, happiness, empathy, surprise |
| `include.attention` | `attention` — presence, eyes on screen, attention |

!!!warning Billing
Enabling `include.emotions` or `include.attention` incurs an additional charge.
!!!

!!!info Only turn on what you actually use
Every option you enable is additional work per face, so each one adds latency to the request. Asking for everything on several faces is measurably slower than asking for what you need.

Ask for the attributes your flow reads and nothing more. `include` is per request, not per collection, so a flow that needs age only at sign-up can leave it off everywhere else.
!!!

### 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:**

```json
{
  "image": {
    "bytes": "base64-encoded-image-string",
    "url": null
  },
  "collectionId": "my-collection",
  "faceMatchThreshold": 80,
  "maxFaces": 1,
  "include": {
    "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.<br><br>**Threshold reference** (computed using extensive in-the-wild datasets):<br>• **95** corresponds to FPR 1e-06<br>• **90** corresponds to FPR 1e-05<br>• **80** corresponds to FPR 1e-4<br>• **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](#optional-attributes) for the cost |
| `include.age` | boolean | No | Return the estimated age |
| `include.gender` | boolean | No | Return the detected gender |
| `include.emotions` | boolean | No | Return the detected emotions. Incurs an additional charge |
| `include.attention` | boolean | No | Return the detected presence and attention. Incurs an additional charge |

**Response Example:**

```json
{
  "faces": [
    {
      "face": {
        "confidence": 0.9987,
        "boundingBox": {
          "x": 120,
          "y": 80,
          "width": 200,
          "height": 250
        }
      },
      "faceId": "face_abc123xyz789",
      "externalKey": "user-123",
      "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](#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](#external-key-management) endpoints. `null` otherwise. A newly added face never has one |
| `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](#face-status) |
| `unprocessedFaceCount` | integer | Faces detected on the image but not answered about, because `maxFaces` limited how many are processed |

**Example Request:**

```bash
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](#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:**

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

**Response Example:**

```json
{
  "faces": [
    {
      "face": {
        "confidence": 0.9987,
        "boundingBox": {
          "x": 120,
          "y": 80,
          "width": 200,
          "height": 250
        }
      },
      "faceId": "face_abc123xyz789",
      "externalKey": "user-123",
      "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,
      "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:**

```bash
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

Compares the faces on the image against **one** face already in the collection, and answers how similar each of them is to it. This is the 1:1 check: the collection is never searched and never written to — the face you name by `faceId` is read directly, and every face on the image is scored against it.

Use it when you already know who the person claims to be. When you do not, use [Identify](#identify) instead.

**Endpoint:** `POST /v1/face/compare`

**Authentication:** API Key or Bearer Token

**Request Body:**

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

**Request Parameters:**

The parameters are the same as [Onboard](#onboard)'s, with one addition — `faceId`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `image` | object | Yes | The image whose faces are compared |
| `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 the face belongs to. Max length 512. May only contain letters, numbers, dashes (`-`), underscores (`_`) and dots (`.`) |
| `faceId` | string | Yes | The id of the face in the collection to compare against, as returned by [Onboard](#onboard). Max 64 characters |
| `faceMatchThreshold` | integer | No | The similarity at or above which a face counts as a match. Default value: **80**. Valid range: 0-100. It decides `isMatch` only — `similarity` is reported either way.<br><br>**Threshold reference** (computed using extensive in-the-wild datasets):<br>• **95** corresponds to FPR 1e-06<br>• **90** corresponds to FPR 1e-05<br>• **80** corresponds to FPR 1e-4<br>• **70** corresponds to FPR 1e-3 |
| `maxFaces` | integer | No | The maximum number of faces on the image to compare. 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](#optional-attributes) for the cost |
| `include.age` | boolean | No | Return the estimated age |
| `include.gender` | boolean | No | Return the detected gender |
| `include.emotions` | boolean | No | Return the detected emotions. Incurs an additional charge |
| `include.attention` | boolean | No | Return the detected presence and attention. Incurs an additional charge |

**Response Example:**

```json
{
  "faces": [
    {
      "face": {
        "confidence": 0.9987,
        "boundingBox": {
          "x": 120,
          "y": 80,
          "width": 200,
          "height": 250
        }
      },
      "similarity": 96,
      "isMatch": true,
      "age": null,
      "gender": null,
      "emotions": null,
      "attention": null
    }
  ],
  "unprocessedFaceCount": 0
}
```

**Response Fields:**

| Field Path | Type | Description |
|------------|------|-------------|
| `faces` | array | One entry per face this call compared, ordered by dominance. 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](#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[].similarity` | integer | How similar this face is to the one named by `faceId`, with value range [0, 100] (higher is better). Reported whether or not it reached the threshold, so a near miss can be told from a face that looks nothing like them |
| `faces[].isMatch` | boolean | Whether `similarity` reached `faceMatchThreshold` |
| `faces[].age` | object (nullable) | Returned only when `include.age` is `true`. Same shape as on [Onboard](#onboard) |
| `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`. Same shape as on [Onboard](#onboard) |
| `faces[].attention` | object (nullable) | Returned only when `include.attention` is `true`. Same shape as on [Onboard](#onboard) |
| `unprocessedFaceCount` | integer | Faces detected on the image but not compared, because `maxFaces` limited how many are processed |

There is no `faceId` and no `faceStatus` on a compare result: you supplied the identity, so the only thing left to answer is how well each face matches it. **Whether the person is on the image at all is `true` for any `faces[].isMatch`** — with `maxFaces: 1` that is simply `faces[0].isMatch`.

**Example Request:**

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

**Response Codes:**
- `200` - The faces were compared; each one either matched or did not
- `400` - Validation failure
- `401` - Authentication failure
- `403` - The account has reached its face check limit
- `404` - Collection not found, or the collection holds no face with that `faceId`
- `504` - The request did not complete within the time budget

!!!info A `404` means the `faceId` is gone, not that the person is absent
A face that was deleted, or a `faceId` belonging to a different collection, comes back as `404`. A person who is simply not on the image is a `200` with `isMatch: false` — the two are different answers and should be handled differently.
!!!

---

### 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:**

```json
{}
```

**Example Request:**

```bash
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, identify and compare work against. For a conceptual overview of what a face collection is and how it relates to a Verification, see [Face collection](/redirect/concepts/#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:**

```json
{
  "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:**

```bash
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:**

```json
{
  "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:**

```bash
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:**

```json
{
  "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:**

```json
{
  "collectionId": "new-collection"
}
```

**Response Fields:**

| Name | Type | Description |
|------|------|-------------|
| `collectionId` | string | Identifier of the created collection |

**Example Request:**

```bash
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:**

```json
{
  "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:**

```json
{
  "collectionId": "my-collection"
}
```

**Response Fields:**

| Name | Type | Description |
|------|------|-------------|
| `collectionId` | string | Identifier of the updated collection |

**Example Request:**

```bash
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.

!!!warning
For large collections the reset can take some time to complete. Stop onboarding into the collection before calling reset: faces onboarded after the reset has started are **not** guaranteed to be removed and may survive the operation. Resume onboarding only once the call has returned.
!!!

**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:**

```json
{
  "collectionId": "my-collection"
}
```

**Response Fields:**

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

**Example Request:**

```bash
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:**

```json
{}
```

**Example Request:**

```bash
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](#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:**

```json
{
  "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](#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:**

```json
{
  "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:**

```bash
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](#external-key-format) |

**Response Example:**

```json
{
  "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:**

```bash
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](#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](#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:**

```json
{}
```

**Example Request:**

```bash
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 (`0x21`–`0x7E`). 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](/redirect/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:**

```bash
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:

- **EU**: [https://verifeye-face-api-eu.realeyes.ai/swagger](https://verifeye-face-api-eu.realeyes.ai/swagger)
- **US**: [https://verifeye-face-api-us.realeyes.ai/swagger](https://verifeye-face-api-us.realeyes.ai/swagger)

The OpenAPI specification is available at:

- **EU**: [https://verifeye-face-api-eu.realeyes.ai/swagger/v1/swagger.json](https://verifeye-face-api-eu.realeyes.ai/swagger/v1/swagger.json)
- **US**: [https://verifeye-face-api-us.realeyes.ai/swagger/v1/swagger.json](https://verifeye-face-api-us.realeyes.ai/swagger/v1/swagger.json)

---

*Last updated: 2026-08-07*
