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
Generate token
Authenticate with client_id and client_secret to receive the Bearer Token.
- 2
Send image
POST to /eye/v1/verify with the image and the checks you want.
- 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.
/auth/v1/tokens/generateExchanges 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.
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"}'
{
"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.
/eye/v1/verifylivenessLivenessselfieface_matchFace matchingselfie+documentauthenticityAuthenticity / anti-spoofingselfieelement_detectionElement detectionselfie+detection_requesttext_extractionText extractionselfie+extraction_requestqr_codeQR code readingselfie(+ optionalextraction_request)document_extractiondevDocument extractiondocument(noselfie)
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 onlydocument_extractionis requested. documentfile- Document image. Required with
face_matchanddocument_extraction. Fordocument_extractionit 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 withqr_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.
nullfor 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.
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"
{
"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
livenessLiveness
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
authenticitycheck.
Behavior
- Requesting
livenesstogether withauthenticityuses a single inference: the anti-spoofing result is reused.
Real selfie of a live person.
curl -X POST https://api.riv.ia.br/eye/v1/verify \ -H "Authorization: Bearer $RIVIA_KEY" \ -F "selfie=@selfie.jpg" \ -F "checks=liveness"
{
"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_matchFace 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
documentthe API responds422.
Selfie and document of the same person.
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"
{
"success": true,
"approved": true,
"checks": ["face_match"],
"face_match": {
"same_person": true,
"confidence": 0.88,
"details": ["Estrutura facial compatível."]
},
"message": "Verificação aprovada"
}authenticityAuthenticity / 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.
curl -X POST https://api.riv.ia.br/eye/v1/verify \ -H "Authorization: Bearer $RIVIA_KEY" \ -F "selfie=@selfie.jpg" \ -F "checks=authenticity"
{
"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_detectionElement 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_schemeorperson.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_countwas sent.
Behavior
- Without
detection_requestthe API responds422. - Detection goes through the anti-spoofing gate even if
authenticityis not requested.
Required badge found.
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"
{
"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_extractionText 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_formatis 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.
nullwhen 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.nullwhen there is no format. detailsstring- Details about the reading or its absence.
Behavior
- Without
extraction_request, or with invalid JSON, the API responds422. - Reading goes through the anti-spoofing gate even if
authenticityis not requested.
Field read and validated by the format.
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"
{
"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_codeQR 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.
nullwhen 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.0on 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.nullwhen nothing was read. matches_formatboolean | null- Whether the value matched the
expected_format.nullwhen there is no format. A QR that was read but does not match the format returnsnot_foundwithmatches_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=nullandstatus=not_found.
Behavior
approvedrequires a code read and an authentic image — the reading goes through the anti-spoofing gate even ifauthenticityis not requested.- Nothing read is
200withapproved=falseandstatus=not_found, not400. - 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.
curl -X POST https://api.riv.ia.br/eye/v1/verify \ -H "Authorization: Bearer $RIVIA_KEY" \ -F "selfie=@bag.jpg" \ -F "checks=qr_code"
{
"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_extractiondevDocument 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"rgandcnhapprove.not_a_document,unsupported_document(e.g. passport, vehicle registration, diploma) andunreadable_documentreject:approved=false,fields=nullandmessageexplains why.model"legacy" | "cin" | "physical" | "digital" | null- Document model:
legacyorcinfor RG,physicalordigitalfor CNH.nullwhen undetermined. side"front" | "back" | "both" | null- Side sent:
front,backorboth.nullwhen 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.nullon rejection types. messagestring | null- Rejection reason.
nullwhen 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 as000.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, withapproved=false. - Combinable:
liveness,face_match,document_extractionruns full KYC in one call — thenselfieis required again. - No retention: the image and the extracted fields are neither stored nor logged.
- Without
documentthe API responds422; a PDF with more than 1 page responds400.
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.
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"
{
"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
checksis a list (CSV) of what you want to run. Only the requested checks come back populated; the rest return asnull.approvedis 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.