Skip to main content

Enroll and identify via REST

This walkthrough covers the calls most integrations need: create a watchlist, register a member with face images, identify a face against the watchlists (1:N), verify two images against each other (1:1), run a passive liveness check and detect faces on a still image. All examples use the host-side base URL http://localhost:8098; from a container on face-matcher-network use http://api:8080. Image data is always a Base64-encoded JPEG or PNG in a data field; the samples abbreviate it.

1. Create a watchlist​

POST /api/v1/Watchlists creates a watchlist. The threshold is the matching score (0-100) a face must reach to count as a match against members of this watchlist; 40 is the platform default and a good starting point (see Accuracy and thresholds). previewColor is the hexadecimal colour Station and the enhanced preview use for members of this watchlist.

{
"displayName": "Employees",
"fullName": "Employees - head office",
"threshold": 40,
"previewColor": "#4adf62"
}

The response echoes the watchlist with its generated id. Keep it: every later call refers to the watchlist by this identifier. GET /api/v1/Watchlists?Ascending=true&PageSize=10&ShowTotalCount=true lists existing watchlists with paging, GET /api/v1/Watchlists/{id} returns one, PUT /api/v1/Watchlists updates one and DELETE /api/v1/Watchlists/{id} removes it (members are kept and must be deleted separately).

2. Register a watchlist member​

POST /api/v1/WatchlistMembers/Register creates a member, extracts a face template from each image and links the member to one or more watchlists in a single call. Provide your own id to reuse an identifier from your system, or omit it to have one generated.

{
"id": "emp-000123",
"fullName": "Jane Doe",
"displayName": "Jane D.",
"images": [
{ "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." }
],
"watchlistIds": [
"8f02f8b6-dd02-4dd1-bc24-bc559ce16705"
],
"faceDetectorConfig": {
"minFaceSize": 30,
"maxFaceSize": 600,
"maxFaces": 1,
"confidenceThreshold": 1450
}
}

Up to 50 images are accepted per request; the API can also download images from URIs you reference instead of inline data (see Enrollment limits). faceDetectorConfig rejects images whose face is too small or too large; set maxFaces to 1 for enrollment so a background face cannot be enrolled by mistake. Each image must also pass the platform's face validation (size, pose and quality) or the request fails with a descriptive error. A member can be linked to further watchlists later with POST /api/v1/WatchlistMembers/LinkToWatchlist and removed from one with .../UnlinkFromWatchlist.

GET /api/v1/WatchlistMembers pages through all members, GET /api/v1/Watchlists/{id}/WatchlistMembers through the members of one watchlist:

{
"totalItemsCount": null,
"items": [
{
"displayName": "Jane D.",
"fullName": "Jane Doe",
"note": null,
"labels": [],
"id": "emp-000123",
"createdAt": "2026-09-01T12:53:47.726661Z",
"updatedAt": null
}
],
"pageSize": 10,
"pageNumber": 1,
"previousPage": null,
"nextPage": null
}

DELETE /api/v1/WatchlistMembers/{id} removes a member, its templates and all its watchlist links.

3. Identify a face (1:N)​

POST /api/v1/Watchlists/Search detects faces on the image, extracts a template for each and matches it against the members of the given watchlists. An empty watchlistIds array searches all watchlists. The minimal request is:

{
"image": { "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." },
"watchlistIds": [],
"threshold": 40,
"maxResultCount": 1
}

threshold overrides the watchlist threshold for this call and maxResultCount limits the number of candidates per face (default 1, the best match). To obtain a ranked candidate list, lower the threshold (for example to 20) and raise maxResultCount to 10. The full request also accepts the detector limits, optional face attributes and a liveness check in the same round trip:

{
"image": { "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." },
"watchlistIds": ["8f02f8b6-dd02-4dd1-bc24-bc559ce16705"],
"threshold": 40,
"maxResultCount": 1,
"faceDetectorConfig": {
"minFaceSize": 35,
"maxFaceSize": 600,
"maxFaces": 20,
"confidenceThreshold": 450
},
"faceDetectorResourceId": "cpu",
"templateGeneratorResourceId": "cpu",
"faceFeaturesConfig": {
"age": true,
"gender": true,
"yawAngle": true,
"pitchAngle": true,
"rollAngle": true,
"sharpness": true,
"brightness": true
},
"spoofDetectorResourceIds": ["none"],
"spoofCheckConfig": {
"distantLivenessScoreThreshold": 90,
"nearbyLivenessScoreThreshold": 90,
"distantLivenessConditions": "default",
"nearbyLivenessConditions": "default",
"keepEvaluatingConditionsAfterFirstFail": false
}
}

The response contains one entry per detected face with the face's crop coordinates, the requested attributes and the matched candidates, each with the member identifiers and the score:

[
{
"matchResults": [
{
"watchlistMemberId": "emp-000123",
"watchlistMemberFullName": "Jane Doe",
"watchlistMemberDisplayName": "Jane D.",
"watchlistId": "8f02f8b6-dd02-4dd1-bc24-bc559ce16705",
"score": 78
}
],
"cropLeftTopX": 412,
"cropLeftTopY": 188,
"cropRightBottomX": 640,
"cropRightBottomY": 431,
"age": 34,
"gender": "Female"
}
]

POST /api/v1/Watchlists/SearchByTemplate does the same with a face template you already hold instead of an image. Searching the history of faces seen by cameras, rather than watchlist members, is a different operation: see Face search.

4. Verify two images (1:1)​

POST /api/v1/Faces/Verify answers whether two images show the same person, without any watchlist. Send the image to check as probeImage and the trusted one as referenceImage; the response is the matching score on the same 0-100 scale, so apply your own threshold (40 by default).

{
"probeImage": { "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." },
"referenceImage": { "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." },
"faceDetectorConfig": {
"minFaceSize": 30,
"maxFaceSize": 600,
"maxFaces": 1,
"confidenceThreshold": 1450
}
}
{ "score": 72 }

5. Check liveness​

POST /api/v1/Faces/SpoofCheck runs passive liveness on the face in an image, either on its own or as part of a search (step 3). spoofDetectorResourceIds selects the detectors: liveness_distant_cpu_remote for the distant check (a face captured from a distance, typical for cameras) and liveness_nearby_cpu_remote for the nearby check (a selfie-style capture); _gpu_ variants exist when GPU acceleration is configured. Each check has a score threshold and a set of preconditions on the face (size, pose, exposure) that must hold before the check runs.

{
"image": { "data": "/9j/4AAQSkZJRgABAQEAYABgAAD..." },
"faceDetectorConfig": {
"minFaceSize": 35,
"maxFaceSize": 600,
"confidenceThreshold": 450
},
"faceDetectorResourceId": "cpu",
"spoofDetectorResourceIds": ["liveness_distant_cpu_remote"],
"spoofCheckConfig": {
"distantLivenessScoreThreshold": 90,
"nearbyLivenessScoreThreshold": 90,
"distantLivenessConditions": "default",
"nearbyLivenessConditions": "default",
"keepEvaluatingConditionsAfterFirstFail": false
}
}

The response aggregates both checks and then reports each one. performed is true when the preconditions held and the check ran, passed when the score reached the threshold. When a check was not performed, notPerformedReasons lists the failed conditions; set keepEvaluatingConditionsAfterFirstFail to true to receive all of them instead of the first.

{
"performed": true,
"passed": true,
"distantLivenessSpoofCheck": {
"performed": true,
"passed": true,
"score": 97.27,
"notPerformedReasons": []
},
"nearbyLivenessSpoofCheck": {
"performed": false,
"passed": false,
"score": 0,
"notPerformedReasons": [
{ "reasonMessage": "Not selected" }
]
}
}

Flowchart of the SpoofCheck call: request timeout, no face detected, conditions not met, score below threshold (spoof) or above (live), with the JSON returned at each outcome

A 408 response means the detection timed out and a 400 with NoFaceDetected means no face was found. Read Liveness for how the two checks differ and how to choose thresholds.

6. Detect faces on an image​

POST /api/v1/Detect runs detection only: it returns the bounding boxes, order and confidence of what it finds on the image, without extracting templates or matching. Use it to validate a capture before enrollment, or to count faces in a frame. The request takes the same image object and detector configuration (minimum and maximum size, maximum count, confidence threshold) as the calls above; the resource IDs in the request select which detectors run, so the same endpoint also serves pedestrian and object detection when those are enabled.

For the exact schemas of every request and response, use the Swagger UI on your installation at http://localhost:8098; it always matches the deployed version.