Skip to main content
POST
cURL

KYC and KYB support

A single endpoint creates both User Verification and Business Verification sessions. The workflow’s type determines which kind:
  • Pass a KYC workflow workflow_id → a User Verification (KYC) session is created. Response session_kind: "user".
  • Pass a KYB workflow workflow_id → a Business Verification (KYB) session is created. Response session_kind: "business".
Configure workflows in the console at Workflows → Create workflow. A workflow is either KYC or KYB at creation time; you cannot switch types afterward.

Examples

Response (201 Created):

E-signature documents

When the workflow has an E-signature step that takes documents from the session (signature_documents_source set to session or both), send them in sign.documents. Each document is the file in base64, so Didit never has to fetch it from a URL.
Without fields, the signer reads the document and agrees to it.

Place fields by anchor text

Instead of a position, a field can carry an anchor: a piece of text in the document that marks where the field goes. Anchors suit documents you generate for each session, for example from a Word template. For workflow documents, place fields in the console. When the text isn’t in the document, the call returns 400 with field_anchor_not_found and the field’s key. Didit doesn’t remove the anchor text from the PDF, so write it in the same color as the page background.

Field values

sign.field_values sets the value of every field whose prefill is api.<key>, on workflow documents and session documents alike. It’s an object keyed by those <key> names. Values are strings of 1 to 500 characters for text and data fields, and booleans for a checkbox.

Errors and repeated calls

A session takes up to 20 documents and 50 MB in total. The call returns 400 with a sign error when:
  • The workflow has no E-signature step.
  • You sent sign.documents and the step only takes workflow documents.
  • The step takes session documents only and you sent none, or it has no workflow documents and you sent none.
  • A document is too large, isn’t valid base64 or isn’t a supported file.
  • A Word file is refused with signature_document_not_supported, or can’t be converted and returns signature_document_conversion_failed, each with the reason listed in Supported files.
  • A field is invalid, its anchor isn’t found (field_anchor_not_found), or a field value is refused.
Calling create again for the same workflow_id and vendor_data returns the pending session. If that call sends sign, its documents, fields and field values must be identical to the first ones (same order, titles, file names, content, fields and values). Anything different returns 400 with {"sign": "signature_document_changed"}: use a different vendor_data, or wait until the pending session finishes.

Testing with sandbox scenarios

On a sandbox-mode application, pass sandbox_scenario to pre-load a deterministic outcome (approve, a specific decline, or an In Review scenario) without touching real providers or your balance:
sandbox_scenario is rejected with 400 on live applications. If you don’t know the outcome yet at create time, arm (or re-arm) it later via POST /v3/session/{session_id}/sandbox/arm/, as long as the session is still Not Started: the hosted flow’s pre-flow scenario picker uses this same endpoint. See Sandbox & Test Data for the full scenario catalog, the magic-value registry, and the arm endpoint’s request/response shape.

vendor_data binds the session to an entity

  • For KYC workflows: binds to a User entity. If the user doesn’t exist, it’s auto-created.
  • For KYB workflows: binds to a Business entity. Auto-created if new.
Learn more in vendor data linking. The hosted flow and native SDK 4.10.0 or later can show an explicit biometric consent step before the first face capture, depending on the application’s consent mode and where the person is. Two optional fields feed that decision:
  • user_state: where the person lives, when you know it (US-IL, IL or Illinois). In Auto mode, a covered state makes the flow ask.
  • biometric_consent: an object with given_at and text_version, for consent you already collected. The flow does not ask again.
Native SDKs below 4.10.0 cannot show the step: their sessions run as before and the decision reports the consent as not collected. See Biometric consent. Create Session generates a verification URL and session token. It does not by itself prove that you showed the user the disclosures or obtained the consent required for your specific use case. If you start a Didit session from your own application, website, SDK wrapper, or white-label flow, you should handle the legal layer in your own UX before the user starts capture.
  1. Tell the user that your company is requesting the verification and that Didit is the verification provider / processor powering the flow.
  2. Link to your privacy notice and any controller-side legal terms that apply to the journey.
  3. Link to Didit’s Verification Privacy Notice and End User Terms for Identity Verification.
  4. If your workflow includes document capture, selfie capture, liveness, or biometrics, collect explicit affirmative consent wherever the applicable law or your legal position requires it.
  5. If you use a custom UI, custom domain, or API-only journey, render these disclosures in your own product before sending the user into the Didit flow.
  6. Keep your own record of notice / consent text, timestamp, and related metadata if your legal team requires an audit trail.
Legal requirements vary by jurisdiction. White-label branding does not remove Didit’s role in the verification flow. Review your implementation with counsel before launch if you operate in jurisdictions with biometric-specific rules.

Authorizations

x-api-key
string
header
required

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

application/json
workflow_id
string<uuid>
required

Stable identifier of the workflow that defines which verification steps the session will run. Workflows are created and managed in the Workflows page of the Console. The workflow_id also implicitly selects whether the session is KYC or KYB.

Example:

"11111111-2222-3333-4444-555555555555"

vendor_data
string

A unique identifier for the user being verified, such as a UUID, email, or internal user ID. This field is used for: (1) User grouping — sessions with the same vendor_data are linked to the same user profile in the Users tab. (2) Cross-session duplicate detection — when checking for duplicated faces, documents, phone numbers, emails, IP addresses, or device fingerprints, sessions with the same vendor_data are treated as the same user and excluded from matches. Without vendor_data, every session is treated as a unique user and all potential duplicates are surfaced. We strongly recommend always providing a vendor_data to reduce noise in duplicate detection.

Example:

"user-123"

multi_account_segment
string

Optional duplicate-detection realm inside your organization. Duplicate face checks compare this session only with other sessions that carry the same segment, so separate account bases (personal and business accounts, or one per B2B sub-client) do not flag a person who legitimately holds one account in each. Omit it to use the single default realm, which is what every session uses today. Blocklist and allowlist matching ignore this field.

Maximum string length: 64
Example:

"business-accounts"

callback
string

URL to redirect the user to after verification completes. Didit automatically appends verificationSessionId and status (Approved, Declined, In Review) as query parameters. Custom URL schemes (e.g. myapp://) are supported for mobile callbacks. If omitted, the workflow's default callback_url is used. When the application has allowed domains (allowed_domains on PATCH /v3/webhook/), an http or https callback must be on one of those origins (same scheme, host and port), otherwise the request fails with 400 on callback: callback must be on one of the application's allowed domains: https://app.example.com. Custom URL schemes are always accepted.

Example:

"https://example.com/verification/callback"

callback_method
enum<string>
default:initiator

Determines which device should handle the redirect to the provided callback URL. Use initiator to redirect only the device that started the flow, completer for the device that finishes it, or both to allow either device to trigger the callback. If you ever notice the callback not triggering reliably, we recommend setting this value to both.

Available options:
initiator,
completer,
both
Example:

"both"

metadata
any

Arbitrary JSON stored with the session and echoed back in the response and webhooks. Any JSON value is accepted (object, string, number, array), but a JSON object of key/value pairs is recommended. Not shown to the end user. Use it to pass your own correlation ids, A/B variants, or business context.

Example:
tags
string[]

Tags added to the session when it is created, by name. Up to 10, each up to 50 characters. Names are case-insensitive and stored in upper case. A name that does not exist yet in the application is created as a new tag. The tags show on the session in the console, where you can filter by them.

Maximum array length: 10
Maximum string length: 50
Example:
language
enum<string>

Language code (ISO 639-1) for the verification process interface. Controls the language displayed to the end user during verification. If not provided, the browser's language will be automatically detected and used. Check all the supported languages here.

Available options:
en,
ar,
bg,
bn,
bs,
ca,
cnr,
cs,
da,
de,
el,
es,
et,
fa,
fi,
fr,
he,
hi,
hr,
hu,
hy,
id,
it,
ja,
ka,
kk,
ko,
ky,
lt,
lv,
mk,
mn,
ms,
nl,
no,
pl,
pt-BR,
pt,
ro,
ru,
sk,
sl,
so,
sq,
sr,
sv,
th,
tr,
uk,
uz,
vi,
zh-CN,
zh-TW,
zh
Example:

"en"

contact_details
object

User contact information that can be used for notifications, prefilling verification forms, and phone verification. This includes email address, preferred language for communications, and phone number.

Example:
expected_details
object

Expected user details used to cross-validate against the data extracted from the user's ID document, Proof of Address, and other verification steps. Mismatches are surfaced as warnings on the decision; some fields (e.g. id_country, expected_document_types) also alter the user-facing flow. For Business Verification (KYB) workflows only the business fields (company_name, registry_country, registration_number) are used — they pre-fill the company registry search for the user — and all person-level fields are ignored.

Example:
portrait_image
string<byte>

Base64-encoded portrait image of the end user's face (max 2MB; JPEG, PNG, WebP, or TIFF). Used as the reference image to match against the liveness capture when the workflow is a Biometric Authentication workflow with Face Match enabled, or any graph workflow where Face Match runs before ID Verification (OCR). Optional when the vendor_data user already has a stored face image: Didit reuses it automatically, resolved in priority order: approved liveness face, ePassport chip photo, ID document portrait, manually enrolled profile face (only faces from approved sessions or faces you enrolled yourself are reused). When provided, portrait_image always takes precedence over the stored face. If omitted and no stored face exists (or vendor_data is not sent), session creation fails with 400. Ignored for other workflow types.

Example:

"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="

sandbox_scenario
string

Sandbox-only scenario slug. Only accepted when the API key belongs to a sandbox-mode application; rejected on live applications. When set, the create flow expands the scenario's bundled magic values onto the session's input fields (contact_details, expected_details) so the mocked providers reproduce the scenario's outcome through the real workflow pipeline. See the scenario catalog at GET /v1/sandbox/scenarios/ or Sandbox & Test Data. You can also arm (or re-arm) a scenario after create via POST /v3/session/{sessionId}/sandbox/arm/, as long as the session is still Not Started. For non-doc lookup, use lookup_partial_match, lookup_no_match, or lookup_provider_error to test the configured attempt budget and fallback. For identity wallets, use wallet_cancelled, wallet_timeout, or wallet_provider_error; approve simulates a verified wallet. All catalog wallets can be tested in sandbox, including schemes unavailable on live applications. Simulated attempts are not charged.

Example:

"decline_document_expired"

sign
object

E-signature input for this session. documents are the documents to sign, for a workflow whose E-signature step takes documents from the session (signature_documents_source session or both): up to 20 documents and 50 MB in total. field_values fills the fields whose prefill is api.<key>, on workflow and session documents alike. Creating a session again for the same workflow_id and vendor_data returns the pending session; if that call sends sign, its documents, fields and values must be identical to the first ones, and anything different returns 400 with {"sign": "signature_document_changed"}. See E-signature documents.

user_state
string

Where the person lives, when you know it: an ISO 3166-2 subdivision code (US-IL, ES-M) or a US state code or name (IL, Illinois). A state whose law requires explicit biometric consent (Illinois, Texas, Colorado) makes the verification flow ask for it before capturing a face, with trigger declared_state. An unrecognized value returns 400. See Biometric consent.

Maximum string length: 16
Example:

"US-IL"

Explicit biometric consent you already collected from the person. It satisfies the requirement, so the verification flow does not ask again, and the decision records it as status: "given" with trigger customer_collected. An answer the person already gave in the flow is never replaced. See Biometric consent.

Response

Session created (or returned, if an unfinished session with the same vendor_data already exists on the workflow's latest published version). The response is the serialized session, including the hosted verification url to redirect the user to.

session_id
string<uuid>
required

Unique identifier of the verification session. Use this id when calling GET /v3/session/{sessionId}/decision/.

Example:

"11111111-2222-3333-4444-555555555555"

session_number
integer
required

Sequential, human-friendly number assigned to the session inside your application. Useful for support and dashboards.

Example:

43762

session_token
string
required

12-character URL-safe token that authorizes the end user to access the hosted verification flow at url. Valid until the session expires. Treat it as a secret — anyone holding it can open the verification flow for this session.

Example:

"3FaJ9wLqX2Mz"

url
string<uri>
required

Hosted verification URL to redirect the end user to. The URL embeds the session_token and, if configured, uses your white-label domain. When the request includes language, the URL contains a language path segment — https://verify.didit.me/{language}/session/{session_token} (e.g. /es/session/...); without language, the segment is omitted.

Example:

"https://verify.didit.me/session/3FaJ9wLqX2Mz"

vendor_data
string | null
required

Identifier you passed in the request to link the session to a user or business in your own system. Echoed back verbatim. Null when not provided.

Example:

"user-123"

metadata
any
required

Arbitrary JSON payload you stored with the session at creation time. Echoed back verbatim — whatever JSON value you sent (object, string, number, array) is returned as-is, though a JSON object is recommended. Not shown to the end user. Always present in responses; null when not provided at creation time.

Example:
status
enum<string>
required

Current status of the session. Newly created sessions return Not Started. If a non-finished session already existed for the same vendor_data on the workflow's latest published version, the returned status reflects that existing session.

Available options:
Not Started,
In Progress,
Approved,
Declined,
In Review,
Expired,
Abandoned,
Kyc Expired,
Resubmitted,
Awaiting User
Example:

"Not Started"

callback
string<uri> | null
required

Final redirect URL the user is sent to after completing the flow. Didit appends ?verificationSessionId={session_id}&status={status} to this URL. Falls back to the workflow's configured callback URL when not provided in the request; null when neither is set. When the application has allowed domains and this URL is a web URL outside them (for example a workflow default set before the list changed), the hosted flow does not redirect the user to it.

Example:

"https://example.com/verification/callback"

workflow_id
string<uuid>
required

Stable identifier of the workflow this session runs on. Always the workflow's stable group identifier — even if you referenced a specific workflow version UUID in the request (backward-compatible lookup), the response carries the stable workflow_id. Use it to correlate the session with the workflow you configured in the Console.

Example:

"11111111-2222-3333-4444-555555555555"

workflow_version
integer
required

Version number of the published workflow version the session was pinned to at creation. Didit resolves workflow_id to the workflow's latest published version when the session is created, and the session keeps running that version even if the workflow is republished later.

Example:

3

tags
string[]

The tags on the session, by name.

Example:
Last modified on November 1, 2026