무료
개발, 테스트 및 초기 사용자 확보에 적합합니다.
- 매월 500건의 전체 KYC 인증
- 신분증, 라이브니스, 얼굴 매칭, 기기 및 IP 확인
- 200개 이상의 사기 신호, 차단 목록, 중복 확인
- Didit 네트워크 전반에서 KYC 재사용 가능
- 워크플로우 빌더, 케이스 관리, SDK
- AI 지원 콘솔 내 AI 에이전트, 문서, 커뮤니티.
전 세계 2,000개 이상의 기관에서 신뢰합니다.

글로벌 커버리지
스페인 DNI, 일본 마이넘버, 영국 여권. 매월 새로운 문서가 추가됩니다., 건당 $0.15, 동일한 결과 형식, 2초 미만의 응답 속도.
방법 01
여권, 주민등록증, 운전면허증 또는 거주 허가증을 촬영합니다. 220개 이상의 국가, 14,000개 이상의 문서, 2초 이내 판정.
$0.15 · 월 500건 무료현재 위치
방법 02
사용자가 국가 ID 번호를 입력하면 Didit이 해당 번호를 발급한 정부 데이터베이스와 대조합니다. 등록 기관에 사진이 있는 경우 셀카와 등록 사진을 대조합니다.
36개국 · 국가별 요금방법 보기
방법 03
사용자가 정부 또는 은행 디지털 신분증(MitID, BankID, itsme, UAE PASS, gov.br, EUDI Wallet)으로 로그인하면, 지갑이 Didit이 검증하는 서명된 속성을 반환합니다.
22개 지갑 · 출시 예정방법 보기
신분증, 라이브니스, 얼굴 매칭, 제재 목록, 주소, 연령, 전화번호, 이메일, 맞춤 질문 등 원하는 확인 항목을 선택하세요. 대시보드에서 플로우로 드래그하거나, 동일한 플로우를 API에 게시하세요. 조건에 따라 분기하고 A/B 테스트를 실행할 수 있으며, 코드가 필요 없습니다.
이번 달 업데이트
국가
문서 유형
언어
스크립트
OCR · MRZ · 바코드
만료 자동 추적 · 상태 변경 시 웹훅
$ curl -X POST https://verification.didit.me/v3/session/ \
-H "x-api-key: $DIDIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_id": "wf_3daf4c64",
"vendor_data": "user-42"
}'{ "url": "verify.didit.me/..." }$ curl -X POST https://verification.didit.me/v3/id-verification/ \
-H "x-api-key: $DIDIT_API_KEY" \
-F "front_image=@front.jpg" \
-F "back_image=@back.jpg"{ "status": "승인됨", "document_type": "여권" }# Didit ID Verification — integrate in 5 minutes
You are integrating Didit's ID Verification module into <my_stack>. Follow
these steps exactly. Every URL, header, and enum value below is canonical —
do not paraphrase or "improve" them.
## 1. Provision an account
- Sign up: https://business.didit.me (no credit card required).
- Or provision programmatically: POST https://apx.didit.me/auth/v2/programmatic/register/
(returns an API key bound to the workspace + application).
## 2. Two integration paths — pick one
### Path A — Workflow Builder (hosted UI)
Best when you want Didit to handle camera, lighting cues, retries,
mobile handoff, and accessibility for you.
1. Create a workflow that contains the ID Verification feature:
POST https://verification.didit.me/v3/workflows/
Authorization header: x-api-key: <your-api-key>
Body: workflow_label, features array with the single entry
{ feature: "OCR" } (UPPERCASE — strict enum; "OCR" is the
workflows-endpoint value for ID Verification, and the only one
POST /v3/workflows/ accepts for it)
2. Create a verification session for an end user:
POST https://verification.didit.me/v3/session/
Body: workflow_id (from step 1), vendor_data (your own user id).
Response: url (the hosted verification link) — redirect the user to it.
3. Listen for webhook callbacks (see "Webhooks" below).
### Path B — Standalone server-to-server API
Best when you already have the document image (mobile SDK capture, native
onboarding app, reseller pipeline).
POST https://verification.didit.me/v3/id-verification/
Content-Type: multipart/form-data
Body fields:
- front_image (required, file)
- back_image (optional, file)
- vendor_data (optional string, your user id)
Response: JSON report with extracted fields, image quality scores,
warnings array, and the verdict.
## 3. Webhooks (Path A only — Path B returns synchronously)
- Register a webhook destination once via
POST https://verification.didit.me/v3/webhook/destinations/
Body: url, subscribed_events: ["session.verified", "session.review_started",
"session.declined", "kyc_expired"]
- Response includes secret_shared_key — store it.
- Every webhook delivery carries an X-Signature-V2 header you MUST verify
before trusting the payload. X-Signature-V2 signs the CANONICAL JSON, not
the raw body bytes — that is what makes it survive a proxy or body parser
that re-encodes the payload. (The legacy X-Signature header is the one
computed over the raw bytes.) The canonical form is the sender's Python
json.dumps(sort_keys=True, separators=(",", ":"), ensure_ascii=False)
after whole-valued floats become ints. Reproduce those bytes EXACTLY —
do NOT "parse, sort keys, JSON.stringify", which fails in four ways:
- Numbers come from the wire text, never from parsed doubles: the sender
signs 1000000000000000129 digit for digit, JSON.parse rounds it. Read
the body as TEXT (express.text, not express.json()) and re-emit
integers through BigInt(source). Register the webhook route ABOVE any
global app.use(express.json()): the first parser to run consumes the
stream, and the digits are gone before the route sees them.
- Floats use Python's repr: 1e-05, not 0.00001; 27.0 becomes 27.
- Keys sort by Unicode CODE POINT, as strings: "10" before "2", and
U+FF21 before U+1F642 (JavaScript's default .sort() reverses the latter
because it compares UTF-16 code units).
- Serialise straight from the sorted entries; never rebuild an object
first, because JavaScript moves integer-like keys to the front.
Then HMAC-SHA256 with secret_shared_key, hex-encode, and compare to the
X-Signature-V2 header in constant time (crypto.timingSafeEqual).
- Freshness comes from the SIGNED body field "timestamp" (Unix seconds):
reject a delivery whose body timestamp is more than 300 seconds from now,
and require the X-Timestamp header to equal it. Never check the header
alone — it is not signed, so a replayed delivery with a rewritten header
would pass. This is the Node 22 + Express handler, paste it verbatim:
// Your endpoint receives a signed ID Verification payload
const crypto = require("node:crypto"); // ESM: import crypto from "node:crypto"
// X-Signature-V2 = HMAC over the canonical JSON, never the raw bytes. Match the sender byte for
// byte: keys sorted by code point, integers digit for digit, floats in Python's repr.
class Num { constructor(src) { this.src = src; } } // a number as written on the wire, not a double
const num = (s) => { if (/^-?\d+$/.test(s)) return BigInt(s).toString(); const n = +s; // ints stay exact
if (Number.isInteger(n)) return BigInt(n).toString(); const [m, e] = n.toExponential().split("e"); // 27.0 -> 27
return +e >= -4 ? String(n) : `${m}e-${String(-e).padStart(2, "0")}`; }; // 1e-05, not 0.00001
const byCodePoint = (a, b) => Buffer.compare(Buffer.from(a), Buffer.from(b)); // UTF-8 order = Python's
const canon = (v) => Array.isArray(v) ? `[${v.map(canon)}]` : v instanceof Num ? num(v.src)
: v && typeof v === "object" ? `{${Object.keys(v).sort(byCodePoint).map((k) => `${JSON.stringify(k)}:${canon(v[k])}`)}}`
: JSON.stringify(v);
// Read the body as text: express.json() would round 1000000000000000129 to a double first.
// Register this route ABOVE any global app.use(express.json()): the first parser to run
// consumes the stream, and a body it already parsed has lost the digits the signature covers.
app.post("/webhooks/didit", express.text({ type: "application/json" }), (req, res) => {
const exact = JSON.parse(req.body, (k, v, c) => typeof v === "number" ? new Num(c.source) : v); // Node 21+
const body = JSON.parse(req.body);
const mac = crypto.createHmac("sha256", SECRET).update(canon(exact), "utf8").digest("hex");
const sig = Buffer.from(String(req.headers["x-signature-v2"] ?? ""));
// Freshness comes from the signed body timestamp; the header alone is unsigned and replayable.
const ts = body.timestamp, fresh = String(ts) === req.headers["x-timestamp"]
&& Math.abs(Date.now() / 1000 - ts) <= 300;
if (!fresh || sig.length !== mac.length
|| !crypto.timingSafeEqual(sig, Buffer.from(mac))) return res.sendStatus(401);
const { status, decision, vendor_data } = body;
// status: Approved | Declined | In Review | Expired | Not Finished
res.sendStatus(200);
});
## 4. Reading the report
Both paths return the same FIELDS, in a different container:
- Path A (session decision + webhook): decision.id_verifications is an ARRAY
— one entry per ID Verification node in the workflow. Select the entry you
want by its node_id, never by index and never as a singular object:
const doc = decision.id_verifications.find(
(v) => v.node_id === "id_verification_1"
);
Every other feature is plural the same way: liveness_checks,
face_matches, aml_screenings, ip_analyses, nfc_verifications,
poa_verifications, database_validations.
- Path B (POST /v3/id-verification/): a single id_verification object on the
response body — this endpoint runs exactly one document.
Each id_verification entry includes:
- status: "Approved" | "Declined" | "In Review" | "Expired" | "Not Finished"
- document_type: "Passport" | "Identity Card" | "Driver's License" | "Residence Permit" | null
- document_number, personal_number
- front_image, back_image, portrait_image (signed URLs, expire in 1 hour)
- date_of_birth (YYYY-MM-DD), age (number)
- expiration_date, date_of_issue (YYYY-MM-DD)
- issuing_state, issuing_state_name (ISO 3166-1 alpha-3 + display name)
- first_name, last_name, full_name
- gender ("M" | "F" | "U"), nationality (ISO 3166-1 alpha-3)
- address, formatted_address, parsed_address (street_1, street_2, city,
region, postal_code, geometry { lat, lng })
- front_image_quality_score / back_image_quality_score (focus, brightness,
resolution, overall, each 0-100)
- warnings: Array<{ risk, log_type, short_description, long_description }>
## 5. Hard rules — do not change
- Base URL for /v3/* endpoints is verification.didit.me (NOT apx.didit.me).
- Feature enum is UPPERCASE: OCR (ID Verification), LIVENESS, FACE_MATCH, AML, IP_ANALYSIS.
- Auth header is x-api-key (lowercase, hyphenated).
- Webhook signature header is X-Signature-V2 (NOT X-Signature), and it signs
the canonical JSON — never HMAC the raw bytes under that header, and never
canonicalise a parsed object: read the body as text and serialise the
sender's bytes (see the handler in section 3).
- Webhook freshness is the signed body "timestamp" (300 s window); the
X-Timestamp header must equal it and is never checked on its own.
- decision.id_verifications is an array; select the node you want by node_id.
- Always verify webhook signatures before trusting payload data.
- Status casing matches exactly: "Approved", "Declined", "In Review",
"Expired", "Not Finished" (title-cased, space-separated).
## 6. Pricing reference (public)
- Path A bundled in a full KYC workflow: $0.33 per session
- Path B standalone /v3/id-verification/ call: $0.15 per call
- 500 free verifications every month, forever, on every account.
## 7. Verify your integration
- Sandbox starts on signup at https://business.didit.me — no separate flag.
- Test docs: deterministic synthetic IDs returned in sandbox.
- Switch to live: flip the application's environment toggle in console.
When in doubt: https://docs.didit.me/core-technology/id-verification/overview
개발, 테스트 및 초기 사용자 확보에 적합합니다.
25개 이상의 모듈, 투명한 가격 정책. 자동 볼륨 할인.
대규모 볼륨 및 규제 프로그램에 적합합니다.
사용량이 증가하면 볼륨 할인이 자동으로 적용됩니다. 협상이나 영업팀과의 통화가 필요 없습니다.
Didit은 신원 및 사기 방지 인프라입니다. 저희가 직접 제품을 개발할 때 있었으면 하고 바랐던 플랫폼이죠. 개방적이고 유연하며 개발자 친화적이어서, 단순히 통합해야 하는 블랙박스가 아니라 스택의 실제 구성 요소처럼 작동합니다.
하나의 API로 개인 확인(KYC, 고객 알기), 기업 확인(KYB, 사업체 알기), 암호화폐 지갑 심사(KYT, 거래 알기), 실시간 거래 모니터링을 모두 처리하며, 다음과 같은 스택을 기반으로 구축되었습니다:
기반 기술: 48개 이상의 언어로 된 14,000개 이상의 문서 유형, 1,000개 이상의 데이터 소스, 모든 세션에서 200개 이상의 사기 신호를 분석합니다. Didit 인프라는 모든 세션에서 동적으로 학습하며 매일 발전합니다.
docs.didit.me/core-technology/id-verification/supported-documents-id-verification에서 찾아볼 수 있으며, 이 페이지의 검색 가능한 표에도 동일한 데이터가 제공됩니다.id_verification JSON 객체로 반환됩니다. 최상위 status는 Approved, Declined, In Review, Expired, 또는 Not Finished 중 하나입니다. 이 객체에는 document_type, document_number, full_name, first_name, last_name, date_of_birth (YYYY-MM-DD), age, expiration_date, date_of_issue, issuing_state (ISO 3166-1 alpha-3), nationality, gender, address, street, city, region, postal_code, geometry 좌표를 포함하는 구조화된 parsed_address, portrait_image / front_image / back_image / front_video / back_video에 대한 서명된 URL, 초점/밝기/해상도/전반적인 이미지 품질 점수(각 0-100), 그리고 warnings 배열이 포함됩니다. 전체 참조는 docs.didit.me/core-technology/id-verification/report-id-verification에서 확인하실 수 있습니다.전체 과정은 일반적으로 처음부터 끝까지 30초 이내에 완료됩니다., 신분증을 들고, 서류를 촬영하고, 셀카를 찍으면 끝입니다. 이는 시장에서 가장 빠른 속도입니다. 기존 KYC 제공업체는 동일한 과정에 보통 90초 이상이 소요됩니다.
백엔드에서 Didit은 사용자가 셀카 촬영을 마친 시점부터 웹훅이 실행되는 시점까지 측정했을 때, p99 기준으로 2초 이내에 결과를 반환합니다. 모바일 캡처는 느린 휴대폰과 느린 네트워크 환경에 최적화되어 있습니다. 점진적 이미지 압축, 지연 소프트웨어 개발 키트 로드, 그리고 사용자가 웹에서 시작하는 경우 QR 코드를 통해 데스크톱에서 휴대폰으로 원탭 핸드오프 기능을 제공합니다.
DOCUMENT_EXPIRED, MINIMUM_AGE_NOT_MET, DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION, ID_DOCUMENT_IN_BLOCKLIST, PORTRAIT_IMAGE_NOT_DETECTED와 같은 자동 거부 트리거는 위험 정책과 관계없이 항상 적용됩니다.모든 세션은 7가지 명확한 상태 중 하나로 분류되므로, 코드는 항상 무엇을 해야 할지 알 수 있습니다.
Approved, 모든 확인이 통과되었습니다. 사용자를 다음 단계로 진행하세요.Declined, 하나 이상의 확인이 실패했습니다. 전체 흐름을 다시 실행하지 않고도 사용자가 특정 실패 단계(예: 셀카 재촬영)를 재제출하도록 허용할 수 있습니다.In Review, 규정 준수 검토를 위해 플래그가 지정되었습니다. 콘솔에서 케이스를 열고 모든 신호를 확인한 후 승인 또는 거부를 결정하세요.In Progress, 사용자가 흐름 중간에 있습니다.Not Started, 링크가 전송되었지만, 사용자가 아직 열지 않았습니다. 너무 오래 방치되면 알림을 보내세요.Abandoned, 사용자가 링크를 열었지만, 제시간에 완료하지 못했습니다. 다시 참여를 유도하거나 만료시키세요.Expired, 세션 링크가 만료되었습니다. 새 세션을 생성하세요.모든 상태 변경 시 서명된 웹훅이 실행되므로, 데이터베이스는 항상 동기화 상태를 유지합니다. 중단되거나 거부된 세션은 무료입니다.
프로덕션 데이터는 기본적으로 유럽 연합의 Amazon Web Services에 처리 및 저장됩니다. 규제 당국이 요구하는 관할권의 경우, 엔터프라이즈 계약을 통해 대체 지역을 요청할 수 있습니다.
모든 곳에서 암호화됩니다. 모든 데이터베이스, 객체 스토어, 백업에서 AES-256 암호화가 적용됩니다. 모든 API 호출, 웹훅, 비즈니스 콘솔 세션에서 전송 계층 보안 1.3이 사용됩니다. 생체 인식 데이터는 별도의 고객 마스터 키로 암호화됩니다.
데이터 보존은 고객이 제어합니다. 기본 보존 기간은 무기한(무제한)이며, 애플리케이션별로 30일에서 10년 사이로 더 짧게 구성할 수 있습니다. 또한 대시보드 또는 API를 통해 언제든지 개별 세션을 삭제할 수 있습니다.
인증 내역: SOC 2 Type 1 & Type 2, ISO/IEC 27001:2022, iBeta Level 1 PAD, 그리고 Didit의 원격 신원 확인이 대면 확인보다 안전하다는 스페인 Tesoro / SEPBLAC / CNMV의 공식 인증이 있습니다. 전체 보고서는 /security-compliance에서 확인할 수 있습니다.
Didit은 신원 인프라에 중요한 규제 기관의 규정을 기본적으로 준수합니다.
자세한 메모, 모든 인증서, 모든 규제 기관 서신은 /security-compliance에서 확인하실 수 있습니다.
세 가지 통합 경로, 스택에 가장 적합한 것을 선택하세요.
동일한 대시보드, 동일한 청구, 세 가지 모두 동일한 성공당 지불 가격입니다. 단계별 가이드는 docs.didit.me/integration/integration-prompt에서 확인하실 수 있습니다.