Skip to content

Face Validation

This endpoint answers a single question: does the face in the image sent match the official biometrics on record for the CPF (the Brazilian individual taxpayer number) given in the URL?

You send one image and a CPF. The API locates the face in the image, crops it, and checks it against the official database. The response is a lean verdict — no comparison between two of your own photos, no registration data, and no document extraction.

This endpoint does not compare two images with each other (that is what Face Match is for), does not perform liveness (use Liveness), does not extract the data written on the document, and does not return the registration data of the CPF (use the Individual Bureau).

Face Validation or Face Match + Datavalid

Both endpoints validate a face against the official database from a CPF. The difference is in what else they do, and in the shape of the verdict.

Face ValidationFace Match + Datavalid
Route/face-validation/v1/natural-person/{CPF}/face-match-and-datavalid/v2/natural-person/{CPF}
Files per call1 image2 files
Compares your images with each otherNoYes, returns matched and confidence
Validates against the official databaseYesYes
Accepted formatsimage/png, image/jpegimage/png, image/jpeg, application/pdf
Sending as JSON (base64)AcceptedAccepted
similarity fieldNot returnedReturned
Probability bandVeryHigh, High, Low, VeryLow, UnknownVeryHigh, High, Low, VeryLow
Registration data of the CPFNot returnedReturns federalRevenueNumberStatus
Format of dataObjectList
Permissionnextid.bureaus.faceValidationnextid.bureaus.faceMatchAndDatavalid

Use Face Validation when you have only one image — a selfie, for example — and all you need to know is whether it belongs to the holder of the CPF. Use Face Match + Datavalid when you also need to check two of your own images against each other, or when you need the registration status of the CPF in the same call.

Request

POST /face-validation/v1/natural-person/{CPF}

Headers

http
Authorization: ApiKey <your-api-key>

This endpoint also accepts the JWT token, in the format Authorization: Bearer <accessToken>. See JWT Token.

The call requires the nextid.bureaus.faceValidation permission. It is a permission of its own: whoever calls Face Match + Datavalid does not normally gain access to this endpoint because of it.

Parameters

ParameterDescriptionRequired
CPFThe CPF whose biometrics will be queried. Goes in the URL path, with or without formatting. It is validated by its check digits before any query takes place.Yes
returnsFaceInfoWhen true, the face object in the response gains boundingBox and croppedBase64. Query string. Default: false.No

The CPF can be sent with or without formatting (123.456.789-09 or 12345678909), always with all 11 digits, including leading zeros.

🚧 The query string of this endpoint is strict

Unlike other endpoints, here the query string is validated:

  • returnsFaceInfo accepts only true or false. Any other encoding — 1, 0, TRUE, yes — is rejected with 422, rather than silently ignored.
  • An unknown parameter brings the request down. ?returnsFaceInfoo=true, with the typo, returns 422 instead of processing the call without the face info.

This is deliberate: a misspelled parameter fails loudly, instead of returning 200 with a response that is not the one you asked for.

Accepted files

ItemValue
Formatsimage/png, image/jpeg
Files per call1 file
Ways to sendmultipart/form-data or base64 in JSON
Maximum size (multipart)15 MB

The image can be sent as multipart/form-data or in base64, in the base64 field of the JSON body, as described in Sending files. Both ways behave the same: the same formats, the same limit of one file per call, and the same rejections.

The field name — the form field in multipart, or the key inside base64 in JSON — is up to you, and it comes back in the response, in metadata.filesInfo. Sending more than one file is rejected with 422, and so is sending none.

‼️ The urls field is not accepted on this endpoint

Sending the file by URL is not accepted here. A JSON body carrying the urls field is rejected with 422, before the file is ever fetched:

json
{
  "id": "e2b0f3a7-5c41-4c2b-9d33-8a5f1c7e4b02",
  "error": {
    "statusCode": 422,
    "error": "Unprocessable Entity",
    "message": "Sending files by URL is not supported. Use multipart/form-data or the \"base64\" field."
  }
}

PNG and JPEG only, in both ways of sending. Any other mimetype — PDF included — returns 422, and so does a submission with more than one file.

If the image has more than one face, the one with the highest confidence is the one used in the validation. If no face is detected, the request fails with 422 — the query to the official database never happens.

Example request

bash
curl -i -X POST 'https://api-homolog.nxcd.app/face-validation/v1/natural-person/12345678909' \
  --header 'Authorization: ApiKey YOUR_API_KEY' \
  --form 'selfie=@./selfie.jpg'

Also asking for the crop of the face found:

bash
curl -i -X POST 'https://api-homolog.nxcd.app/face-validation/v1/natural-person/123.456.789-09?returnsFaceInfo=true' \
  --header 'Authorization: ApiKey YOUR_API_KEY' \
  --form 'selfie=@./selfie.jpg'

The same call, with the image in base64:

bash
curl -i -X POST 'https://api-homolog.nxcd.app/face-validation/v1/natural-person/12345678909' \
  --header 'Authorization: ApiKey YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{ "base64": { "selfie": "BASE_64_HERE" } }'

Response

FieldDescriptionType
idUnique identifier of the requestString
versionAPI version that served the callString
dataObject with the result of the validation. It is not a list.Object
data.federalRevenueNumberThe CPF queried, without formattingString
data.faceThe verdict of the validationObject
data.face.availabilitytrue when there was enough of a face to assess. false when there was no verdict.Boolean
data.face.probabilityProbability band of the face sent belonging to the holder of the CPF: VeryHigh, High, Low, VeryLow or UnknownString
data.face.boundingBoxPosition of the face in the image sent, in proportions between 0 and 1. Only appears with returnsFaceInfo=true.Object
data.face.croppedBase64Crop of the face in base64. Only appears with returnsFaceInfo=true.String
metadataObject with the metadata of the requestObject
metadata.filesInfoList with a single entry, for the file sentObject[]
metadata.filesInfo[].fieldnameName of the field used to send the fileString
metadata.filesInfo[].nameName of the file sentString
metadata.filesInfo[].sizeSize of the file sent, in bytesNumber
metadata.filesInfo[].pagesNumber of pages of the file. Since only images are accepted, it is always 1.Number
metadata.filesInfo[].mimetypeType of the file sentString
metadata.filesInfo[].encodingEncoding of the file as sentString
metadata.filesInfo[].sha256SHA-256 hash of the file sentString
metadata.timeSpentProcessing time of the request, in millisecondsNumber

boundingBox carries top, left, width and height as proportions of the image, between 0 and 1 — not in pixels. The origin is the top-left corner: left and top are the distance from the face to that corner, divided by the width and the height of the image respectively. To get pixels, multiply left and width by the width of the image, and top and height by its height.

The values of probability

availabilityprobabilityWhat it means
trueVeryHighAssessed. Very high probability of being the holder of the CPF.
trueHighAssessed. High probability.
trueLowAssessed. Low probability.
trueVeryLowAssessed. Very low probability.
falseUnknownNot assessed. There was not enough of a face to produce a verdict.

‼️ Unknown is not a bad result — it is the absence of a result

This is the point that gets misread most often. Unknown and VeryLow are not the same thing, and the difference changes the decision you make:

  • VeryLow (with availability: true) is a negative verdict: the face was compared with the official database, and the result is that it probably is not the holder of the CPF.
  • Unknown (with availability: false) is no verdict at all: there was not enough of a face to assess. The API is not saying the person is not the holder — it is saying it does not know.

Treating Unknown as a rejection denies access to legitimate people over an image or database problem. Treating Unknown as an approval lets anyone through. The right thing is to treat it as a third path: ask for a new capture, or send it for manual review.

In practice, test availability before looking at probability.

🚧 This endpoint does not return similarity

If you are coming from Face Match + Datavalid, note that the similarity field does not exist here, and it is not going to start existing.

The validation used by this endpoint produces a risk band, not a continuous measure. There is no number from 0 to 1 behind probability that was ever measured. Exposing one would be inventing precision the data does not have.

Do not build business rules expecting a continuous score here: decide on the probability bands, which are the real data.

JSON examples

  1. Face validated with very high probability
Status Code: 200
json
{
  "id": "13cfec7c-b238-4820-a4d1-5173e4c1418e",
  "version": "v1",
  "data": {
    "federalRevenueNumber": "12345678909",
    "face": {
      "availability": true,
      "probability": "VeryHigh"
    }
  },
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "selfie",
        "name": "selfie.jpg",
        "size": 890098,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "41c2ece339abfcf9ba1e7d5a60666162a24ef34ba95adeb41ed1d9721c91c6c4"
      }
    ],
    "timeSpent": 4210
  }
}
  1. No verdict — there was not enough of a face to assess
Status Code: 200
json
{
  "id": "6a1d1e8c-4a7d-4a5e-9f2c-1b0f2a9c7e11",
  "version": "v1",
  "data": {
    "federalRevenueNumber": "12345678909",
    "face": {
      "availability": false,
      "probability": "Unknown"
    }
  },
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "selfie",
        "name": "selfie.jpg",
        "size": 762310,
        "pages": 1,
        "mimetype": "image/jpeg",
        "encoding": "7bit",
        "sha256": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae"
      }
    ],
    "timeSpent": 3980
  }
}

TIP

This is a 200, not an error. The request was processed; what did not happen was a verdict. See the block above on how to handle Unknown.

  1. With returnsFaceInfo=true
Status Code: 200
json
{
  "id": "a7f0c3d2-9e51-4b8a-8f31-2d4c6b9e0a77",
  "version": "v1",
  "data": {
    "federalRevenueNumber": "12345678909",
    "face": {
      "availability": true,
      "probability": "High",
      "boundingBox": { "top": 0.11, "left": 0.26, "width": 0.42, "height": 0.69 },
      "croppedBase64": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAJYCAMAAA...K5CYII="
    }
  },
  "metadata": {
    "filesInfo": [
      {
        "fieldname": "selfie",
        "name": "selfie.png",
        "size": 1204877,
        "pages": 1,
        "mimetype": "image/png",
        "encoding": "7bit",
        "sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
      }
    ],
    "timeSpent": 5120
  }
}

Response headers

HeaderWhen it appears
Nextid-ReqIdOn every response. Carries the request identifier, the same one as the id field in the body.

Errors

CodeWhen it happens
401 UnauthorizedAPI key or JWT token missing, invalid, or without the nextid.bureaus.faceValidation permission.
404 Not FoundThe version given in the URL does not exist. Only v1 is accepted.
406 Not AcceptableThe body was sent as JSON, but without the base64 field.
413 Payload Too LargeThe file sent is larger than 15 MB.
415 Unsupported Media TypeThe Content-Type header is missing, or is neither multipart/form-data nor application/json.
422 Unprocessable EntityA CPF that fails check-digit validation; the urls field in the body; no file sent; more than one file; a file format that is not accepted; an unknown query parameter or one with an invalid value; no face detected in the image; a face rejected for low quality.
500 Internal Server ErrorUnexpected failure during processing, including the official database being unavailable.

TIP

This endpoint hardly uses 400 at all. 422 is the rejection code for every problem with what was sent — from the CPF to the file format, query string included.

The file-related rejections come back with 422 and one of these messages, the same in both ways of sending:

messageWhen it happens
Sending files by URL is not supported. Use multipart/form-data or the "base64" field.The body carries the urls field.
An image file is required.No file was sent.
Exceed limit of files. Max allowed 1.More than one file in the same call.
Expected one of the following mimetypes: image/png, image/jpegThe file is neither PNG nor JPEG. PDF included.

Example response with an invalid CPF:

Status Code: 422
json
{
  "id": "13cfec7c-b238-4820-a4d1-5173e4c1418e",
  "error": {
    "statusCode": 422,
    "error": "Unprocessable Entity",
    "message": "Invalid Federal Revenue Number"
  }
}

Example response when no face is detected in the image:

Status Code: 422
json
{
  "id": "6a1d1e8c-4a7d-4a5e-9f2c-1b0f2a9c7e11",
  "error": {
    "statusCode": 422,
    "error": "Unprocessable Entity",
    "message": "No face detected in the image"
  }
}

🚧 No face in the image is 422, not Unknown

These are different situations, and they arrive by different routes:

  • We found no face in the image you sent422, with the message above. The validation never even runs.
  • We found the face, but there was no way to produce a verdict200, with availability: false and probability: "Unknown".

In the first case the problem is in the image sent, and asking for a new capture usually solves it.

The error response format is described in HTTP response codes.

Versions

This endpoint has a single version, v1. It is the default when the version is omitted from the URL.

VersionStatusWhat changes
v1RecommendedThe only version. Serves both /face-validation/natural-person/{CPF} and /face-validation/v1/natural-person/{CPF}.

Any other value in the URL — /face-validation/v2/natural-person/{CPF}, for example — returns 404.

Nextcode | Identity Verification Solutions