Riv.IA EYE API

Visual identity verification over a REST API. A single endpoint runs any combination of checks on the same image, authenticated with a Bearer Token.

Production
https://api.riv.ia.br
Dev
https://dev.riv.ia.br

How to use the API

Every endpoint requires a Bearer Token. Generate the token first and include it in the header of every request.

  1. 1

    Generate token

    Authenticate with client_id and client_secret to receive the Bearer Token.

  2. 2

    Send image

    POST to /eye/v1/verify with the image and the checks you want.

  3. 3

    Get verdict

    Explainable JSON response with the aggregated approved verdict.

Need credentials? To get your client_id and client_secret, contact us by email at contato@riviadev.com.br.

Authentication

Use your credentials to generate a short-lived Bearer Token. Include it in the Authorization: Bearer <token> header on every subsequent call.

POST/auth/v1/tokens/generate

Exchanges your credentials for an access Bearer Token.

Body (application/json)

client_idstringrequired
Client identifier provided by RiviaDev.
client_secretstringrequired
Client secret. Keep it safe — never expose it in client-side code.
Request
curl -X POST https://api.riv.ia.br/auth/v1/tokens/generate \
  -H "Content-Type: application/json" \
  -d '{"client_id":"SEU_CLIENT_ID","client_secret":"SEU_CLIENT_SECRET"}'
Response200 OK
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR...",
  "token_type": "Bearer",
  "expires_in": 3600
}

/verify endpoint

Single entry point for visual verification. Combine the checks you need in checks — the approved verdict is the conjunction of every requested check.

POST/eye/v1/verify

Headers

Authorizationstringrequired
Bearer Token obtained from /auth/v1/tokens/generate. Format: Bearer <token>.

Body (multipart/form-data)

checksstringrequired
Comma-separated list of checks to run: liveness, face_match, authenticity, element_detection, text_extraction, qr_code, document_extraction.
selfiefile
Image to verify: the person's selfie or, for qr_code, the photo with the QR code. Formats: JPG, JPEG, PNG, GIF, BMP, WEBP, DNG (max. 10MB). Required with every check, except when only document_extraction is requested.
documentfile
Document image. Required with face_match and document_extraction. For document_extraction it also accepts a 1-page PDF.
detection_requeststring (JSON)
JSON with the elements to detect. Required with element_detection.
extraction_requeststring (JSON)
JSON with the text fields to read. Required with text_extraction; optional with qr_code, where it enables the OCR fallback.

Response

Each requested check comes back as an object named after it; checks not requested come back as null. Each object's schema is in the check's section.

successboolean
Whether the call was processed successfully.
approvedboolean
Aggregated verdict: true when every requested check passed.
checksstring[]
List of the checks actually run, in order.
<check>object | null
Result of each requested check. null for checks not requested.
messagestring | null
Additional human-readable message about the result.

Human-readable texts in the response (message, details, reasons, summaries) are returned in Portuguese, as shown in the examples.

Several checks in one call

Combine checks in the same request — they run in parallel.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@selfie.jpg" \
  -F "document=@rg.jpg" \
  -F 'detection_request={"elements":[{"type":"object","description":"crachá","required":true}]}' \
  -F "checks=liveness,face_match,authenticity,element_detection"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["liveness", "face_match", "authenticity", "element_detection"],
  "liveness": { "resultado": true, "confidence": 0.93, ... },
  "face_match": { "same_person": true, "confidence": 0.88, ... },
  "authenticity": { "is_authentic": true, "confidence": 0.9, ... },
  "element_detection": { "all_required_found": true, ... },
  "message": "Verificação aprovada"
}

Checks

liveness

Liveness

Checks whether the image is a real selfie of a live person — not a screen photo, a printed photo or an AI-generated image.

Send: checks=liveness + selfie

Object liveness

resultadoboolean
true when the image is a real selfie of a human being. This is what feeds approved.
person_presentboolean | null
Whether a physical person is in the image.
confidencefloat | null
Confidence of the verdict.
detalhesstring[]
Reasons for rejection. Empty when approved.
authenticityobject | null
Anti-spoofing verdict computed in the same inference. Same schema as the authenticity check.

Behavior

  • Requesting liveness together with authenticity uses a single inference: the anti-spoofing result is reused.

Real selfie of a live person.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@selfie.jpg" \
  -F "checks=liveness"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["liveness"],
  "liveness": {
    "resultado": true,
    "person_present": true,
    "confidence": 0.93,
    "detalhes": [],
    "authenticity": { "is_authentic": true, ... }
  },
  "message": "Verificação aprovada"
}
face_match

Face matching

Compares the face in the selfie with the document photo and says whether they are the same person.

Send: checks=face_match + selfie + document

Object face_match

same_personboolean
true when the selfie and the document belong to the same person.
confidencefloat
Matching confidence, from 0 to 1.
detailsstring[]
Structural details of the analysis (eyes, nose, contour).

Behavior

  • Without document the API responds 422.

Selfie and document of the same person.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@selfie.jpg" \
  -F "document=@rg.jpg" \
  -F "checks=face_match"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["face_match"],
  "face_match": {
    "same_person": true,
    "confidence": 0.88,
    "details": ["Estrutura facial compatível."]
  },
  "message": "Verificação aprovada"
}
authenticity

Authenticity / anti-spoofing

Detects screen photos, printed photos or AI-generated images. Works on any image, with a person or an object.

Send: checks=authenticity + selfie

Object authenticity

is_authenticboolean
true when the image is not a spoof.
is_screen_photoboolean
Photo of a screen or monitor.
is_printed_photoboolean
Photo of printed material.
is_ai_generatedboolean
AI-generated image.
confidencefloat
Confidence of the verdict.
signalsstring[]
Structural signals observed (e.g. moiré, pixel grid, monitor bezel).
reasonsstring[]
Human-readable justification of the verdict.

Behavior

  • Reflections and glare alone never classify an image as a screen photo: at least 2 structural artifacts are required.

No evidence of spoofing.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@selfie.jpg" \
  -F "checks=authenticity"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["authenticity"],
  "authenticity": {
    "is_authentic": true,
    "is_screen_photo": false,
    "is_printed_photo": false,
    "is_ai_generated": false,
    "confidence": 0.9,
    "signals": [],
    "reasons": ["Sem evidência de spoof."]
  },
  "message": "Verificação aprovada"
}
element_detection

Element detection

Checks whether expected elements are in the image: clothing, accessories, objects, text, logos, colors or people.

Send: checks=element_detection + selfie + detection_request

detection_requeststring (JSON)required
Up to 5 elements. type: clothing, accessory, object, text, logo, color_scheme or person. expected_count (1–10) is optional, useful for people. E.g.: {"elements":[{"type":"object","description":"crachá","required":true}]}.

Object element_detection

all_required_foundboolean
true when every required element was found.
detection_summarystring
Human-readable summary of the analysis.
detected_elementsobject[]
Result for each requested element. Schema below.
blocked_by_authenticityboolean
true when the list came back empty because the image failed the anti-spoofing gate, not because detection failed.

Each item of detected_elements

typestring
Type of the requested element.
descriptionstring
Description of the requested element.
foundboolean
Whether the element was found.
confidencefloat
Detection confidence.
detailsstring
Details about the detection or its absence.
quantity_foundinteger | null
Quantity found, when expected_count was sent.

Behavior

  • Without detection_request the API responds 422.
  • Detection goes through the anti-spoofing gate even if authenticity is not requested.

Required badge found.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@selfie.jpg" \
  -F 'detection_request={"elements":[{"type":"object","description":"crachá","required":true}]}' \
  -F "checks=element_detection"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["element_detection"],
  "element_detection": {
    "all_required_found": true,
    "detection_summary": "Crachá detectado na imagem.",
    "detected_elements": [{
      "type": "object",
      "description": "crachá",
      "found": true,
      "confidence": 0.92,
      "details": "Crachá visível no peito.",
      "quantity_found": null
    }],
    "blocked_by_authenticity": false
  },
  "message": "Verificação aprovada"
}
text_extraction

Text extraction

Reads specific text fields from the image, such as a code printed on a label, with optional format validation.

Send: checks=text_extraction + selfie + extraction_request

extraction_requeststring (JSON)required
Up to 5 fields. expected_format is an optional RE2 regex (full match; no backreferences or lookaround). E.g.: {"fields":[{"name":"codigo_bag","description":"código impresso abaixo do QR","expected_format":"^[A-Z]{3}[0-9]{5}$","required":true}]}.

Object text_extraction

all_required_foundboolean
true when every required field was read and validated.
extraction_summarystring
Human-readable summary of the extraction.
extracted_fieldsobject[]
Result for each requested field. Schema below.
blocked_by_authenticityboolean
true when the list came back empty because the image failed the anti-spoofing gate, not because reading failed.

Each item of extracted_fields

namestring
Name of the requested field.
valuestring | null
Text read. null when nothing was read.
foundboolean
Whether the value was read and, if there is a format, validated.
confidencefloat
Reading confidence.
matches_formatboolean | null
Whether the value matched the expected_format. null when there is no format.
detailsstring
Details about the reading or its absence.

Behavior

  • Without extraction_request, or with invalid JSON, the API responds 422.
  • Reading goes through the anti-spoofing gate even if authenticity is not requested.

Field read and validated by the format.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@bag.jpg" \
  -F 'extraction_request={"fields":[{"name":"codigo_bag","description":"código impresso abaixo do QR","expected_format":"^[A-Z]{3}[0-9]{5}$","required":true}]}' \
  -F "checks=text_extraction"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["text_extraction"],
  "text_extraction": {
    "all_required_found": true,
    "extraction_summary": "Código lido abaixo do QR.",
    "extracted_fields": [{
      "name": "codigo_bag",
      "value": "ABC12345",
      "found": true,
      "confidence": 0.95,
      "matches_format": true,
      "details": "Código impresso legível."
    }],
    "blocked_by_authenticity": false
  },
  "message": "Verificação aprovada"
}
qr_code

QR code reading

Reads the QR code in the photo. With extraction_request, it reads the printed code when the QR is missing, unreadable or empty.

Send: checks=qr_code + selfie (+ optional extraction_request)

Object qr_code

foundboolean
true when a value was read and, if there is an expected_format, it matched.
valuestring | null
Text read from the QR or by OCR. null when there is no valid reading.
source"qr" | "ocr" | null
Where the value came from.
status"read" | "ocr_fallback" | "not_found"
read: read from the QR. ocr_fallback: read by OCR because there was no readable QR. not_found: nothing valid was read.
confidencefloat | null
Reading confidence. 1.0 on the QR path — the decode is Reed-Solomon validated, so either the encoded value comes out or nothing does. On the fallback, the confidence of the field read by OCR. null when nothing was read.
matches_formatboolean | null
Whether the value matched the expected_format. null when there is no format. A QR that was read but does not match the format returns not_found with matches_format=false, without triggering OCR.
qr_countinteger
Number of QR codes with text detected in the photo. With more than one, the largest one wins.
detailsstring
Human-readable reason for the result (e.g. QR has no text, decoding timeout, DNG read directly by OCR). Free text: do not use it in logic.
blocked_by_authenticityboolean
true when the image failed the anti-spoofing gate (screen photo, printed photo or AI-generated). In that case found=false, value=null and status=not_found.

Behavior

  • approved requires a code read and an authentic image — the reading goes through the anti-spoofing gate even if authenticity is not requested.
  • Nothing read is 200 with approved=false and status=not_found, not 400.
  • DNG files go straight to OCR, so they are only read with extraction_request.

With several QRs, the largest one wins. Without extraction_request there is no fallback.

Request
curl -X POST https://api.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "selfie=@bag.jpg" \
  -F "checks=qr_code"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["qr_code"],
  "qr_code": {
    "found": true,
    "value": "ABC12345",
    "source": "qr",
    "status": "read",
    "confidence": 1.0,
    "matches_format": null,
    "qr_count": 2,
    "details": "QR code lido",
    "blocked_by_authenticity": false
  },
  "message": "Verificação aprovada"
}
document_extractiondev

Document extraction

Identifies whether the image is a document and, if it is a Brazilian RG or CNH, returns the extracted fields with per-field confidence.

Send: checks=document_extraction + document (no selfie)

documentfilerequired
JPEG, PNG or WebP image, or a 1-page PDF (e.g. a digital CNH exported from the Carteira Digital de Trânsito app), up to 10MB. One side per call — front or back; the digital CNH has both on the same page.

Object document_extraction

type"rg" | "cnh" | "not_a_document" | "unsupported_document" | "unreadable_document"
rg and cnh approve. not_a_document, unsupported_document (e.g. passport, vehicle registration, diploma) and unreadable_document reject: approved=false, fields=null and message explains why.
model"legacy" | "cin" | "physical" | "digital" | null
Document model: legacy or cin for RG, physical or digital for CNH. null when undetermined.
side"front" | "back" | "both" | null
Side sent: front, back or both. null when undetermined.
classification_confidencefloat
Confidence of the document type classification.
fieldsobject | null
Extracted fields, each as {value, confidence}. Every key of the type is always present; an unreadable field comes as {value: null, confidence: 0} — never an invented value, so you decide what goes to human review. null on rejection types.
messagestring | null
Rejection reason. null when the document is an RG or CNH.

Fields by type

rgtype = "rg"
numero_rg, orgao_expedidor, uf_expedidor, data_expedicao, nome, data_nascimento, filiacao_1, filiacao_2, naturalidade, cpf.
cnhtype = "cnh"
numero_registro, nome, data_nascimento, filiacao_1, filiacao_2, cpf, doc_identidade_numero, doc_identidade_orgao, doc_identidade_uf, categoria, data_primeira_habilitacao, data_emissao, validade, local_emissao, observacoes.
valuestring | null
Dates as YYYY-MM-DD. CPF as 000.000.000-00, validated by its check digit. Names without accents, in uppercase. Parentage in printed order, without telling father from mother.

Behavior

  • A rejection is also 200, with approved=false.
  • Combinable: liveness,face_match,document_extraction runs full KYC in one call — then selfie is required again.
  • No retention: the image and the extracted fields are neither stored nor logged.
  • Without document the API responds 422; a PDF with more than 1 page responds 400.

Back of an RG. An unreadable field comes with value null and confidence 0.Available only on the dev environment while in validation; not yet in production.

Request
curl -X POST https://dev.riv.ia.br/eye/v1/verify \
  -H "Authorization: Bearer $RIVIA_KEY" \
  -F "document=@rg_verso.jpg" \
  -F "checks=document_extraction"
Response200 OK
{
  "success": true,
  "approved": true,
  "checks": ["document_extraction"],
  "document_extraction": {
    "type": "rg",
    "model": "legacy",
    "side": "back",
    "classification_confidence": 0.96,
    "fields": {
      "numero_rg": { "value": "12.345.678-9", "confidence": 0.95 },
      "orgao_expedidor": { "value": "SSP", "confidence": 0.94 },
      "uf_expedidor": { "value": "SP", "confidence": 0.96 },
      "data_expedicao": { "value": "2015-03-12", "confidence": 0.93 },
      "nome": { "value": "MARIA DA SILVA", "confidence": 0.97 },
      "data_nascimento": { "value": "1990-07-04", "confidence": 0.96 },
      "filiacao_1": { "value": "JOSE DA SILVA", "confidence": 0.92 },
      "filiacao_2": { "value": null, "confidence": 0.0 },
      "naturalidade": { "value": "SAO PAULO-SP", "confidence": 0.9 },
      "cpf": { "value": "123.456.789-09", "confidence": 0.95 }
    },
    "message": null
  },
  "message": "Verificação aprovada"
}

Status codes

200OK
Verification processed. A business rejection is also 200 with approved=false.
400Bad Request
Invalid parameters, file in an unsupported format, larger than 10MB or corrupted (the image cannot be opened). For document_extraction, also a PDF with more than 1 page.
401Unauthorized
Bearer Token missing, invalid or expired.
422Validation Error
Syntactically valid but semantically incorrect payload: the file or JSON the check requires is missing, or the JSON is invalid.
500Internal Server Error
Internal failure. Try again; if it persists, contact support.
503Service Unavailabledev
AI provider temporarily unavailable for document_extraction. Try again shortly.

Behavior notes

  • checks is a list (CSV) of what you want to run. Only the requested checks come back populated; the rest return as null.
  • approved is the logical AND of the requested checks.
  • Checks run in parallel on the same image.
  • Accepted image formats: JPG, JPEG, PNG, GIF, BMP, WEBP, DNG (max. 10MB each).

Ready to integrate?

Talk to our team to get sandbox credentials and start your Riv.IA EYE integration.