Didit Verification Management

v4.1.0

Full Didit identity verification platform management — account creation, API keys, sessions, workflows, questionnaires, users, billing, blocklist, and webhoo...

0· 312· 1 versions· 0 current· 0 all-time· Updated 11h ago· MIT-0
byDidit@rosasalberto

Install

openclaw skills install didit-verification-management

Didit Identity Verification Platform

The single skill for the entire Didit verification platform. Covers account creation, session management, workflow configuration, questionnaires, user management, billing, blocklist, and webhook configuration — 45+ endpoints across 9 categories.

For standalone verification APIs (ID scan, liveness, face match, AML, etc.), see the individual didit-* skills.

API Reference Links:


Getting Started — Zero to Verifying

Go from nothing to a live verification link in 4 API calls, no browser needed:

import requests

# 1. Register (any email, no business email required)
requests.post("https://apx.didit.me/auth/v2/programmatic/register/",
    json={"email": "you@gmail.com", "password": "MyStr0ng!Pass"})

# 2. Check email for 6-char OTP, then verify → get api_key
resp = requests.post("https://apx.didit.me/auth/v2/programmatic/verify-email/",
    json={"email": "you@gmail.com", "code": "A3K9F2"})
api_key = resp.json()["application"]["api_key"]
headers = {"x-api-key": api_key, "Content-Type": "application/json"}

# 3. Create a KYC workflow
wf = requests.post("https://verification.didit.me/v3/workflows/",
    headers=headers,
    json={"workflow_label": "My KYC", "workflow_type": "kyc",
          "is_liveness_enabled": True, "is_face_match_enabled": True}).json()

# 4. Create a session → send user to the URL
session = requests.post("https://verification.didit.me/v3/session/",
    headers=headers,
    json={"workflow_id": wf["uuid"], "vendor_data": "user-123"}).json()
print(f"Send user to: {session['url']}")

To add credits: GET /v3/billing/balance/ to check, POST /v3/billing/top-up/ with {"amount_in_dollars": 50} for a Stripe checkout link.


Authentication

Two auth schemes are used across the platform:

EndpointsAuthHeader
Register, Verify Email, LoginNone(unauthenticated)
List Organizations, Get CredentialsBearerAuthorization: Bearer <access_token>
Everything else (sessions, workflows, etc.)API Keyx-api-key: <api_key>

Get your api_key via programmatic registration (above) or from Didit Business Console → API & Webhooks.


Account Setup

Base URL: https://apx.didit.me/auth/v2

1. Register

POST /programmatic/register/
BodyTypeRequiredDescription
emailstringYesAny email address
passwordstringYesMin 8 chars, 1 upper, 1 lower, 1 digit, 1 special

Response (201): {"message": "Registration successful...", "email": "..."}

Rate limit: 5 per IP per hour.

2. Verify Email & Get Credentials

POST /programmatic/verify-email/
BodyTypeRequiredDescription
emailstringYesSame email from register
codestringYes6-character alphanumeric OTP from email

Response (200):

{
  "access_token": "eyJ...",
  "refresh_token": "eyJ...",
  "expires_in": 86400,
  "organization": {"uuid": "...", "name": "..."},
  "application": {"uuid": "...", "client_id": "...", "api_key": "YOUR_KEY_HERE"}
}

application.api_key is the x-api-key for all subsequent calls.

3. Login (Existing Accounts)

POST /programmatic/login/
BodyTypeRequiredDescription
emailstringYesAccount email
passwordstringYesAccount password

Response (200): {"access_token": "...", "refresh_token": "...", "expires_in": 86400}

Progressive lockout: 5 fails = 15min, 10 = 1hr, 20 = 24hr.

4. List Organizations

GET /organizations/me/

Auth: Authorization: Bearer <access_token>

Response (200): Array of {"uuid": "...", "name": "...", "contact_email": "..."}

5. Get Application Credentials

GET /organizations/me/{org_id}/applications/{app_id}/

Auth: Authorization: Bearer <access_token>

Response (200): {"uuid": "...", "client_id": "...", "api_key": "..."}


Workflows

Base URL: https://verification.didit.me/v3

Workflows define verification steps, thresholds, and accepted documents. Each has a UUID used as workflow_id when creating sessions.

Workflow Types:

TypePurposeTypical Features
kycFull identity verification (ID + selfie)ID Verification, Liveness, Face Match, AML, NFC
adaptive_age_verificationAge gating with ID fallback for borderline casesAge Estimation, Liveness, per-country age restrictions
biometric_authenticationRe-verify returning users (no document)Liveness, Face Match against stored portrait
address_verificationVerify proof of address documentsProof of Address, geocoding, name matching
questionnaire_verificationCustom form/questionnaire verificationQuestionnaire, optional ID/liveness add-ons
email_verificationEmail OTP verification as a workflowEmail send/check, breach/disposable detection
phone_verificationPhone OTP verification as a workflowPhone send/check, carrier/VoIP detection

Features (toggleable per workflow): ID Verification, Liveness, Face Match, NFC, AML, Phone, Email, Proof of Address, Database Validation, IP Analysis, Age Estimation, Questionnaire.

1. List Workflows

GET /v3/workflows/

Response (200): Array of workflow objects with uuid, workflow_label, workflow_type, is_default, features, total_price.

2. Create Workflow

POST /v3/workflows/
BodyTypeDefaultDescription
workflow_labelstringautoDisplay name
workflow_typestringkycWorkflow template type
is_defaultbooleanfalseSet as default
is_liveness_enabledbooleanfalseLiveness detection
face_liveness_methodstringpassive"passive", "active_3d", "flashing"
face_liveness_score_decline_thresholdinteger50Below this → auto-decline
is_face_match_enabledbooleanfalseSelfie-to-document match
face_match_score_decline_thresholdinteger50Below this → auto-decline
face_match_score_review_thresholdinteger70Below this → manual review
is_aml_enabledbooleanfalseAML/PEP/sanctions screening
aml_decline_thresholdinteger80Above this → auto-decline
is_phone_verification_enabledbooleanfalsePhone verification step
is_email_verification_enabledbooleanfalseEmail verification step
is_database_validation_enabledbooleanfalseGov database validation
is_ip_analysis_enabledbooleanfalseIP risk analysis
is_nfc_enabledbooleanfalseNFC chip reading (mobile only, ePassports)
is_age_restrictions_enabledbooleanfalseEnable per-country age restrictions (for adaptive_age_verification)
documents_allowedobject{}Restrict accepted countries/doc types (empty = accept all)
duplicated_user_actionstringno_actionno_action, review, decline (set after creation via update)
max_retry_attemptsinteger3Max retries per session
retry_window_daysinteger7Days within which retries are allowed

Response (201): Workflow object with uuid.

wf = requests.post("https://verification.didit.me/v3/workflows/",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"workflow_label": "KYC + AML", "workflow_type": "kyc",
          "is_liveness_enabled": True, "is_face_match_enabled": True,
          "is_aml_enabled": True}).json()

3. Get Workflow

GET /v3/workflows/{settings_uuid}/

4. Update Workflow

PATCH /v3/workflows/{settings_uuid}/

Partial update — only send fields to change.

5. Delete Workflow

DELETE /v3/workflows/{settings_uuid}/

Response: 204 No Content. Existing sessions are not affected.


Sessions

Base URL: https://verification.didit.me/v3

Sessions are the core unit of verification. Every verification starts by creating a session linked to a workflow.

Lifecycle: Create → User verifies at URL → Webhook/poll decision → Optionally update status

Statuses: Not Started, In Progress, In Review, Approved, Declined, Abandoned, Expired, Resubmitted

Rate limits: 300 req/min per method. Session creation: 600/min. Decision polling: 100/min.

1. Create Session

POST /v3/session/
BodyTypeRequiredDescription
workflow_iduuidYesWorkflow UUID
vendor_datastringNoYour user identifier
callbackurlNoRedirect URL (Didit appends verificationSessionId + status)
callback_methodstringNo"initiator", "completer", or "both"
metadataJSON stringNoCustom data stored with session
languagestringNoISO 639-1 UI language
contact_details.emailstringNoPre-fill email for email verification step
contact_details.phonestringNoPre-fill phone (E.164) for phone verification step
contact_details.send_notification_emailsbooleanNoSend status update emails to user
contact_details.email_langstringNoLanguage for email notifications (ISO 639-1)
expected_details.first_namestringNoTriggers mismatch warning if different (fuzzy match)
expected_details.last_namestringNoExpected last name (fuzzy match)
expected_details.date_of_birthstringNoYYYY-MM-DD
expected_details.genderstringNo"M", "F", or null
expected_details.nationalitystringNoISO 3166-1 alpha-3 country code
expected_details.id_countrystringNoISO alpha-3 for expected ID document country (overrides nationality)
expected_details.poa_countrystringNoISO alpha-3 for expected PoA document country
expected_details.addressstringNoExpected address (human-readable, for PoA matching)
expected_details.identification_numberstringNoExpected document/personal/tax number
expected_details.ip_addressstringNoExpected IP address (logs warning if different)
portrait_imagebase64NoReference portrait for Biometric Auth (max 1MB)

Response (201):

{
  "session_id": "...",
  "session_number": 1234,
  "session_token": "abcdef123456",
  "url": "https://verify.didit.me/session/abcdef123456",
  "status": "Not Started",
  "workflow_id": "..."
}

Send the user to url to complete verification.

2. Retrieve Session (Get Decision)

GET /v3/session/{sessionId}/decision/

Returns all verification results. Image URLs expire after 60 minutes.

Response (200): Full decision with status, features, id_verifications, liveness_checks, face_matches, aml_screenings, phone_verifications, email_verifications, poa_verifications, database_validations, ip_analyses, reviews.

3. List Sessions

GET /v3/sessions/
QueryTypeDefaultDescription
vendor_datastringFilter by your user identifier
statusstringFilter by status (e.g. Approved, Declined, In Review)
countrystringFilter by ISO 3166-1 alpha-3 country code
workflow_idstringFilter by workflow UUID
offsetinteger0Number of items to skip
limitinteger20Max items to return

Response (200): Paginated list with count, next, previous, results[].

4. Delete Session

DELETE /v3/session/{sessionId}/delete/

Response: 204 No Content. Permanently deletes all associated data.

5. Batch Delete Sessions

POST /v3/sessions/delete/
BodyTypeDescription
session_numbersarrayList of session numbers to delete
delete_allbooleanDelete all sessions (use with caution)

6. Update Session Status

PATCH /v3/session/{sessionId}/update-status/
BodyTypeRequiredDescription
new_statusstringYes"Approved", "Declined", or "Resubmitted"
commentstringNoReason for change
send_emailbooleanNoSend notification email
email_addressstringConditionalRequired when send_email is true
email_languagestringNoEmail language (default: "en")
nodes_to_resubmitarrayNoFor Resubmitted: [{"node_id": "feature_ocr", "feature": "OCR"}]

Resubmit requires session to be Declined, In Review, or Abandoned.

7. Generate PDF Report

GET /v3/session/{sessionId}/generate-pdf

Rate limit: 100 req/min.

8. Share Session

POST /v3/session/{sessionId}/share/

Generates a share_token for B2B KYC sharing. Only works for finished sessions.

9. Import Shared Session

POST /v3/session/import-shared/
BodyTypeRequiredDescription
share_tokenstringYesToken from sharing partner
trust_reviewbooleanYestrue: keep original status; false: set to "In Review"
workflow_idstringYesYour workflow ID
vendor_datastringNoYour user identifier

A session can only be imported once per partner application.

10. List Session Reviews

GET /v3/sessions/{session_id}/reviews/

Response (200): Array of review activity items:

[
  {
    "id": 1,
    "action": "status_change",
    "old_status": "In Review",
    "new_status": "Approved",
    "note": "Document verified manually",
    "created_at": "2025-06-01T15:00:00Z"
  }
]

11. Create Session Review

POST /v3/sessions/{session_id}/reviews/
BodyTypeRequiredDescription
new_statusstringYes"Approved", "Declined", or "In Review"
commentstringNoReview note

Response (201): The created review item.


Blocklist

Block entities from a session to auto-decline future matches.

1. Add to Blocklist

POST /v3/blocklist/add/
BodyTypeRequiredDescription
session_iduuidYesSession to blocklist items from
blocklist_facebooleanNoBlock biometric face template
blocklist_documentbooleanNoBlock document fingerprint
blocklist_phonebooleanNoBlock phone number
blocklist_emailbooleanNoBlock email address

Warning tags on match: FACE_IN_BLOCKLIST, ID_DOCUMENT_IN_BLOCKLIST, PHONE_NUMBER_IN_BLOCKLIST, EMAIL_IN_BLOCKLIST

2. Remove from Blocklist

POST /v3/blocklist/remove/

Same structure with unblock_face, unblock_document, unblock_phone, unblock_email.

3. List Blocklist

GET /v3/blocklist/
QueryTypeDescription
item_typestring"face", "document", "phone", "email". Omit for all.

Questionnaires

Custom forms attached to verification workflows. Support 7 element types: short_text, long_text, multiple_choice, checkbox, file_upload, date, number.

1. List Questionnaires

GET /v3/questionnaires/

2. Create Questionnaire

POST /v3/questionnaires/
BodyTypeRequiredDescription
titlestringYesDisplay title
descriptionstringNoDescription shown to users
default_languagestringNoDefault language code
languagesarrayNoSupported languages
form_elementsarrayYesQuestion objects

Form element:

FieldTypeRequiredDescription
element_typestringYesOne of the 7 types above
labelobjectYesTranslations: {"en": "Question?", "es": "¿Pregunta?"}
is_requiredbooleanNoMandatory answer
optionsarrayConditionalRequired for multiple_choice/checkbox
requests.post("https://verification.didit.me/v3/questionnaires/",
    headers=headers,
    json={
        "title": "Employment Details",
        "default_language": "en",
        "form_elements": [
            {"element_type": "short_text",
             "label": {"en": "Occupation?"}, "is_required": True},
            {"element_type": "multiple_choice",
             "label": {"en": "Employment status"},
             "options": [{"label": {"en": "Employed"}}, {"label": {"en": "Student"}}]},
        ]
    })

3. Get Questionnaire

GET /v3/questionnaires/{questionnaire_uuid}/

4. Update Questionnaire

PATCH /v3/questionnaires/{questionnaire_uuid}/

5. Delete Questionnaire

DELETE /v3/questionnaires/{questionnaire_uuid}/

Response: 204 No Content.


Users

Manage verified individuals identified by vendor_data.

1. List Users

GET /v3/users/
QueryTypeDescription
statusstringApproved, Declined, In Review, Pending
searchstringSearch by name or identifier
countrystringISO 3166-1 alpha-3
limitintegerResults per page (max 200)
offsetintegerPagination offset

Response (200): Paginated list with vendor_data, full_name, status, session_count, issuing_states, approved_emails, approved_phones.

2. Get User

GET /v3/users/{vendor_data}/

3. Update User

PATCH /v3/users/{vendor_data}/
BodyTypeDescription
display_namestringCustom display name
statusstringManual override: Approved, Declined, In Review
metadataobjectCustom JSON metadata

4. Batch Delete Users

POST /v3/users/delete/
BodyTypeDescription
vendor_data_listarrayList of vendor_data strings
delete_allbooleanDelete all users

Billing

1. Get Credit Balance

GET /v3/billing/balance/

Response (200):

{
  "balance": "142.5000",
  "auto_refill_enabled": true,
  "auto_refill_amount": "100.0000",
  "auto_refill_threshold": "10.0000"
}

2. Top Up Credits

POST /v3/billing/top-up/
BodyTypeRequiredDescription
amount_in_dollarsnumberYesMinimum $50
success_urlstringNoRedirect after payment
cancel_urlstringNoRedirect on cancel

Response (200):

{
  "checkout_session_id": "cs_live_...",
  "checkout_session_url": "https://checkout.stripe.com/..."
}

Present checkout_session_url to the user for payment.


Webhook Configuration

Set up webhooks programmatically — no console needed.

1. Get Webhook Configuration

GET /v3/webhook/

Response (200):

{
  "webhook_url": "https://myapp.com/webhooks/didit",
  "webhook_version": "v3",
  "secret_shared_key": "whsec_a1b2c3d4e5f6g7h8i9j0...",
  "capture_method": "both",
  "data_retention_months": null
}
FieldTypeDescription
webhook_urlstring/nullURL where notifications are sent (null if not configured)
webhook_versionstring"v1", "v2", or "v3" (v3 recommended)
secret_shared_keystringHMAC secret for verifying webhook signatures
capture_methodstring"mobile", "desktop", or "both"
data_retention_monthsinteger/nullMonths to retain session data (null = unlimited)

2. Update Webhook Configuration

PATCH /v3/webhook/
BodyTypeRequiredDescription
webhook_urlstring/nullNoURL for notifications (set null to disable)
webhook_versionstringNo"v1", "v2", or "v3"
rotate_secret_keybooleanNotrue to generate a new secret (old one immediately invalidated)
capture_methodstringNo"mobile", "desktop", or "both"
data_retention_monthsinteger/nullNo1–120 months, or null for unlimited

Example — set webhook URL:

requests.patch(
    "https://verification.didit.me/v3/webhook/",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"webhook_url": "https://myapp.com/webhooks/didit", "webhook_version": "v3"},
)

Example — rotate secret:

r = requests.patch(
    "https://verification.didit.me/v3/webhook/",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"rotate_secret_key": True},
)
new_secret = r.json()["secret_shared_key"]

Example — disable webhooks:

requests.patch(
    "https://verification.didit.me/v3/webhook/",
    headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
    json={"webhook_url": None},
)

Response (200): Same shape as GET — returns the updated configuration.


Webhook Events & Signatures

Didit sends POST requests to your webhook URL when session status changes. Retries up to 2 times with exponential backoff (1 min, 4 min).

Payload

{
  "session_id": "...",
  "status": "Approved",
  "webhook_type": "status.updated",
  "vendor_data": "user-123",
  "timestamp": 1627680000,
  "decision": { ... }
}

Event types: status.updated (status change), data.updated (KYC/POA data manually updated).

Signature Verification (V2 — recommended)

Two headers: X-Signature-V2 (HMAC-SHA256 hex) and X-Timestamp (Unix seconds).

import hashlib, hmac, time, json

def verify_webhook_v2(body_dict: dict, signature: str, timestamp: str, secret: str) -> bool:
    if abs(time.time() - int(timestamp)) > 300:
        return False
    def process_value(v):
        if isinstance(v, float) and v == int(v):
            return int(v)
        if isinstance(v, dict):
            return {k: process_value(val) for k, val in v.items()}
        if isinstance(v, list):
            return [process_value(i) for i in v]
        return v
    canonical = json.dumps(process_value(body_dict), sort_keys=True, ensure_ascii=False, separators=(",", ":"))
    message = f"{timestamp}:{canonical}"
    expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)

Simple Signature (Fallback)

Header: X-Signature-Simple — HMAC of key fields only.

def verify_webhook_simple(session_id, status, webhook_type, timestamp, signature, secret):
    message = f"{timestamp}:{session_id}:{status}:{webhook_type}"
    expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)

Error Responses (All Endpoints)

CodeMeaningAction
400Invalid requestCheck required fields and formats
401Invalid or missing API keyVerify x-api-key header
403Insufficient credits or no permissionCheck balance, API key permissions
404Resource not foundVerify IDs
429Rate limitedCheck Retry-After header, exponential backoff

Common Workflows

Full KYC Onboarding

1. POST /programmatic/register/      → register
2. POST /programmatic/verify-email/  → get api_key
3. POST /v3/workflows/               → create KYC workflow
4. PATCH /v3/webhook/                → set webhook_url + webhook_version "v3"
5. POST /v3/session/                 → create session → get URL
6. User completes verification at URL
7. Webhook fires → GET /v3/session/{id}/decision/ → read results

Programmatic Review + Blocklist

1. Webhook: status "In Review"
2. GET /v3/session/{id}/decision/   → inspect results
3. If fraud: PATCH update-status → Declined + POST /v3/blocklist/add/
   If legit: PATCH update-status → Approved

B2B KYC Sharing

Service A: POST /v3/session/{id}/share/       → get share_token
Service B: POST /v3/session/import-shared/    → import with trust_review=true

Check Balance Before Sessions

1. GET /v3/billing/balance/    → check if balance > 0
2. If low: POST /v3/billing/top-up/ → get Stripe checkout URL
3. POST /v3/session/           → create session

Questionnaire + Workflow

1. POST /v3/questionnaires/  → create form → save uuid
2. POST /v3/workflows/       → questionnaire_verification type
3. POST /v3/session/         → session with workflow_id

Utility Scripts

setup_account.py — Register and verify accounts

pip install requests
python scripts/setup_account.py register you@gmail.com 'MyStr0ng!Pass'
# (check email for code)
python scripts/setup_account.py verify you@gmail.com A3K9F2
# Prints api_key, org_uuid, app_uuid
python scripts/setup_account.py login you@gmail.com 'MyStr0ng!Pass'

manage_workflows.py — CRUD workflows

export DIDIT_API_KEY="your_key"
python scripts/manage_workflows.py list
python scripts/manage_workflows.py create --label "My KYC" --type kyc --liveness --face-match
python scripts/manage_workflows.py get <uuid>
python scripts/manage_workflows.py update <uuid> --enable-aml --aml-threshold 75
python scripts/manage_workflows.py delete <uuid>

create_session.py — Create verification sessions

export DIDIT_API_KEY="your_key"
python scripts/create_session.py --workflow-id <uuid> --vendor-data user-123
python scripts/create_session.py --workflow-id <uuid> --vendor-data user-123 --callback https://myapp.com/done

All scripts can be imported as libraries:

from scripts.setup_account import register, verify_email, login
from scripts.manage_workflows import list_workflows, create_workflow
from scripts.create_session import create_session

Version tags

latestvk97e5yxaw7pgyamjae22bq3yn1826y98

Runtime requirements

🛡️ Clawdis
Primary envDIDIT_API_KEY