| title | Age & Identity Verification | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sidebarTitle | Overview | ||||||||||||
| icon | id-card | ||||||||||||
| description | Production-grade age and identity verification with document intelligence, biometric matching, liveness detection, and multi-layer fraud prevention — covering 45 countries | ||||||||||||
| keywords |
|
Tuteliq Verification is a production-grade identity and age verification pipeline that combines document intelligence, biometric matching, liveness detection, and multi-layer fraud prevention in a single API call.
Most verification providers give you OCR and a face match. Tuteliq cross-references every data source on the document against every other — MRZ check digits, barcode data, OCR text, front vs. back, document vs. selfie — and flags any inconsistency as potential tampering. This catches forgeries that pass single-layer checks.
Confirm user age via document analysis, biometric estimation, or both. Full identity confirmation with document authentication, face matching, liveness detection, and fraud prevention. 45 countries with algorithmic validation. ICAO 9303 MRZ. PDF417 barcode decoding. 7 cross-referencing layers, recapture detection, LLM-powered authenticity analysis, and IP geolocation checks. Algorithmic check digit validation for CPF, personnummer, Aadhaar, Codice Fiscale, CURP, SSN, and 39 more. Not just format checks — full mathematical verification. MRZ vs. OCR. Barcode vs. OCR. Front vs. back. Document vs. selfie. Declared type vs. detected type. IP vs. document country. OCR confidence gating. Every inconsistency is flagged. Vision model analyzes document layout, security features, fonts, and color consistency against known templates. Detects screen photos, printout recaptures, and digital manipulation.| Feature | Minimum Tier | Credits |
|---|---|---|
| Age Verification (liveness only) | Pro ($99/mo) | 10 per verification |
| Age Verification (full) | Pro ($99/mo) | 20 per verification |
| Identity Verification | Business ($349/mo) | 25 per verification |
POST /v1/verification/age
Verify a user's age through document analysis, biometric age estimation, or both. Returns a verified age range, confidence score, and detailed document intelligence.
| Method | How it works | Best for |
|---|---|---|
| Document | Extracts DOB from government ID via OCR, MRZ parsing, and barcode decoding | High-assurance age gates, parental consent |
| Biometric | Estimates age from a selfie using vision AI (child/teen/adult classification) | Frictionless age checks, onboarding flows |
| Combined | Document + biometric with cross-reference — flags age inconsistencies > 10 years | Maximum assurance — verifies the document belongs to the person |
console.log(result.verified); // true console.log(result.estimated_age); // 15 console.log(result.age_range); // "13-15" console.log(result.is_minor); // true console.log(result.confidence); // 0.97 console.log(result.document_type); // "passport"
```python Python
result = client.verify_age(
document=open("id-front.jpg", "rb"),
document_back=open("id-back.jpg", "rb"), # Optional
selfie=open("selfie.jpg", "rb"),
method="combined",
)
print(result.verified) # True
print(result.estimated_age) # 15
print(result.age_range) # "13-15"
print(result.is_minor) # True
curl -X POST https://api.tuteliq.ai/v1/verification/age \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "document=@id-front.jpg" \
-F "document_back=@id-back.jpg" \
-F "selfie=@selfie.jpg" \
-F "method=combined"{
"verified": true,
"estimated_age": 15,
"age_range": "13-15",
"is_minor": true,
"confidence": 0.97,
"method": "combined",
"document_type": "passport",
"document_country": "GB",
"biometric_age": 15,
"document_age": 15,
"document": {
"ocr_confidence": 94,
"mrz_valid": true,
"document_number_valid": true,
"expired": false
},
"credits_used": 20
}| Field | Type | Description |
|---|---|---|
verified |
boolean | Whether age verification succeeded |
estimated_age |
integer | Best estimate of user's age |
age_range |
string | Tuteliq age bracket: under-10, 10-12, 13-15, or 14-17 |
is_minor |
boolean | Whether the user is under 18 |
confidence |
float | Confidence score (0.0-1.0) |
method |
string | Method used: document, biometric, or combined |
document_type |
string | Detected document type |
document_country |
string | ISO 3166-1 alpha-2 country code |
biometric_age |
integer | Age estimated from selfie (if provided) |
document_age |
integer | Age from document DOB (if provided) |
document.ocr_confidence |
integer | OCR confidence percentage (0-100) |
document.mrz_valid |
boolean | Whether MRZ check digits passed (if MRZ present) |
document.document_number_valid |
boolean | Whether document number passed algorithmic validation |
document.expired |
boolean | Whether the document has expired |
credits_used |
integer | Credits consumed |
DOB is extracted from multiple sources and cross-referenced. Priority order:
- MRZ (Machine Readable Zone) — Most reliable. ICAO 9303 check digit validated.
- PDF417 barcode — US/Canadian driver's licenses. AAMVA-encoded structured data.
- OCR labels — Text patterns like "Date of Birth:", "DOB:", date formats.
- Selfie estimation — Vision AI age bracket classification (fallback).
When multiple sources disagree, the verification flags the inconsistency.
| Document Type | Coverage |
|---|---|
| Passport | All ICAO-compliant passports worldwide (MRZ validated) |
| National ID card | All countries with MRZ-equipped cards + 45 countries with document number validation |
| Driver's license | All countries via OCR + US/Canada via PDF417 barcode |
| Residence permit | Via OCR text extraction |
Documents must be a clear, well-lit photo in JPEG or PNG format. Maximum file size: 10MB.
POST /v1/verification/identity
Full identity verification combining document authentication, face matching, liveness detection, and multi-layer fraud prevention.
Every identity verification runs all of these checks automatically:
| Check | What it does |
|---|---|
| Document number validation | Algorithmic check digit verification for 45 countries (CPF, personnummer, Aadhaar, CURP, SSN, etc.) |
| MRZ validation | ICAO 9303 check digits on document number, DOB, expiry, and composite — catches any MRZ tampering |
| Document authenticity | AI vision analysis of layout, security features, fonts, colors, and photo integration |
| Recapture detection | Detects photos of screens (moire patterns), printouts, and photo-of-photo attacks |
| Face matching | 128-dimensional descriptor comparison between document photo and live selfie |
| Liveness detection | Visual analysis: landmark motion, texture analysis, depth cues, cross-frame consistency |
| MRZ/OCR cross-referencing | Compares name, DOB, and document number between MRZ and printed text — flags mismatches as tampering |
| Front/back cross-referencing | Compares extracted data between document front and back sides |
| Barcode cross-referencing | Compares PDF417 barcode data against OCR and MRZ (US/CA licenses) |
| Document type consistency | Validates declared type matches MRZ-detected type (passport vs. ID card) |
| IP geolocation check | Flags geographic inconsistency between document country and request origin |
| OCR confidence gating | Flags low-confidence extractions as unreliable |
| Document expiry | Checks expiry from labels, MRZ, and barcodes |
| Age consistency | Cross-checks document age against selfie age estimate (flags > 10 year discrepancy) |
console.log(result.verified); // true console.log(result.match_score); // 0.98 console.log(result.liveness_passed); // true console.log(result.document_authenticated); // true console.log(result.checks.mrz_valid); // true console.log(result.checks.recapture); // "none"
```python Python
result = client.verify_identity(
document=open("id-front.jpg", "rb"),
document_back=open("id-back.jpg", "rb"),
selfie=open("selfie.jpg", "rb"),
document_type="passport",
)
print(result.verified) # True
print(result.match_score) # 0.98
print(result.checks.mrz_valid) # True
print(result.checks.recapture) # "none"
curl -X POST https://api.tuteliq.ai/v1/verification/identity \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "document=@id-front.jpg" \
-F "document_back=@id-back.jpg" \
-F "selfie=@selfie.jpg" \
-F "document_type=passport"{
"verified": true,
"match_score": 0.98,
"liveness_passed": true,
"document_authenticated": true,
"estimated_age": 34,
"is_minor": false,
"confidence": 0.99,
"document_type": "passport",
"document_country": "GB",
"flags": [],
"checks": {
"mrz_valid": true,
"mrz_fields": {
"document_number": "L898902C3",
"nationality": "GBR",
"date_of_birth": "1990-06-15",
"expiry_date": "2030-01-01",
"surname": "SMITH",
"given_names": "JOHN WILLIAM"
},
"document_number_valid": true,
"document_expired": false,
"face_match": true,
"liveness": true,
"recapture": "none",
"authenticity": {
"is_authentic": true,
"confidence": 0.94,
"security_features": ["hologram", "microprint", "guilloche_pattern"],
"anomalies": []
},
"barcode": {
"format": "PDF417",
"has_aamva": true,
"first_name": "JOHN",
"last_name": "SMITH",
"date_of_birth": "1990-06-15",
"state": "CA"
},
"cross_references_passed": true
},
"failure_reasons": [],
"credits_used": 25
}| Field | Type | Description |
|---|---|---|
verified |
boolean | All checks passed |
match_score |
float | Face match score (0.0-1.0) |
liveness_passed |
boolean | Liveness check passed |
document_authenticated |
boolean | Document passed authenticity checks |
estimated_age |
integer | Age from document DOB |
is_minor |
boolean | Under 18 |
confidence |
float | Overall confidence (0.0-1.0) |
document_type |
string | Detected document type |
document_country |
string | ISO 3166-1 alpha-2 country code |
flags |
array | Warning flags (e.g., low_ocr_confidence, geographic_inconsistency) |
checks |
object | Individual check results — see below |
failure_reasons |
array | Human-readable reasons for failure (empty if verified) |
credits_used |
integer | Credits consumed |
| Field | Type | Description |
|---|---|---|
mrz_valid |
boolean | null | MRZ check digits valid (null if no MRZ) |
mrz_fields |
object | null | Parsed MRZ fields with check-digit-validated data |
document_number_valid |
boolean | null | Document number passed algorithmic validation |
document_expired |
boolean | Document expiry check |
face_match |
boolean | Face on document matches selfie |
liveness |
boolean | Liveness detection passed |
recapture |
string | "none", "screen", "printout", or "photo_of_photo" |
authenticity |
object | AI-powered document authenticity analysis |
barcode |
object | null | PDF417/barcode data (US/CA licenses) |
cross_references_passed |
boolean | All cross-referencing checks passed |
| Status | Meaning | Examples |
|---|---|---|
verified |
All checks passed | Valid document, face match, liveness passed |
failed |
Hard failure — definitive fraud signal | Liveness failed, face mismatch, recapture detected |
needs_review |
Soft failure — human review recommended | Low OCR confidence, document expired, name mismatch between MRZ and printed text |
// 1. Verify age during sign-up
const verification = await tuteliq.verifyAge({
selfie: selfieStream,
method: 'biometric',
});
// 2. Store the verified age group
const ageGroup = verification.age_range; // "13-15"
// 3. Use verified age group in all safety calls
const safety = await tuteliq.detectUnsafe({
text: messageContent,
ageGroup: ageGroup, // Properly calibrated risk scoring
});// 1. Verify child's age
const childAge = await tuteliq.verifyAge({
selfie: childSelfieStream,
method: 'biometric',
});
if (childAge.estimated_age < 13) {
// 2. Verify parent's identity
const parent = await tuteliq.verifyIdentity({
document: parentIdStream,
selfie: parentSelfieStream,
});
if (parent.verified && !parent.is_minor) {
await grantParentalConsent(childUserId, parent);
}
}const identity = await tuteliq.verifyIdentity({
document: documentStream,
selfie: selfieStream,
});
if (identity.verified && !identity.is_minor) {
await grantModeratorRole(userId);
}Verification errors use the VRF_9xxx code range:
| Code | HTTP | Description |
|---|---|---|
VRF_9001 |
400 | Document image is required |
VRF_9002 |
400 | Document image is unreadable or too low quality |
VRF_9003 |
400 | Unsupported document type |
VRF_9004 |
400 | Selfie image is required for liveness check |
VRF_9005 |
400 | Face not detected in selfie |
VRF_9006 |
400 | Liveness check failed |
VRF_9007 |
500 | Verification service error (safe to retry) |
VRF_9008 |
403 | Age verification not available on your plan (requires Pro) |
VRF_9009 |
403 | Identity verification not available on your plan (requires Business) |
from tuteliq import TuteliqError
try:
result = client.verify_identity(
document=open("id.jpg", "rb"),
selfie=open("selfie.jpg", "rb"),
)
except TuteliqError as e:
if e.code == "VRF_9002":
pass # Ask user to retake photo
elif e.code == "VRF_9006":
pass # Liveness failed