KTP capture
The KTP (Kartu Tanda Penduduk) is the Indonesian national ID card. Identity Platform supports capturing it via SDK or raw API. Two endpoints are involved:
- Flash challenge — single-use color sequence the SDK shows during capture.
- Capture submit — bundle of frames + OCR + sensor data.
Issue a flash challenge
/api/identity/document/ktp/challengeRequest
customerIdstringRequiredstepsinteger5ttlMsinteger90000Response
{
"flashChallenge": {
"id": "fc_01HXY...",
"purpose": "ktp",
"nonce": "9f8e7d6c-...",
"colors": ["#FF0000", "#00FF00", "#0000FF", "#FFFFFF", "#000000"],
"holdMs": [500, 300, 400, 500, 400],
"expiresAt": "2026-05-24T08:15:52.481Z",
"hmacSecret": "qe_whsec_..."
}
}The challenge can only be consumed once. The hmacSecret is used by web SDKs to sign the eventual capture bundle. Native SDKs derive their own signing key from a device-provisioned secret.
Submit a capture
/api/identity/document/ktp/captureBundle shape
Top-level
customerIdstringRequiredbundleobjectRequiredhmacKeystringbundle
versionstringRequiredsdkVersionstringRequiredplatformstringRequiredframesstring[]RequiredcapturedAtstring (ISO-8601)RequiredsignaturestringRequiredflashChallengeNoncestringflashSamplesobject[]ocrFieldsobjectocrConfidenceobjectResponse
{
"verification": {
"id": "ver_01HXY...",
"status": "verified | failed | pending",
"score": 87
},
"ktpBundle": {
"id": "ktpb_01HXY...",
"verdict": "passed | failed | requires_review",
"assuranceTier": "high | standard | low",
"framesStored": 1
},
"antiSpoof": {
"fused": "live | spoof | unclear",
"assuranceTier": "high | standard | low",
"voters": [{ "name": "flash_reflectance", "passed": true }, ...],
"passed": 4,
"present": 6
},
"tamper": {
"fused": "authentic | tampered | unclear",
"voters": [
{ "name": "nik_consistency", "passed": true, "reason": "checksum ok" },
{ "name": "font_match", "passed": true, "reason": "Arial Latin Indonesian" },
{ "name": "hologram_check", "passed": false, "reason": "no reflective specular ring" }
]
},
"faceEnrolment": {
"id": "fe_01HXY...",
"fallbackStub": false,
"modelId": "qe-face-v3"
}
}Assurance tiers
| Tier | Requires |
|---|---|
high | Flash challenge satisfied + anti-spoof and tamper both pass |
standard | Anti-spoof pass + tamper pass, no flash challenge (e.g. older SDK or web fallback) |
low | One or both fused checks failed — bundle is persisted for analyst review, not auto-trusted |
A high or standard outcome bumps the customer's KYC tier to standard. A low outcome creates the verification but does not move the tier; an analyst can override via the dashboard.
OCR fields we extract
The KTP has 16 standard fields. Identity Platform extracts and validates:
| Field | OCR | Validated against |
|---|---|---|
nik | Yes | Province · regency · gender · DOB consistency (NIK is structured) |
fullName | Yes | Latin or Arabic-Latin only |
placeOfBirth | Yes | — |
dateOfBirth | Yes | Cross-checked with nik |
gender | Yes | Cross-checked with nik |
bloodType | Yes | — |
address | Yes | — |
rtRw | Yes | Format NNN/NNN |
kelurahan | Yes | — |
kecamatan | Yes | — |
religion | Yes | Enum (Islam, Kristen, …) |
maritalStatus | Yes | Enum |
occupation | Yes | — |
nationality | Yes | Enum (WNI · WNA) |
validUntil | Yes | Date or "SEUMUR HIDUP" |
issueDate | Yes | — |
Failure modes
Common reasons a capture lands in `requires_review`
- Glare on the laminate hiding the hologram (
hologram_checkfails). - Photocopy or scan instead of original KTP (
specular_checkfails). - NIK checksum doesn't match birth date or gender (
nik_consistencyfails). - Screen-capture replay attack (
screen_moiredetects display-emitted moiré pattern). - Camera flash mode disabled — flash samples can't validate the challenge colors.
A requires_review bundle is fully persisted (frames sealed at rest, embeddings stored, voters recorded). Your analyst override from the dashboard creates a new verification row marked actorUserId = <analyst> and bumps the tier appropriately.