curl -X POST 'https://verification.didit.me/v3/id-verification/' \
-H 'x-api-key: YOUR_API_KEY' \
-F 'front_image=@./id_front.jpg' \
-F 'back_image=@./id_back.jpg' \
-F 'perform_document_liveness=true' \
-F 'save_api_request=true' \
-F 'vendor_data=user-123'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.id_verification.verify(
front_image=open("front_image.jpg", "rb"),
back_image=open("back_image.jpg", "rb"),
perform_document_liveness=True,
save_api_request=True,
vendor_data="user-123",
metadata={"flow": "onboarding"},
)
print(result)import Didit, { fileFromPath } from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.idVerification.verify({
front_image: await fileFromPath("front_image.jpg"),
back_image: await fileFromPath("back_image.jpg"),
perform_document_liveness: true,
save_api_request: true,
vendor_data: "user-123",
metadata: { flow: "onboarding" },
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.IDVerification.Verify(ctx, didit.IDVerificationVerifyParams{
FrontImage: didit.FileFromPath("front_image.jpg"),
BackImage: didit.Some(didit.FileFromPath("back_image.jpg")),
PerformDocumentLiveness: didit.Bool(true),
SaveAPIRequest: didit.Bool(true),
VendorData: didit.String("user-123"),
Metadata: didit.Some(map[string]any{"flow": "onboarding"}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.nio.file.Path;
import java.util.Map;
import me.didit.Didit;
import me.didit.types.IdVerificationVerifyParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.idVerification().verify(IdVerificationVerifyParams.builder()
.frontImage(Path.of("front_image.jpg"))
.backImage(Path.of("back_image.jpg"))
.performDocumentLiveness(true)
.saveApiRequest(true)
.vendorData("user-123")
.metadata(Map.of("flow", "onboarding"))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
use Didit\File;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->idVerification->verify([
'front_image' => File::fromPath('front_image.jpg'),
'back_image' => File::fromPath('back_image.jpg'),
'perform_document_liveness' => true,
'save_api_request' => true,
'vendor_data' => 'user-123',
'metadata' => ['flow' => 'onboarding'],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.id_verification.verify(
front_image: Pathname("front_image.jpg"),
back_image: Pathname("back_image.jpg"),
perform_document_liveness: true,
save_api_request: true,
vendor_data: "user-123",
metadata: { flow: "onboarding" }
)
puts result.to_husing System;
using System.Collections.Generic;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.IdVerification.VerifyAsync(new IdVerificationVerifyParams
{
FrontImage = FileUpload.FromPath("front_image.jpg"),
BackImage = FileUpload.FromPath("back_image.jpg"),
PerformDocumentLiveness = true,
SaveApiRequest = true,
VendorData = "user-123",
Metadata = new Dictionary<string, object> { ["flow"] = "onboarding" },
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "11d219ed-d59c-4b1d-8d65-9b933f12d5b8",
"id_verification": {
"status": "Approved",
"document_type": "Identity Card",
"document_subtype": "ID_CARD_GENERIC",
"document_number": "CBX164224",
"personal_number": "20446581H",
"portrait_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-portrait_image-8c2f.jpg?signature=...",
"front_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-front_image-4a1e.jpg?signature=...",
"back_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-back_image-77b0.jpg?signature=...",
"front_image_camera_front": null,
"back_image_camera_front": null,
"front_image_camera_front_face_match_score": null,
"back_image_camera_front_face_match_score": null,
"front_image_quality_score": {
"focus_score": 78,
"brightness_score": 83.2,
"brightness_issue": "ok",
"is_document_fully_visible": true,
"resolution_score": 40.7,
"overall_score": 70.2
},
"back_image_quality_score": {
"focus_score": 100,
"brightness_score": 95.6,
"brightness_issue": "ok",
"is_document_fully_visible": true,
"resolution_score": 43,
"overall_score": 84.4
},
"date_of_birth": "1980-01-13",
"age": 46,
"expiration_date": "2032-03-31",
"date_of_issue": "2022-03-31",
"issuing_state": "ESP",
"issuing_state_name": "Spain",
"first_name": "Julio Francisco",
"last_name": "Fores Sena",
"full_name": "Julio Francisco Fores Sena",
"gender": "M",
"address": "Brda. Urb. El Cardonal 0052 52 Po3 B,Taco,San Cristobal De La Laguna,Santa Cruz De Tenerife",
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"place_of_birth": "Valencia, Valencia",
"marital_status": "UNKNOWN",
"nationality": "ESP",
"extra_fields": {
"first_surname": "Fores",
"second_surname": "Sena"
},
"mrz": {
"surname": "FORES SENA",
"name": "JULIO FRANCISCO",
"country": "ESP",
"nationality": "ESP",
"birth_date": "800113",
"expiry_date": "320331",
"sex": "M",
"document_type": "ID",
"document_number": "CBX164224",
"optional_data": "20446581H",
"optional_data_2": "",
"birth_date_hash": "9",
"expiry_date_hash": "8",
"document_number_hash": "3",
"final_hash": "3",
"personal_number": "20446581H",
"warnings": [],
"errors": [],
"mrz_type": "TD1",
"mrz_string": "IDESPCBX164224320446581H<<<<<<\n8001139M3203318ESP<<<<<<<<<<<3\nFORES<SENA<<JULIO<FRANCISCO<<<",
"mrz_key": "CBX164224380011393203318"
},
"parsed_address": {
"street_1": "Avenida el Cardonal 52",
"street_2": "b",
"city": "La Laguna",
"region": "Canarias",
"country": "ES",
"postal_code": "38108",
"address_type": "Avenida",
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"raw_results": {
"address_components": [
{
"long_name": "b",
"short_name": "b",
"types": [
"subpremise"
]
},
{
"long_name": "52",
"short_name": "52",
"types": [
"street_number"
]
},
{
"long_name": "Avenida el Cardonal",
"short_name": "Av. el Cardonal",
"types": [
"route"
]
},
{
"long_name": "La Laguna",
"short_name": "La Laguna",
"types": [
"locality",
"political"
]
},
{
"long_name": "Spain",
"short_name": "ES",
"types": [
"country",
"political"
]
},
{
"long_name": "38108",
"short_name": "38108",
"types": [
"postal_code"
]
}
],
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"geometry": {
"location": {
"lat": 28.4501161,
"lng": -16.3004407
},
"location_type": "ROOFTOP"
},
"types": [
"street_address",
"subpremise"
]
},
"document_location": {
"latitude": 28.4501161,
"longitude": -16.3004407
},
"label": "Spain Identity Card Address",
"is_verified": true,
"category": "Residential"
},
"warnings": [],
"barcodes": []
},
"vendor_data": "user-123",
"metadata": {
"flow": "onboarding"
},
"created_at": "2026-06-12T02:21:56.013573+00:00"
}ID Verification API: Document OCR & Authenticity
Verify identity documents across 220+ countries and 14,000+ types. Authenticity, OCR, MRZ, sub-2s inference, pay-per-use pricing, no contracts.
curl -X POST 'https://verification.didit.me/v3/id-verification/' \
-H 'x-api-key: YOUR_API_KEY' \
-F 'front_image=@./id_front.jpg' \
-F 'back_image=@./id_back.jpg' \
-F 'perform_document_liveness=true' \
-F 'save_api_request=true' \
-F 'vendor_data=user-123'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.id_verification.verify(
front_image=open("front_image.jpg", "rb"),
back_image=open("back_image.jpg", "rb"),
perform_document_liveness=True,
save_api_request=True,
vendor_data="user-123",
metadata={"flow": "onboarding"},
)
print(result)import Didit, { fileFromPath } from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.idVerification.verify({
front_image: await fileFromPath("front_image.jpg"),
back_image: await fileFromPath("back_image.jpg"),
perform_document_liveness: true,
save_api_request: true,
vendor_data: "user-123",
metadata: { flow: "onboarding" },
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.IDVerification.Verify(ctx, didit.IDVerificationVerifyParams{
FrontImage: didit.FileFromPath("front_image.jpg"),
BackImage: didit.Some(didit.FileFromPath("back_image.jpg")),
PerformDocumentLiveness: didit.Bool(true),
SaveAPIRequest: didit.Bool(true),
VendorData: didit.String("user-123"),
Metadata: didit.Some(map[string]any{"flow": "onboarding"}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.nio.file.Path;
import java.util.Map;
import me.didit.Didit;
import me.didit.types.IdVerificationVerifyParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.idVerification().verify(IdVerificationVerifyParams.builder()
.frontImage(Path.of("front_image.jpg"))
.backImage(Path.of("back_image.jpg"))
.performDocumentLiveness(true)
.saveApiRequest(true)
.vendorData("user-123")
.metadata(Map.of("flow", "onboarding"))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
use Didit\File;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->idVerification->verify([
'front_image' => File::fromPath('front_image.jpg'),
'back_image' => File::fromPath('back_image.jpg'),
'perform_document_liveness' => true,
'save_api_request' => true,
'vendor_data' => 'user-123',
'metadata' => ['flow' => 'onboarding'],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.id_verification.verify(
front_image: Pathname("front_image.jpg"),
back_image: Pathname("back_image.jpg"),
perform_document_liveness: true,
save_api_request: true,
vendor_data: "user-123",
metadata: { flow: "onboarding" }
)
puts result.to_husing System;
using System.Collections.Generic;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.IdVerification.VerifyAsync(new IdVerificationVerifyParams
{
FrontImage = FileUpload.FromPath("front_image.jpg"),
BackImage = FileUpload.FromPath("back_image.jpg"),
PerformDocumentLiveness = true,
SaveApiRequest = true,
VendorData = "user-123",
Metadata = new Dictionary<string, object> { ["flow"] = "onboarding" },
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"request_id": "11d219ed-d59c-4b1d-8d65-9b933f12d5b8",
"id_verification": {
"status": "Approved",
"document_type": "Identity Card",
"document_subtype": "ID_CARD_GENERIC",
"document_number": "CBX164224",
"personal_number": "20446581H",
"portrait_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-portrait_image-8c2f.jpg?signature=...",
"front_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-front_image-4a1e.jpg?signature=...",
"back_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-back_image-77b0.jpg?signature=...",
"front_image_camera_front": null,
"back_image_camera_front": null,
"front_image_camera_front_face_match_score": null,
"back_image_camera_front_face_match_score": null,
"front_image_quality_score": {
"focus_score": 78,
"brightness_score": 83.2,
"brightness_issue": "ok",
"is_document_fully_visible": true,
"resolution_score": 40.7,
"overall_score": 70.2
},
"back_image_quality_score": {
"focus_score": 100,
"brightness_score": 95.6,
"brightness_issue": "ok",
"is_document_fully_visible": true,
"resolution_score": 43,
"overall_score": 84.4
},
"date_of_birth": "1980-01-13",
"age": 46,
"expiration_date": "2032-03-31",
"date_of_issue": "2022-03-31",
"issuing_state": "ESP",
"issuing_state_name": "Spain",
"first_name": "Julio Francisco",
"last_name": "Fores Sena",
"full_name": "Julio Francisco Fores Sena",
"gender": "M",
"address": "Brda. Urb. El Cardonal 0052 52 Po3 B,Taco,San Cristobal De La Laguna,Santa Cruz De Tenerife",
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"place_of_birth": "Valencia, Valencia",
"marital_status": "UNKNOWN",
"nationality": "ESP",
"extra_fields": {
"first_surname": "Fores",
"second_surname": "Sena"
},
"mrz": {
"surname": "FORES SENA",
"name": "JULIO FRANCISCO",
"country": "ESP",
"nationality": "ESP",
"birth_date": "800113",
"expiry_date": "320331",
"sex": "M",
"document_type": "ID",
"document_number": "CBX164224",
"optional_data": "20446581H",
"optional_data_2": "",
"birth_date_hash": "9",
"expiry_date_hash": "8",
"document_number_hash": "3",
"final_hash": "3",
"personal_number": "20446581H",
"warnings": [],
"errors": [],
"mrz_type": "TD1",
"mrz_string": "IDESPCBX164224320446581H<<<<<<\n8001139M3203318ESP<<<<<<<<<<<3\nFORES<SENA<<JULIO<FRANCISCO<<<",
"mrz_key": "CBX164224380011393203318"
},
"parsed_address": {
"street_1": "Avenida el Cardonal 52",
"street_2": "b",
"city": "La Laguna",
"region": "Canarias",
"country": "ES",
"postal_code": "38108",
"address_type": "Avenida",
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"raw_results": {
"address_components": [
{
"long_name": "b",
"short_name": "b",
"types": [
"subpremise"
]
},
{
"long_name": "52",
"short_name": "52",
"types": [
"street_number"
]
},
{
"long_name": "Avenida el Cardonal",
"short_name": "Av. el Cardonal",
"types": [
"route"
]
},
{
"long_name": "La Laguna",
"short_name": "La Laguna",
"types": [
"locality",
"political"
]
},
{
"long_name": "Spain",
"short_name": "ES",
"types": [
"country",
"political"
]
},
{
"long_name": "38108",
"short_name": "38108",
"types": [
"postal_code"
]
}
],
"formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
"geometry": {
"location": {
"lat": 28.4501161,
"lng": -16.3004407
},
"location_type": "ROOFTOP"
},
"types": [
"street_address",
"subpremise"
]
},
"document_location": {
"latitude": 28.4501161,
"longitude": -16.3004407
},
"label": "Spain Identity Card Address",
"is_verified": true,
"category": "Residential"
},
"warnings": [],
"barcodes": []
},
"vendor_data": "user-123",
"metadata": {
"flow": "onboarding"
},
"created_at": "2026-06-12T02:21:56.013573+00:00"
}Authorizations
Your application's API key, from Developers -> API keys in the Business Console. The primary key has full access. A named key can be scoped: none, read or write per resource, limited to some workflows or to approved sessions, to a list of IP addresses, and to an expiry date. 401 means the key is wrong, revoked or expired; 403 means no key was sent, the key has no access to this resource or action, or the request came from an address outside its IP list; 404 on a session route means the session is outside the key's workflows or statuses. A key without media access receives image, video and PDF URLs as null, and a key without sessions write receives session links and tokens as null. See https://docs.didit.me/console/api-keys.
Body
Front side of the identity document. Allowed extensions: tiff, jpg, jpeg, png, webp, pdf. Maximum upload size: 10 MB (larger files are rejected with 400). PDFs are rendered to an image before OCR; encrypted PDFs require front_image_password. Images are automatically compressed to ~0.5 MB and EXIF orientation is applied. Upload the full, uncropped capture with all four corners of the document visible.
Back side of the document — send it whenever the document has one (many ID cards carry the MRZ or a barcode on the back; omitting it simply leaves the MRZ and back-side fields empty in the response — this endpoint does not raise a missing-MRZ warning). Same format and size limits as front_image. When omitted, back_image and back-side fields are null in the response.
Screens both images for presentation fraud: screen replays (SCREEN_CAPTURE_DETECTED), printed copies (PRINTED_COPY_DETECTED), and portrait manipulation (PORTRAIT_MANIPULATION_DETECTED). Any of these auto-declines. Enabled by default; set to false to skip fraud screening and reduce latency. When enabled and the provider returns no usable score, the response is In Review with a DOCUMENT_LIVENESS_NOT_COMPUTED warning rather than Approved.
true
Accepted and validated (1–120) but currently not applied by this endpoint — it never produces a MINIMUM_AGE_NOT_MET warning from this field. Enforce age rules from the returned date_of_birth/age, or use a verification-session workflow with age restrictions.
1 <= x <= 120What to do when no expiration date can be read (EXPIRATION_DATE_NOT_DETECTED). Default NO_ACTION because many identity documents print no expiry date. Note: a detected and past expiry date always declines (DOCUMENT_EXPIRED), regardless of this option.
NO_ACTION, DECLINE What to do when the extracted MRZ fails check-digit validation (MRZ_VALIDATION_FAILED). A missing MRZ raises no warning on this endpoint. Only relevant for documents that carry an MRZ. REVIEW is not supported on this endpoint and returns 400.
NO_ACTION, DECLINE What to do when extracted data is internally inconsistent: DATA_INCONSISTENT, MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME (visual zone disagrees with the MRZ), or DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS.
NO_ACTION, DECLINE What to do when a critical printed field was physically covered on the capture (CRITICAL_FIELD_OCCLUSION_DETECTED) - an identity number, a name, the date of birth, or the expiry date. Declines by default, like this endpoint's other document-integrity risks: covering the numbers a report is reconciled against is what a redacted document looks like. The value read for a covered field is dropped from the extraction either way, so the matching *_NOT_DETECTED warning usually accompanies it. REVIEW is not supported on this endpoint and returns 400.
NO_ACTION, DECLINE What to do when a number does not match the format its issuing authority uses for that document type (DOCUMENT_NUMBER_FORMAT_MISMATCH, PERSONAL_NUMBER_FORMAT_MISMATCH). Default NO_ACTION, which reports the warning without changing the status. REVIEW is not supported on this endpoint and returns 400.
NO_ACTION, DECLINE When true (default), persists the call as an API-type session — visible in the Business Console, retrievable via GET /v3/session/{sessionId}/decision/ using the returned request_id, announced through a status.updated webhook, and with document images returned as media URLs. When false, nothing is stored, request_id is a transient UUID, and images are returned inline as base64 JPEG strings.
true
Response
Document processed. id_verification.status is Approved, Declined, or In Review; every detected issue is itemized in id_verification.warnings. In Review is returned when a requested document-liveness check produced no usable result (DOCUMENT_LIVENESS_NOT_COMPUTED). A problematic document still returns 200 with status: "Declined" — inspect the body, not just the HTTP code. When save_api_request=true, request_id is the persisted session id and image fields are short-lived media URLs; with save_api_request=false they are inline base64 JPEG strings.