Face Gallery

Every channel in Verification Channels is stateless: you supply both sides of the comparison on every call. The Face Gallery is the opposite. You enrol a face once under an identifier you choose, and from then on you compare against it by reference.

That changes what you can build. Re-authenticating a returning customer no longer costs a registry lookup, and you can ask a question the stateless channels cannot answer at all: who is this?

This surface stores biometric data

Everything else in Biometrics is transient. A gallery holds face data until you erase it, which makes consent, retention, and deletion your responsibility as a data controller. Read Consent and retention before you enrol anyone.


Galleries are private to your organisation. Every enrolment, search, and deletion is scoped to you — no other organisation can see your faces or match against them, and you cannot match against theirs.

Two identifiers organise the space:

  • subject_id — your own identifier for the person: a customer ID, an employee number, an account reference. You choose it, and you use it to address that person forever after. Because it appears in requests and logs, do not put personal data in it — no names, no BVNs, no phone numbers.
  • gallery_id — an optional partition, defaulting to default. Use it to keep separate populations apart, such as customers and blocklist. A search only ever looks inside one gallery.

A subject_id is unique within a gallery, not across them. The same person can be enrolled in customers and in staff as two independent records.


Enrolment requires an explicit consent: true. The call is rejected without it, and that is deliberate: a face template is sensitive personal data under the NDPR, and storing one without a lawful basis is a regulatory problem, not a technical one.

Prembly records that you asserted consent. It cannot record the consent itself — that evidence lives with you. Pass your own reference to it in consent_reference so the enrolment can be tied back to the record that justifies it: a signed form, a timestamped in-app acceptance, a contract clause.

Two practical obligations follow:

  1. Erase faces you no longer need. A gallery has no expiry. Records persist until deleted, so retention is a process you run, not a setting you configure.
  2. Be able to honour an erasure request. The delete endpoint is that mechanism — see Erasing a face.

3. Enrolling a Face

POST https://api.prembly.com/api/v1/verification/biometrics/face/enroll/
X-API-Key: <your_api_key>
Content-Type: application/json

{
  "subject_id": "cust-1042",
  "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...",
  "consent": true,
  "consent_reference": "tos-accept-2026-10-14-cust-1042",
  "check_liveness": true
}

The enrolled image is the reference every later call is measured against. A poorly lit or partially occluded enrolment degrades every verification you perform for that person from then on, and the failures will look like false rejections rather than a bad enrolment. Spend the effort here.

Pass check_liveness: true to reject a printout, a screen, or a mask at the point of enrolment. It is the cheapest place to catch a spoof, because a spoofed enrolment poisons the record permanently.

If the subject is already enrolled in that gallery, the call fails rather than silently replacing the record. Re-enrol deliberately with overwrite: true — when a customer's appearance has changed, or when you captured a better image.

Full field list and responses: Enrol a face.


4. Verify — 1:1

Use verify when the person has told you who they are and you need to confirm it.

POST https://api.prembly.com/api/v1/verification/biometrics/face/verify/
X-API-Key: <your_api_key>
Content-Type: application/json

{
  "subject_id": "cust-1042",
  "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA..."
}

The response carries is_match, a confidence score from 0 to 100, and the threshold that was applied. The default threshold is 80; raise it for higher-risk actions such as a large transfer, and pass it per call with confidence_threshold.

Prefer verify over identify whenever you can. It is a direct comparison against one known record, so it is both cheaper and more accurate than searching a population.

Full field list and responses: Verify a face.


5. Identify — 1:N

Use identify when you do not know who the person is and want the gallery to tell you.

POST https://api.prembly.com/api/v1/verification/biometrics/face/identify/
X-API-Key: <your_api_key>
Content-Type: application/json

{
  "image": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...",
  "top_k": 5
}

Identification runs in two stages, and understanding the split is the key to reading the response:

  1. Search. The submitted face is matched across the gallery, returning candidates ranked by similarity — a score from 0 to 1 describing how near the search found them.
  2. Confirmation. The top three candidates are then compared individually against their enrolled images, producing a confidence from 0 to 100 and an is_match verdict for each.

best_match is the highest confirmed candidate at or above your confidence_threshold. Candidates beyond the top three are returned with confidence and is_match set to null — they were found by the search but never confirmed.

`confirm: false` returns a lead, not proof

Setting confirm: false skips the second stage entirely. best_match is then simply the nearest result by vector similarity, with no verification that it is the same person. Treat it as a shortlist for a human to review — never as the basis for granting access, approving a transaction, or accusing someone.

Finding nobody is a normal outcome: matched: false with an empty candidate list means the face is not in that gallery.

Full field list and responses: Identify a face.


6. Erasing a Face

DELETE https://api.prembly.com/api/v1/verification/biometrics/face/enrollments/cust-1042/?gallery_id=default
X-API-Key: <your_api_key>

This permanently removes the stored image and the derived face data. It is the mechanism to reach for when a customer exercises an erasure right, closes their account, or leaves your organisation.

Deletion touches more than one store. If any part cannot be cleared, the call returns 503 and keeps the record intact rather than leaving a half-deleted enrolment behind, so it is safe to retry until it succeeds. Treat anything other than a success as "not yet deleted" and try again.

To inspect an enrolment without deleting it, GET the same path. It returns the enrolment metadata — when it was created and updated, whether liveness was checked, the consent flag and reference. The stored image is never returned.

Full details: Get an enrolment · Erase an enrolment.


7. What Gets Billed

enroll, verify, and identify are billed operations. Reading an enrolment and erasing one are not.

The rule that surprises people: a negative result is a successful operation and is billed. A verify returning is_match: false, or an identify finding nobody, answered your question correctly and costs the same as a match. What is not billed is work that never happened — a 404 because the subject was never enrolled, a 402 for insufficient balance, or a 503 from an unavailable dependency.

Sandbox keys (test_sk_...) return canned responses and never store, search, charge, or log anything. Use them to build the integration before enrolling a real face.

On this page

Prembly Slack Community

Join our developer Slack channel for real-time integration support, engineering updates, and direct Q&A with the Prembly team.

Join Slack Channel