無料
開発、テスト、そして最初のユーザー向け。
- 毎月500件のフルKYC認証
- 本人確認、生体認証、顔照合、デバイス&IP
- 200以上の不正検知シグナル、ブロックリスト、重複チェック
- Diditネットワーク全体でKYCを再利用可能
- ワークフロービルダー、ケース管理、SDK
- AIサポート コンソール内AIエージェント、ドキュメント、コミュニティ。
世界中の3,000以上の組織から信頼されています。
再利用可能なIDがもたらすもの
DiditのKYCはすべて、署名付きの再利用可能なKYC検証として、他のDiditを利用するアプリと共有できます。受け取り側のプラットフォームは無料で読み取り可能です。一度の検証で、Diditを受け入れるすべてのビジネスで利用できます。無料で始められます。
ID、生体認証、顔照合、制裁リスト、住所、年齢、電話番号、メールアドレス、カスタム質問など、必要なチェックを選択します。ダッシュボードでフローにドラッグ&ドロップするか、同じフローをAPIに投稿します。条件分岐やA/Bテストもコード不要で実行できます。
curl -X POST https://verification.didit.me/v3/session/$SESSION_ID/share/ \
-H "x-api-key: $DIDIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"for_application_id": "<receiving-application-id>",
"ttl_in_seconds": 3600
}'{ "share_token": "eyJ…" }curl -X POST https://verification.didit.me/v3/session/import-shared/ \
-H "x-api-key: $RECEIVING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"share_token": "<share-token>",
"trust_review": false,
"workflow_id": "<receiving-workflow-uuid>",
"vendor_data": "user-42"
}'{ "shared_from_session": "…" }# Didit Reusable KYC: verify a person once, share the finished session with another application
You are adding Reusable KYC to my_stack. A person completes one identity
verification; the finished session is then shared with a second Didit
application, which imports the full decision without running the checks
again. Every URL, header and enum value below is canonical. Do not paraphrase
or "improve" them.
## 0. What exists, and what does not
- Reuse is server to server, between two Didit applications: the source
application mints a share token for a finished session, the receiving
application redeems it. Both sides use their own x-api-key.
- There is NO reusable_identity object on the webhook or the decision, NO
metadata.request_fields or other selective-disclosure parameter, NO
workflow setting that "accepts" a credential, and NO revocation event. Do
not invent them. metadata on a session is free-form JSON that Didit echoes
back, nothing more. The receiving application gets the whole decision.
- This is not an EU Digital Identity (EUDI) Wallet integration. Didit does
not issue or accept EUDI Wallet credentials through these calls.
## 1. Provision
- Sign up: https://business.didit.me
- Source application: its API key. Receiving application: its API key and its
application id (a UUID: shown in the console, and sent as application_id
on every webhook that application receives). They are different
applications; sharing a session with the application that owns it is
refused.
## 2. Create the workflow for the first verification
POST https://verification.didit.me/v3/workflows/
-H "x-api-key: <source-api-key>"
-H "Content-Type: application/json"
{
"workflow_label": "KYC onboarding",
"features": [
{ "feature": "OCR" },
{ "feature": "LIVENESS" },
{ "feature": "FACE_MATCH" },
{ "feature": "IP_ANALYSIS" }
]
}
OCR is the ID Verification feature (uppercase, strict: ID_VERIFICATION is
rejected). Add { "feature": "AML" } for sanctions and PEP screening; it is
priced separately. Response: 201. The workflow id is uuid (workflow_id carries
the same value) and features comes back as one string, "OCR + LIVENESS + ...".
Create the workflow once and keep the id: every call makes a new one.
Prices: https://didit.me/pricing
## 3. Create a session
POST https://verification.didit.me/v3/session/
-H "x-api-key: <source-api-key>"
-H "Content-Type: application/json"
-d '{ "workflow_id": "<uuid from step 2>", "vendor_data": "<your user id>" }'
Response: 201 with session_id, session_token, url and status "Not Started".
Redirect the user to url (hosted flow) or open it in the Web, iOS, Android,
React Native or Flutter SDK. The field is named url on this response.
One unfinished session exists per workflow_id and vendor_data pair: calling
create again with the same pair answers 201 again with that same session.
Optional: callback, a URL the user's browser is sent back to when the flow
ends. It is a browser redirect, not a webhook: never trust its query string.
## 4. Webhook
Register a destination in the console (API & Webhooks), or over the API:
POST https://verification.didit.me/v3/webhook/destinations/
-H "x-api-key: <your-api-key>"
-H "Content-Type: application/json"
-d '{
"label": "Verification webhooks",
"url": "https://<your-public-host>/webhooks/didit",
"webhook_version": "v3",
"subscribed_events": ["status.updated", "data.updated"]
}'
label, url and subscribed_events are required. url must be a public HTTPS
address: Didit does not deliver to localhost or private addresses. Response:
201 with uuid and secret_shared_key. Store secret_shared_key as the webhook
secret (DIDIT_WEBHOOK_SECRET); it is unique to this destination. Remove a
destination with DELETE /v3/webhook/destinations/{uuid}/ (204).
What arrives:
- webhook_type is "status.updated" (the session changed status) or
"data.updated" (verification data was corrected after the fact)
- a destination receives the events of every session of the application,
so filter on workflow_id or vendor_data when several flows share it
- creating a session already sends status.updated with status
"Not Started". The decision key is present only when status is Approved,
Declined, In Review or Abandoned.
Verify every delivery:
Header: X-Signature-V2 (not X-Signature, not X-Signature-Simple)
Algorithm: HMAC-SHA256, hex digest, over the canonical JSON of the payload
(Python json.dumps(sort_keys=True, separators=(",", ":"),
ensure_ascii=False) after whole-valued floats become ints).
Never hash the raw request bytes under this header: that is
the older X-Signature.
Freshness: the signed body field timestamp is the dispatch time (Unix
seconds). Reject when abs(now - timestamp) > 300 seconds, and
reject when the X-Timestamp header does not equal it.
Idempotency: event_id is the same on every retry of one event, so store it
and skip a delivery you already processed. One session can
still send the same status under two event ids, and the
console's Try Webhook test deliveries carry no event_id, so
also make the handler safe to run twice for one
(session_id, status, webhook_type).
Compare: constant-time (crypto.timingSafeEqual)
Reference handler (Express). Keep the verification lines as written.
The handler is a fragment. Put this above it and app.listen(process.env.PORT)
below it. It needs Express and Node 21 or newer (an older Node rejects
every delivery). It expects a JSON body: answer 400 yourself if you accept
anything else on this route, and refuse to start without the secret.
const express = require("express");
const app = express();
const SECRET = process.env.DIDIT_WEBHOOK_SECRET; // secret_shared_key of the destination
// Your endpoint receives a signed 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 { session_id, status, webhook_type, vendor_data, decision } = body;
// decision is present on Approved, Declined, In Review and Abandoned.
res.sendStatus(200);
});
Status values (exact strings): Not Started, In Progress, Approved, Declined,
In Review, Resubmitted, Expired, Abandoned, Kyc Expired. Awaiting User only
appears on business verification sessions.
## 5. Read the decision
The same V3 decision reaches you two ways:
- webhook body: body.decision.id_verifications[]
- GET https://verification.didit.me/v3/session/{session_id}/decision/
-H "x-api-key: <your-api-key>"
This response IS the decision object. Read id_verifications at the top
level: there is no decision wrapper here.
Until the user finishes the ID step, status is "Not Started" or "In Progress"
and id_verifications is null, not an empty array. Guard for it.
The other feature results sit next to it as plural arrays: liveness_checks[],
face_matches[], ip_analyses[], aml_screenings[]. shared_from_session is null
on a session the person completed here.
## 6. Share the finished session (source application)
Only a session whose status is Approved, Declined or In Review can be shared.
POST https://verification.didit.me/v3/session/{session_id}/share/
-H "x-api-key: <source-api-key>"
-H "Content-Type: application/json"
-d '{ "for_application_id": "<receiving application id>", "ttl_in_seconds": 3600 }'
Response: 200 with share_token, for_application_id and session_kind ("user").
ttl_in_seconds: 60 to 86400, default 3600. Each call mints a new token; a
token cannot be revoked, it only expires. Send it to the receiving
application's backend, never to a browser.
## 7. Import it (receiving application)
POST https://verification.didit.me/v3/session/import-shared/
-H "x-api-key: <receiving-api-key>"
-H "Content-Type: application/json"
-d '{
"share_token": "<share_token from step 6>",
"trust_review": false,
"workflow_id": "<a workflow id of the receiving application>",
"vendor_data": "<user id on the receiving side>"
}'
share_token, trust_review and workflow_id are required. trust_review true
keeps the source status (Approved stays Approved); false puts the imported
session In Review so the receiving team decides. Response: 201 with the same
decision shape as step 5, a new session_id, and shared_from_session pointing
at the source session.
## 8. Errors to handle
The message is under detail for some errors and under the field name
(for_application_id, share_token) for others: read both.
- share, 400: "Cannot share a session with the same application."
- share, 400: "Target application does not exist."
- share, 400: "Only finished sessions ("Approved", "Declined", "In Review")
can be shared."
- import, 400 on share_token: "Invalid share token.", "Share token has
expired.", "This token is not valid for this application."
- import, 400: share_token, trust_review or workflow_id missing
- import, 403: "This session has already been shared with your
application." Mint a new token.
- import, 404: "Workflow does not exist for this application."
## 9. Hard rules
- base URL for v3 endpoints: verification.didit.me
- auth header: x-api-key, one key per application
- feature enum: OCR, LIVENESS, FACE_MATCH, IP_ANALYSIS, AML (uppercase)
- result path: decision.id_verifications[] in the webhook body and
id_verifications[] on the GET decision response
- webhook: X-Signature-V2 plus X-Timestamp, canonical JSON, freshness from
the signed body timestamp
## 10. Verify your integration
Sandbox (an application in sandbox mode: nothing is billed).
https://docs.didit.me/integration/sandbox-testing
- create the workflow, create a session, and read its decision: expect 201,
201 with url, and 200 with status "Not Started"
- to get a finished session without a person, call
POST https://verification.didit.me/v3/session/{session_id}/simulate/ with
your x-api-key and { "new_status": "Approved" }. It sets the status and
sends the webhook; the feature arrays stay null.
- share that session. With no second application yet, send a random UUID
as for_application_id and expect the 400 "Target application does not
exist.": that proves the call is wired. A full share and import needs
the second application's id and API key; do not fake that step.
- assert the webhook accepts a correctly signed payload and rejects a wrong
X-Signature-V2, a changed body, and a payload whose signed timestamp is
older than 300 seconds, even when X-Timestamp is refreshed
Docs:
- https://docs.didit.me/core-technology/reusable-kyc/overview
- https://docs.didit.me/sessions-api/create-session
- https://docs.didit.me/sessions-api/retrieve-session
- https://docs.didit.me/integration/webhooks
開発、テスト、そして最初のユーザー向け。
25以上のモジュールを公開価格で提供。自動ボリュームディスカウントあり。
大量利用や規制対象プログラム向け。
利用量の増加に応じて割引が自動適用されます。交渉や営業担当とのやり取りは不要です。
Diditは、本人確認と不正対策のためのインフラです。私たちが自社でプロダクトを開発していた時に「こんなプラットフォームがあれば」と願った、オープンで柔軟、そして開発者に優しいプラットフォームです。ブラックボックスのように統合するのではなく、お客様のスタックの真の構成要素として機能します。
単一のAPIで、個人の確認(KYC、顧客確認)、企業の確認(KYB、企業確認)、暗号資産ウォレットのスクリーニング(KYT、取引確認)、およびリアルタイムでの取引監視をカバーします。このスタックは、以下の特長を持つように構築されています。
基盤となるフットプリントは、48以上の言語に対応した14,000以上のドキュメントタイプ、1,000以上のデータソース、そしてすべてのセッションで200以上の不正シグナルを検出します。Diditのインフラは、すべてのセッションから動的に学習し、日々進化しています。
eIDAS 2.0, electronic IDentification, Authentication and trust Services の略, は、EUのID規則を2024年に更新したものです。主な変更点は「欧州デジタルIDウォレット」(EUDIウォレットと略されることが多い)です。これは、2026年末までにすべてのEU市民および居住者が利用できるようになるスマートフォンアプリです。
このウォレットには、信頼できる機関によって発行され、暗号学的証明を伴って依拠当事者に提示される検証可能なクレデンシャル(デジタル運転免許証、検証済みID証明、学術卒業証書など)が格納されます。オプションで選択的開示(生年月日全体を明かすことなく18歳以上であることを証明するなど)も可能です。
企業にとって、eIDAS 2.0は、ウォレットで提示されたクレデンシャルを受け入れることと、ユーザーが携帯するクレデンシャルを発行することの両方に関わります。
規則 (EU) 2024/1183(eIDAS 2)が定める内容は以下のとおりです。
その他の企業は、任意でウォレットを受け入れることができます。DiditのEUDIウォレット受け入れは近日中に開始されます。企業がEUDIウォレットを受け入れるために必要なことについては、こちらをご覧ください。
通常、エンドツーエンドで30秒以内に完了します。IDを手に取り、書類を撮影し、セルフィーを撮影すれば完了です。これは市場で最速です。従来のKYCプロバイダーでは、同じフローで90秒以上かかることが一般的です。
バックエンドでは、ユーザーがセルフィーを完了した瞬間からWebhookが発火するまで、Diditはp99で2秒未満で結果を返します。モバイルキャプチャは、低速な電話やネットワーク向けに最適化されています。プログレッシブ画像圧縮、遅延SDKロード、そしてユーザーがウェブから開始した場合のQRコードによるデスクトップから電話へのワンタップハンドオフなどが含まれます。
フェデレーテッドログインは、IDプロバイダーにアカウントが存在することを証明するもので、受け入れ側のビジネスはメールアドレスと名前を取得できます。これは規制要件を満たすレベルの本人確認ではありません。
再利用可能な本人確認には、以下の情報が含まれます。
検証側は、独自のKYCを実行するのと同等の規制上の安心感を得られます。しかも、コスト、手間、コンバージョン率の低下を伴いません。
すべてのセッションは7つの明確なステータスのいずれかに分類されるため、コードは常に何をすべきか把握できます。
Approved, すべてのチェックに合格しました。ユーザーを次のステップに進めます。Declined, 1つ以上のチェックに失敗しました。ユーザーは、フロー全体を再実行することなく、特定の失敗したステップ(例:セルフィーの再撮影)を再提出できます。In Review, コンプライアンスレビューのためにフラグが立てられました。コンソールでケースを開き、すべてのシグナルを確認し、承認または却下を決定します。In Progress, ユーザーはフローの途中にいます。Not Started, リンクは送信されましたが、ユーザーはまだ開いていません。長時間放置されている場合はリマインダーを送信します。Abandoned, ユーザーはリンクを開きましたが、時間内に完了しませんでした。再エンゲージするか、期限切れにします。Expired, セッションリンクの有効期限が切れました。新しいセッションを作成します。署名付きウェブフックはすべてのステータス変更時に発火するため、データベースは常に同期されます。AbandonedおよびDeclinedセッションは無料です。
本番データは、デフォルトで欧州連合内のAmazon Web Servicesで処理・保存されます。規制当局が要求する管轄区域については、エンタープライズ契約により代替リージョンをリクエストできます。
あらゆる場所で暗号化。すべてのデータベース、オブジェクトストレージ、バックアップにおいて、保存データはAES-256で暗号化されます。すべてのAPIコール、Webhook、Business Consoleセッションにおける転送データは、Transport Layer Security 1.3で保護されます。生体認証データは、個別のCustomer Master Keyで暗号化されます。
データ保持期間はお客様が管理できます。デフォルトの保持期間は無期限(無制限)ですが、アプリケーションごとに30日から10年の間で短縮設定が可能です。また、ダッシュボードまたはAPIからいつでも個々のセッションを削除できます。
認証: SOC 2 Type 1 & Type 2、ISO/IEC 27001:2022、iBeta Level 1 PAD、およびスペインのTesoro / SEPBLAC / CNMVによる、Diditのリモート本人確認が対面での本人確認よりも安全であるという公式認定。詳細レポートは/security-complianceでご覧いただけます。
Diditは、本人確認インフラにとって重要な規制当局の要件にデフォルトで準拠しています。
詳細なメモ、すべての証明書、すべての規制当局からの書簡は/security-complianceをご覧ください。
3つの統合パスがあります。お使いのスタックに合うものをお選びください。
同じダッシュボード、同じ請求、3つすべてで成功ごとの料金です。ステップバイステップガイドはdocs.didit.me/integration/integration-promptをご覧ください。
再利用可能な本人確認は、従来のKYCスタックに対するプライバシーのアップグレードです。
受け入れ側での処理の法的根拠は、GDPRにおける正当な利益です。ユーザーはサービスにアクセスするために明示的に資格情報を提示しています。Diditの標準データ処理契約(DPA)は、共同管理者関係をカバーしています。
3つのトリガータイプがあります。
受信プラットフォームは、ユーザーに更新を求める必要がなく、資格情報自体が鮮度信号を保持し、古い資格情報は提示時に拒否されます。
検証者(受け入れ側のプラットフォーム)は、元の検証者が取得したのと同じ提示ごとの証拠パックと、発行の連鎖を取得します。
Diditは、EU加盟国の政府から正式な証明書を取得している唯一のKYCプラットフォームです。スペイン財務省、スペイン銀行、SEPBLACは共同で、このサービスが対面での検証よりも安全であると証明しました。この証明は、再利用されたすべての提示に適用されます。