Biometrics · Face match

Your user's face, against the photo on their ID.

You send two images: the selfie and the front of the document. You get a 0–100 score and the decision already made against your threshold. It accepts the INE, the passport and any official document worldwide.

configurable thresholdface attributes includedzero retention
simula-la-decisionFREE · IN YOUR BROWSER
How it works

Two images in. One decision out.

It's a 1:1 verification: it compares two images against each other. It doesn't search for a face inside a database or build a biometric record of your users.

1Takes the two imagesThe selfie in face and the document front in front, both Base64 (JPG or PNG). The body accepts up to 28 MB.
2Detects and isolates the facesIt locates the face in the selfie and extracts the photo from the document. If either is missing, or there's more than one person in the selfie, it tells you instead of guessing.
3Compares and decidesIt computes similarity from 0 to 100 and checks it against your threshold. The response already carries the resolved decision in isMatch: your code compares nothing.
Input data

What you send it.

Two required fields and two optional ones. The names travel in English in the JSON; here's what each one is.

FieldWhat it isRequired
faceYour user's selfie, in Base64 (JPG or PNG). It must contain exactly one face: if there are more, the response is MULTIPLE_FACES_DETECTED.Yes
frontThe front of the document, in Base64. That's where the photo to compare comes from. It accepts any official document worldwide: INE, passport, residence card, professional ID.Yes
thresholdAcceptance threshold, 1 to 100. Defaults to 90 (KYC-grade). Overridable per call: a high threshold for sensitive operations, more permissive for low-risk actions.Optional
documentTypeRequires an exact document type: INE, IFE, MEX_PASSPORT, MEX_RESIDENCE_CARD, MEX_PROFESSIONAL_ID. With ANY it just has to be some recognized document. Omit it and the type isn't validated.Optional

The images aren't stored: they're used to compute the comparison and discarded on response. We don't keep biometric templates.

The API response

A score, a decision, and why it failed.

Beyond the result, the response carries a selfie analysis built so you can ask for a useful retry —"take off your sunglasses"— instead of a blind "try again".

faceMatchScoreSimilarity from 0 to 100 between both faces. It arrives as null when the score wasn't the basis of the result —no document detected, no face, multiple faces—: no signal is leaked that doesn't apply.
isMatchThe decision, already resolved: true if the score met your threshold. It's the only field your approval logic needs to read.
thresholdAppliedThe threshold used on that call. It stays in the response so you can audit later why an operation was approved or rejected.
detectedDocumentTypeWhich document was recognized in the front image: INE, MEX_PASSPORT, OTHER…
selfieAnalysisWhat was seen in the selfie: quality, face orientation, eyes, glasses, covered face. See the field-by-field detail ↓
issues[]Actionable retry hints: wearing_sunglasses, eyes_closed, face_covered, poor_image_quality, not_facing_camera, low_detection_confidence. With those you tell the user exactly what to fix.
More than a score

Every response tells you what it saw in the selfie.

Most providers in Mexico hand you a score and a yes or no. We also return selfieAnalysis: the facial attributes we detected, ready for your own rules. If your policy requires the face front-on, no sunglasses and eyes open, you don't need another provider or your own vision model — it's already in the same response, at no extra cost.

selfieAnalysis
"selfieAnalysis": {
  "imageQuality": "excellent",
  "detectionConfidence": "high",
  "orientation": "front",
  "eyesOpen": true,
  "mouthOpen": false,
  "wearingGlasses": true,
  "wearingSunglasses": false,
  "faceCovered": false,
  "issues": ["wearing_glasses"]
}

A real response: the person is wearing prescription glasses. The comparison passed (isMatch: true) and the detail stays in issues in case your policy treats it differently.

FieldValuesWhat it's for
imageQualityexcellent · good · poorSharpness and conditions of the photo. On poor, ask for another one before deciding anything.
detectionConfidencehigh · medium · lowHow confident the face detection was. Useful to require manual review when it isn't high.
orientationfront · looking_left · looking_right · looking_up · looking_down · tilted · sidewaysWhere the person is looking. If your policy requires a front-facing photo, accept only front.
eyesOpentrue · falseEyes open. A blink ruins the file photo even if the comparison passes.
mouthOpentrue · falseMouth open. Relevant if you need an ID-style photo with a neutral expression.
wearingGlassestrue · falsePrescription glasses. Many flows allow them; others require removing them for the file.
wearingSunglassestrue · falseSunglasses. Almost no policy accepts them: it's the flag most used to force a retry.
faceCoveredtrue · falseFace partially covered —mask, scarf, hand—. Ask to remove whatever blocks it.
issues[]wearing_glasses · wearing_sunglasses · face_covered · eyes_closed · poor_image_quality · not_facing_camera · low_detection_confidenceThe actionable summary. Instead of reading flag by flag, you loop this array and tell the user exactly what to fix.
Retry with an instruction, not blind"Take off your sunglasses" turns into an approval what another provider would have returned as an unexplained rejection.
Your own acceptance rulesYou can require orientation: "front", no sunglasses and eyes open, even if the face match passed. You set the policy, not the provider.
A usable file photoIf you keep the selfie in the customer's file, these flags tell you whether the image is good enough before you archive it.

Be clear on what it is: these attributes describe the image, they don't prove a live person is in front of the camera. They're quality and policy control, not liveness detection.

Every type, a decision

What each response means and what your code does with it.

The type field is your decision point. Three paths: proceed if the faces match, review if the problem is image quality and it's worth asking for another, reject if the result is that it isn't the same person.

typeWhat it meansDecision
SUCCESSThe faces match: the score met the threshold. It's the person on the document.Proceed
NO_FACE_DETECTEDNo face detected in the selfie or in the document. Almost always a blurry, dark or badly framed photo: ask for just that image again.Review
MULTIPLE_FACES_DETECTEDMore than one face in the selfie. Ask for a new one, with nobody else in frame.Review
NO_DOCUMENT_DETECTEDThe front image isn't a recognized identity document. It only shows up if you asked for type validation.Review
DOCUMENT_MISMATCHIt's a valid document, but not the one you required: you asked for MEX_PASSPORT and an INE arrived.Review
FACE_MISMATCHBoth faces were detected fine, but they aren't the same person: the score came in below the threshold.Reject

Technical errors arrive separately, with status: "ERROR": an image that isn't JPG/PNG or a body over 28 MB is INVALID_REQUEST with billable: false — no charge.

Raw infrastructure · 100% REST API

One call, one credit.

Same envelope as every OrigoID service: status, type, data. You learn the contract once and it works for biometrics, INE, CURP, RFC and IMSS.

Your threshold, not ours90 by default, KYC-grade — and you change it per call depending on the risk of the operation.
Global from day oneThe document can be from any country. Type validation is optional and explicit.
Zero retentionThe images are processed and discarded. No stored biometric templates.
compara-caras
const res = await fetch("https://api.origoid.com/global/biometrics/v1/face-matches", {
  method: "POST",
  headers: {
    "x-api-key": process.env.ORIGOID_API_KEY,
    "content-type": "application/json",
  },
  body: JSON.stringify({
    face: selfieBase64,
    front: documentFrontBase64,
    threshold: 90,
    documentType: "INE",
  }),
});

const { data } = await res.json();
if (data.isMatch) approve();
import os, requests

res = requests.post(
    "https://api.origoid.com/global/biometrics/v1/face-matches",
    headers={
        "x-api-key": os.environ["ORIGOID_API_KEY"],
        "content-type": "application/json",
    },
    json={
        "face": selfie_base64,
        "front": document_front_base64,
        "threshold": 90,
        "documentType": "INE",
    },
    timeout=30,
)
result = res.json()
curl -X POST https://api.origoid.com/global/biometrics/v1/face-matches \
  -H "x-api-key: $ORIGOID_API_KEY" \
  -H "content-type: application/json" \
  -d '{"face": "<base64>", "front": "<base64>", "threshold": 90, "documentType": "INE"}'
Response with the decision already made
{
  "status": "OK",
  "type": "SUCCESS",
  "data": {
    "faceMatchScore": 96.4,
    "isMatch": true,
    "thresholdApplied": 90,
    "detectedDocumentType": "INE",
    "selfieAnalysis": {
      "imageQuality": "excellent", "eyesOpen": true,
      "wearingSunglasses": false, "issues": []
    }
  },
  "billable": true
}
Real scope

What this service does not do.

We'd rather say it before you integrate. Face match answers one very specific question; these others need another piece.

Full identity? That's two calls.

Face match confirms it's the same person; the Lista Nominal confirms the credential is registered and current. Crossing both responses is an if in your backend.

See Validate INE
FAQ

What people usually ask us.

How does face match against an ID work?

You send two Base64 images: the selfie in face and the document front in front. The API detects the face in each, extracts the document photo and returns a 0–100 score along with isMatch, the decision against your threshold. It's 1:1: it compares two images, it doesn't search a database.

Which documents does it accept?

Any official document worldwide with a photo. If you want to require a specific one, use documentType: INE, IFE, MEX_PASSPORT, MEX_RESIDENCE_CARD or MEX_PROFESSIONAL_ID. With ANY it just has to be a recognized document; if it isn't, it answers NO_DOCUMENT_DETECTED.

Which threshold should I use?

The default is 90, calibrated for KYC. Lowering it accepts more legitimate cases with difficult photos, but lets more impostors through; raising it cuts fraud at the cost of rejecting legitimate people. Since it's per call, it's common to use a high one for sensitive operations and a more permissive one for low-risk actions. The response includes thresholdApplied so you can audit the decision.

Can I tell whether the person is wearing glasses or not facing the camera?

Yes, and it comes in the same response at no extra cost. The selfieAnalysis block carries wearingGlasses, wearingSunglasses, faceCovered, eyesOpen, mouthOpen, the face orientation (front, looking_left, tilted…), the image quality and the detection confidence. With that you apply your own acceptance rules —say, require a front-facing photo with no sunglasses— even if the face match already passed. See the detail.

Is it liveness detection?

No. It confirms two faces belong to the same person, and selfieAnalysis describes image quality, but it doesn't verify a live person is in front of the camera: a photo of a photo can pass. It also doesn't certify that the physical document is authentic.

Do you store the images?

No. The selfie and the document are used to compute the comparison and discarded on response: no image database and no stored biometric templates. Zero retention across all our regions.

How much does it cost?

One comparison = 1 credit. You start free with courtesy credits, no card, and the price per verification drops with your volume. All plans published. A malformed request isn't charged (billable: false).

Start free, no card.

Courtesy credits to try face match in production. Access is enabled after a brief validation of your company.

Request access