> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Session

> Create a User Verification (KYC) or Business Verification (KYB) session and receive a hosted verification `url` plus a `session_token` to redirect or embed for your end user. The `workflow_id` selects which verification steps run and whether the session is KYC or KYB.

Call this from your backend (never the browser — the API key is a secret) whenever a user or business needs to be verified: at signup, before a sensitive action, or when re-verification is required. Prerequisites: a published workflow (created in the Console [Workflows](https://docs.didit.me/console/workflows) page) and an application API key with available credits. Sandbox applications bypass the credit check.

**Idempotency:** when `vendor_data` is provided and an unfinished session (`Not Started`, `In Progress`, `Resubmitted`, or `Awaiting User`) with the same `vendor_data` already exists on the workflow's **current latest published version** (and that session already has a hosted verification URL), the existing session is returned (still `201`) instead of creating a duplicate — with its `callback` and `metadata` updated to the newly provided values. An unfinished session created on an older version of the workflow (one that has since been republished) is **not** reused; a new session pinned to the latest published version is created instead. Sessions in `Approved`, `Declined`, `In Review`, `Expired`, `Abandoned`, or `Kyc Expired` are never reused.

**Side effects:** the hosted verification URL is generated and stored; if `contact_details.send_notification_emails` is `true` and an email is provided, a verification invite email is sent. The session expires after the workflow's configured session expiration time. For KYB workflows, `contact_details.phone` and `expected_details` are ignored.

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="Create Session Prompt"
  prompt={`Create a verification session via Didit's REST API.

Endpoint:
POST https://verification.didit.me/v3/session/

Authentication:
Use the x-api-key header with your Didit API key. The workflow's type (KYC or KYB) determines which kind of session is created — there is no separate endpoint for KYB.

Request body:
- workflow_id (string, UUID, required) — Stable workflow UUID from /v3/workflows/. KYC workflows create User Verification sessions; KYB workflows create Business Verification sessions.
- vendor_data (string, optional) — Your stable identifier for the entity (user or business). Strongly recommended: it binds the session to a User or Business entity and is used for duplicate detection across sessions.
- callback (string, URL, optional) — Where the hosted flow redirects the user when finished. Didit appends ?verificationSessionId={session_id}&status={status}. Falls back to the workflow default.
- callback_method (string, optional, default "initiator") — One of "initiator", "completer", "both". Use "both" if the callback sometimes fails to fire.
- metadata (object, optional) — Arbitrary JSON echoed back in responses and webhooks. Not shown to the user.
- language (string, optional) — ISO 639-1 code for the verification UI (en, es, fr, de, pt, etc.). Defaults to browser detection.
- contact_details (object, optional) — { email, send_notification_emails, email_lang, phone }. Pre-fills phone/email steps and (optionally) sends notification emails.
- expected_details (object, optional) — Expected first_name, last_name, date_of_birth, nationality (ISO-3), id_country (ISO-3), expected_document_types (e.g. ["P","ID","DL","RP","SSC"]). Mismatches surface as decision warnings.

Idempotency:
Within one application, only one non-finished session can exist per (workflow_id, vendor_data). Calling create again with the same pair returns the existing session (and updates its callback + metadata) instead of creating a duplicate.

curl example:
curl -X POST https://verification.didit.me/v3/session/ \\
-H "x-api-key: <API_KEY>" \\
-H "Content-Type: application/json" \\
-d '{
"workflow_id": "<WORKFLOW_ID>",
"vendor_data": "user-42",
"callback": "https://yourapp.com/callback",
"metadata": { "plan": "premium" }
}'

Response (201 Created) — keys you must handle:
- session_id (UUID) — use this on every subsequent /v3/session/{session_id}/... call.
- session_kind ("user" | "business") — discriminator, dispatch on this.
- session_number (int) — human-friendly per-application sequence.
- session_token (string) — short-lived, embedded in the hosted URL. Treat as secret.
- url (string) — hosted verification URL. Redirect the end user here. (Field name is "url", NOT "verification_url".)
- status (string) — newly created sessions return "Not Started".
- workflow_id, workflow_version, vendor_data, metadata, callback — echoed back.

See /reference/data-models for the full schema.

Failure modes:
- 400 — { "workflow_id": ["Invalid workflow_id."] } when the UUID is unknown.
- 400 — { "vendor_data": ["This user or business has been blocked and cannot create new verification sessions."] } when blocklisted.
- 400 — { "detail": "You don't have enough credits to perform this request. Please top up at https://business.didit.me" } when out of credits.
- 401 — invalid or missing API key.
- 403 — { "detail": "You do not have permission to perform this action." } when the key lacks write:sessions.
- 429 — rate limited (600 successful calls/minute per token/IP). Honour Retry-After.

Disclosure & consent:
This endpoint generates a URL — it does not record that you obtained user consent. Show your privacy notice and any biometric-specific consent in your own UX before redirecting to the hosted flow.

Next step:
After the user finishes (status changes to Approved / Declined / In Review), call GET https://verification.didit.me/v3/session/{session_id}/decision/ for the full decision — see /sessions-api/retrieve-session.

For end-to-end Didit integration, paste in the full prompt at /integration/integration-prompt.`}
/>

## 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

<Tabs>
  <Tab title="User Verification (KYC)">
    ```bash theme={null}
    curl -X POST https://verification.didit.me/v3/session/ \
      -H "x-api-key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "workflow_id": "YOUR_KYC_WORKFLOW_ID",
        "vendor_data": "user-42",
        "callback": "https://yourapp.com/callback"
      }'
    ```

    **Response (201 Created):**

    ```json theme={null}
    {
      "session_id": "4c5c7f3a-1f82-4f3b-8d8e-1a8d2d2f9b7a",
      "session_kind": "user",
      "session_number": 1024,
      "session_token": "...",
      "url": "https://verify.didit.me/session/...",
      "status": "Not Started",
      "workflow_id": "YOUR_KYC_WORKFLOW_ID",
      "vendor_data": "user-42"
    }
    ```
  </Tab>

  <Tab title="Business Verification (KYB)">
    ```bash theme={null}
    curl -X POST https://verification.didit.me/v3/session/ \
      -H "x-api-key: YOUR_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "workflow_id": "YOUR_KYB_WORKFLOW_ID",
        "vendor_data": "biz-acme-001",
        "callback": "https://yourapp.com/callback"
      }'
    ```

    **Response (201 Created):**

    ```json theme={null}
    {
      "session_id": "bs-01HJX1...",
      "session_kind": "business",
      "session_number": 89,
      "session_token": "...",
      "url": "https://verify.didit.me/session/...",
      "status": "Not Started",
      "workflow_id": "YOUR_KYB_WORKFLOW_ID",
      "vendor_data": "biz-acme-001"
    }
    ```
  </Tab>
</Tabs>

## `vendor_data` binds the session to an entity

* For KYC workflows: binds to a [User entity](/entities/users/overview). If the user doesn't exist, it's auto-created.
* For KYB workflows: binds to a [Business entity](/entities/businesses/overview). Auto-created if new.

Learn more in [vendor data linking](/entities/vendor-data-linking).

## Important: disclosure and consent still happen in your product

`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.

## Recommended pre-verification checklist

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](https://didit.me/terms/verification-privacy-notice) and [End User Terms for Identity Verification](https://didit.me/terms/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.

<Warning>
  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.
</Warning>


## OpenAPI

````yaml POST /v3/session/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/session/:
    post:
      tags:
        - Sessions
      summary: Create a verification session
      description: >-
        Create a User Verification (KYC) or Business Verification (KYB) session
        and receive a hosted verification `url` plus a `session_token` to
        redirect or embed for your end user. The `workflow_id` selects which
        verification steps run and whether the session is KYC or KYB.


        Call this from your backend (never the browser — the API key is a
        secret) whenever a user or business needs to be verified: at signup,
        before a sensitive action, or when re-verification is required.
        Prerequisites: a published workflow (created in the Console
        [Workflows](https://docs.didit.me/console/workflows) page) and an
        application API key with available credits. Sandbox applications bypass
        the credit check.


        **Idempotency:** when `vendor_data` is provided and an unfinished
        session (`Not Started`, `In Progress`, `Resubmitted`, or `Awaiting
        User`) with the same `vendor_data` already exists on the workflow's
        **current latest published version** (and that session already has a
        hosted verification URL), the existing session is returned (still `201`)
        instead of creating a duplicate — with its `callback` and `metadata`
        updated to the newly provided values. An unfinished session created on
        an older version of the workflow (one that has since been republished)
        is **not** reused; a new session pinned to the latest published version
        is created instead. Sessions in `Approved`, `Declined`, `In Review`,
        `Expired`, `Abandoned`, or `Kyc Expired` are never reused.


        **Side effects:** the hosted verification URL is generated and stored;
        if `contact_details.send_notification_emails` is `true` and an email is
        provided, a verification invite email is sent. The session expires after
        the workflow's configured session expiration time. For KYB workflows,
        `contact_details.phone` and `expected_details` are ignored.
      operationId: post_v3_session_create
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - workflow_id
              properties:
                workflow_id:
                  type: string
                  format: uuid
                  description: >-
                    Stable identifier of the workflow that defines which
                    verification steps the session will run. Workflows are
                    created and managed in the [Workflows](/console/workflows)
                    page of the Console. The `workflow_id` also implicitly
                    selects whether the session is KYC or KYB.
                  example: 11111111-2222-3333-4444-555555555555
                  x-readme-id: '0.0'
                vendor_data:
                  type: string
                  description: >-
                    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
                  x-readme-id: '0.1'
                callback:
                  type: string
                  description: >-
                    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.
                  example: https://example.com/verification/callback
                  x-readme-id: '0.2'
                callback_method:
                  type: string
                  description: >-
                    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`.
                  enum:
                    - initiator
                    - completer
                    - both
                  default: initiator
                  example: both
                  x-readme-id: '0.3'
                metadata:
                  description: >-
                    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:
                    user_type: premium
                    account_id: ABC123
                  x-readme-id: '0.4'
                language:
                  type: string
                  description: >-
                    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](/integration/supported-languages).
                  enum:
                    - 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
                  x-readme-id: '0.5'
                contact_details:
                  type: object
                  description: >-
                    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:
                    email: john.doe@example.com
                    send_notification_emails: true
                    email_lang: en
                    phone: '+14155552671'
                  properties:
                    email:
                      type: string
                      format: email
                      description: >-
                        Email address of the user (e.g., "john.doe@example.com")
                        that will be used during the [Email
                        Verification](/core-technology/email-verification/overview)
                        step. If not provided, the user must provide it during
                        the verification flow.
                      example: john.doe@example.com
                      x-readme-id: '1.0'
                    send_notification_emails:
                      type: boolean
                      default: false
                      description: >-
                        If true and an email is provided, Didit sends the
                        initial "Verify your identity" email asynchronously when
                        the User Verification (KYC) or Business Verification
                        (KYB) session is created. Didit also sends verification
                        status notifications for sessions requiring manual
                        review to the provided email address (e.g., from 'In
                        Review' to 'Approved' or 'Declined'). This helps users
                        return to your application once their verification is
                        complete. If you have white-label activated for the
                        session, the email sent will be white-labeled.
                      example: true
                      x-readme-id: '1.1'
                    email_lang:
                      type: string
                      description: >-
                        Language code (ISO 639-1) for email notifications.
                        Controls the language of all email communications (e.g.,
                        "en", "es", "fr"). There is no stored default — when
                        omitted, the verification invite email simply falls back
                        to English (`en`) at send time. The enum below is a
                        snapshot; the live source of truth for accepted values
                        is the [supported
                        languages](/integration/supported-languages) doc —
                        always consult it before relying on a specific code.
                      enum:
                        - 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
                      x-readme-id: '1.2'
                    phone:
                      type: string
                      description: >-
                        Phone number in E.164 format (e.g., "+14155552671") that
                        will be used during the [Phone
                        Verification](/core-technology/phone-verification/overview)
                        step. If not provided, the user must provide it during
                        the verification flow. Ignored for Business Verification
                        (KYB) workflows. **Important:** This phone number is
                        only enforced if it is a valid E.164 phone number. If
                        the provided number is invalid or cannot be parsed, it
                        will be ignored and the user will be able to input any
                        valid phone number during the Phone Verification step.
                      example: '+14155552671'
                      x-readme-id: '1.3'
                  x-readme-id: '0.4'
                expected_details:
                  type: object
                  description: >-
                    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. Ignored for Business Verification (KYB)
                    workflows.
                  example:
                    first_name: John
                    last_name: Doe
                    date_of_birth: '1990-05-15'
                    nationality: USA
                    id_country: USA
                    expected_document_types:
                      - P
                      - ID
                  properties:
                    first_name:
                      type: string
                      description: >-
                        User's first name. For example, `John`. The matching
                        uses fuzzy comparison, and you can tune the strictness
                        by configuring the name match score threshold in the
                        Console for both ID Document and Proof of Address
                        workflows.
                      example: John
                      x-readme-id: '1.0'
                    last_name:
                      type: string
                      description: >-
                        User's last name. For example, `Doe`. The matching uses
                        fuzzy comparison, and you can tune the strictness by
                        configuring the name match score threshold in the
                        Console for both ID Document and Proof of Address
                        workflows.
                      example: Doe
                      x-readme-id: '1.1'
                    date_of_birth:
                      type: string
                      format: date
                      description: >-
                        User's date of birth with format: YYYY-MM-DD. For
                        example, `1990-05-15`.
                      example: '1990-05-15'
                      x-readme-id: '1.2'
                    gender:
                      type: string
                      nullable: true
                      enum:
                        - M
                        - F
                      default: null
                      description: User's gender. Must be either 'M', 'F', or null.
                      example: M
                      x-readme-id: '1.3'
                    nationality:
                      type: string
                      description: >-
                        ISO 3166-1 alpha-3 country code representing the
                        applicant's country of origin. For example, `USA`. See
                        the [full list of supported country
                        codes](https://docs.didit.me/core-technology/id-verification/supported-documents-id-verification#supported-documents-by-country).
                      example: USA
                      x-readme-id: '1.4'
                    country:
                      type: string
                      description: >-
                        ISO 3166-1 alpha-3 country code used as a fallback for
                        both ID Verification and Proof of Address
                        country-mismatch checks. Required when `address` is
                        provided and neither `id_country` nor `poa_country` is
                        set.
                      example: USA
                      x-readme-id: 1.4.1
                    id_country:
                      type: string
                      description: >-
                        ISO 3166-1 alpha-3 country code representing the
                        expected country of the applicant's ID document, which
                        may differ from nationality. For example, `GBR`. Takes
                        priority over `country` for ID verification country
                        mismatch checks. See the [full list of supported country
                        codes](https://docs.didit.me/core-technology/id-verification/supported-documents-id-verification#supported-documents-by-country).
                      example: GBR
                      x-readme-id: '1.5'
                    poa_country:
                      type: string
                      description: >-
                        ISO 3166-1 alpha-3 country code representing the
                        expected country of the Proof of Address document. For
                        example, `USA`. Takes priority over `country` for POA
                        country mismatch checks. See the [full list of supported
                        country
                        codes](https://docs.didit.me/core-technology/id-verification/supported-documents-id-verification#supported-documents-by-country).
                      example: USA
                      x-readme-id: 1.5.1
                    address:
                      type: string
                      description: >-
                        The address in a human readable format, including as
                        much information as possible. For example, `123 Main St,
                        San Francisco, CA 94105, USA`. When provided, you must
                        also pass `country` or `poa_country`, otherwise the
                        request fails with `400`.
                      example: 123 Main St, San Francisco, CA 94105, USA
                      x-readme-id: '1.6'
                    identification_number:
                      type: string
                      description: >-
                        The user's document number, personal number, or tax
                        number. For example, `123456789`.
                      example: '123456789'
                      x-readme-id: '1.7'
                    ip_address:
                      type: string
                      description: >-
                        Expected IP address for the session, in IPv4 or IPv6
                        format. If the actual IP address differs from this
                        value, a warning will be logged. For example,
                        `192.168.1.100` or `2001:db8::1`.
                      example: 192.168.1.100
                      x-readme-id: '1.8'
                    expected_document_types:
                      type: array
                      description: >-
                        Restricts the document types the user can present during
                        the ID verification (OCR) step. When set, the document
                        selection screen only shows the requested types and the
                        returned `documents_allowed` is filtered accordingly.
                        Values are case-insensitive and deduplicated; unknown
                        values are rejected with `400`. Allowed codes: `P`
                        (passport), `ID` (national ID), `DL` (driver's license),
                        `RP` (residence permit), `HIC` (health insurance card),
                        `TC` (tax card), `SSC` (social security card).
                      items:
                        type: string
                        enum:
                          - P
                          - ID
                          - DL
                          - RP
                          - HIC
                          - TC
                          - SSC
                      example:
                        - P
                        - ID
                      x-readme-id: '1.9'
                  x-readme-id: '0.5'
                portrait_image:
                  type: string
                  format: byte
                  description: >-
                    Base64-encoded portrait image of the end user's face (max
                    2MB; JPEG, PNG, WebP, or TIFF). Required when the workflow
                    is a `Biometric Authentication` workflow with Face Match
                    enabled, or any graph workflow where Face Match runs before
                    ID Verification (OCR). Used as the reference image to match
                    against the liveness capture. Ignored for other workflow
                    types.
                  example: >-
                    iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=
                  x-readme-id: '0.6'
              example:
                workflow_id: 11111111-2222-3333-4444-555555555555
                vendor_data: user-123
                callback: https://example.com/verification/callback
                callback_method: both
                metadata:
                  user_type: premium
                  account_id: ABC123
                language: en
                contact_details:
                  email: john.doe@example.com
                  send_notification_emails: true
                  email_lang: en
                  phone: '+14155552671'
                expected_details:
                  first_name: John
                  last_name: Doe
                  date_of_birth: '1990-05-15'
                  id_country: USA
                  expected_document_types:
                    - P
                    - ID
            examples:
              Create KYC session:
                summary: Full happy-path payload for a User Verification (KYC) session
                description: >-
                  Realistic create call that uses every commonly-set field:
                  workflow selection, vendor linkage, callback routing,
                  metadata, locale, contact prefill, and expected user details.
                  Copy-paste this directly into the cURL sample above.
                value:
                  workflow_id: 11111111-2222-3333-4444-555555555555
                  vendor_data: user-123
                  callback: https://example.com/verification/callback
                  callback_method: both
                  metadata:
                    user_type: premium
                    account_id: ABC123
                  language: en
                  contact_details:
                    email: john.doe@example.com
                    send_notification_emails: true
                    email_lang: en
                    phone: '+14155552671'
                  expected_details:
                    first_name: John
                    last_name: Doe
                    date_of_birth: '1990-05-15'
                    nationality: USA
                    id_country: USA
                    expected_document_types:
                      - P
                      - ID
              Create KYB session:
                summary: Happy-path payload for a Business Verification (KYB) session
                description: >-
                  Same shape as the KYC call — the `workflow_id` is what makes
                  the session KYB. `vendor_data` typically references the
                  company in your system and `contact_details.email` targets the
                  legal representative who will complete the flow. Note:
                  `expected_details` and `contact_details.phone` are ignored for
                  KYB workflows, so they are omitted here.
                value:
                  workflow_id: 33333333-4444-5555-6666-777777777777
                  vendor_data: company-acme-001
                  callback: https://example.com/kyb/callback
                  callback_method: both
                  metadata:
                    tier: enterprise
                    company_id: ACME-001
                  language: en
                  contact_details:
                    email: legal-rep@acme.example.com
                    send_notification_emails: true
                    email_lang: en
      responses:
        '201':
          description: >-
            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.
          content:
            application/json:
              schema:
                type: object
                required:
                  - session_id
                  - session_number
                  - session_token
                  - url
                  - vendor_data
                  - metadata
                  - status
                  - workflow_id
                  - workflow_version
                  - callback
                properties:
                  session_id:
                    type: string
                    format: uuid
                    description: >-
                      Unique identifier of the verification session. Use this id
                      when calling `GET /v3/session/{sessionId}/decision/`.
                    example: 11111111-2222-3333-4444-555555555555
                  session_number:
                    type: integer
                    description: >-
                      Sequential, human-friendly number assigned to the session
                      inside your application. Useful for support and
                      dashboards.
                    example: 43762
                  session_token:
                    type: string
                    description: >-
                      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:
                    type: string
                    format: uri
                    description: >-
                      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:
                    type: string
                    nullable: true
                    description: >-
                      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:
                    description: >-
                      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:
                      user_type: premium
                      account_id: ABC123
                  status:
                    type: string
                    enum:
                      - Not Started
                      - In Progress
                      - Approved
                      - Declined
                      - In Review
                      - Expired
                      - Abandoned
                      - Kyc Expired
                      - Resubmitted
                      - Awaiting User
                    description: >-
                      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.
                    example: Not Started
                  callback:
                    type: string
                    format: uri
                    nullable: true
                    description: >-
                      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.
                    example: https://example.com/verification/callback
                  workflow_id:
                    type: string
                    format: uuid
                    description: >-
                      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:
                    type: integer
                    description: >-
                      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
              examples:
                KYC session:
                  summary: KYC session created
                  value:
                    session_id: 11111111-2222-3333-4444-555555555555
                    session_number: 43762
                    session_token: 3FaJ9wLqX2Mz
                    url: https://verify.didit.me/en/session/3FaJ9wLqX2Mz
                    vendor_data: user-123
                    metadata:
                      user_type: premium
                      account_id: ABC123
                    status: Not Started
                    workflow_id: 11111111-2222-3333-4444-555555555555
                    workflow_version: 3
                    callback: https://example.com/verification/callback
                KYB session:
                  summary: KYB session created
                  value:
                    session_id: 22222222-3333-4444-5555-666666666666
                    session_number: 43763
                    session_token: Yk7pQ2vN8aBc
                    url: https://verify.didit.me/en/session/Yk7pQ2vN8aBc
                    vendor_data: company-acme-001
                    metadata:
                      tier: enterprise
                    status: Not Started
                    workflow_id: 33333333-4444-5555-6666-777777777777
                    workflow_version: 1
                    callback: https://example.com/kyb/callback
        '400':
          description: >-
            Bad request — the payload failed validation (missing or unknown
            `workflow_id`, invalid `language`, blocklisted `vendor_data`,
            malformed `portrait_image`, `address` without
            `country`/`poa_country`, invalid `sandbox_scenario`, etc.) or your
            organization does not have enough credits to start the session.


            The body is an object keyed by the offending field name (or `detail`
            for non-field errors). Values are an **array of strings** for
            input-validation failures (e.g. missing required field, invalid
            language, oversized portrait image) and a **plain string** for
            failures detected while creating the session (unknown `workflow_id`,
            blocklisted `vendor_data`, missing required `portrait_image`).
            Handle both shapes.
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                properties:
                  detail:
                    type: string
                    description: >-
                      Human-readable error message for non-field errors (e.g.
                      insufficient credits). Field-level failures use the field
                      name as the key instead, with a string or array-of-strings
                      value.
                    example: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
              examples:
                Missing workflow_id:
                  summary: Required field omitted (array-of-strings shape)
                  value:
                    workflow_id:
                      - This field is required.
                Invalid workflow_id:
                  summary: Unknown workflow_id (plain-string shape)
                  value:
                    workflow_id: Invalid workflow_id.
                Insufficient credits:
                  summary: Organization is out of credits
                  value:
                    detail: >-
                      You don't have enough credits to perform this request.
                      Please top up at https://business.didit.me
                Blocklisted vendor_data:
                  summary: vendor_data is blocklisted
                  value:
                    vendor_data: >-
                      This user or business has been blocked and cannot create
                      new verification sessions.
                Portrait image too large:
                  summary: portrait_image fails size validation
                  value:
                    portrait_image:
                      - Image size exceeds 2MB.
                Portrait image required:
                  summary: Biometric Authentication workflow without portrait_image
                  value:
                    portrait_image: >-
                      This field is required for performing face match for
                      biometric authentication.
        '403':
          description: >-
            Authentication or authorization failed. The endpoint returns `403`
            whenever the `x-api-key` header is missing, malformed, the API key
            has expired, or the API key is valid but the client does not have
            permission to create sessions in this application (for example the
            API key belongs to a different organization). Re-fetch the
            application API key via the [Auth API](/auth-api/get-credentials)
            and retry.


            **Note:** All four failure modes return the same response body —
            `{"detail": "You do not have permission to perform this action."}` —
            and there is no machine-readable discriminator (no error code field,
            no `WWW-Authenticate` challenge) that lets you tell them apart. If
            you need to differentiate (for example to surface a more specific
            error to the end user), check the `x-api-key` header is present and
            belongs to the intended application before calling this endpoint.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    example: You do not have permission to perform this action.
              examples:
                Missing or invalid API key:
                  summary: No `x-api-key` header or invalid API key
                  value:
                    detail: You do not have permission to perform this action.
                No permission:
                  summary: Client cannot create sessions
                  value:
                    detail: You do not have permission to perform this action.
        '429':
          description: >-
            Rate limit exceeded — more than 600 session-create requests in a
            60-second window from the same API key / IP. Inspect `Retry-After`
            and back off before retrying.


            **Note:** The source code also defines a `FREE_SESSION_RATE_LIMIT`
            of 10 calls/minute alongside `PAID_SESSION_RATE_LIMIT=600`
            (`sessions/serializers/session.py`), but that free-tier ceiling is
            **not applied to this V3 endpoint** — the rate-limit middleware
            enforces the 600/min `SESSION_CREATE_RATE_LIMIT` regardless of plan.
            600/min is the only limit you can hit here.
          headers:
            Retry-After:
              description: Seconds the caller must wait before retrying the request.
              schema:
                type: integer
                minimum: 1
                example: 42
            X-RateLimit-Limit:
              description: >-
                Maximum number of requests allowed in the current 60-second
                window for the limit that was breached (600 for session-create).
                Absent on the sandbox-quota variant.
              schema:
                type: integer
                example: 600
            X-RateLimit-Remaining:
              description: >-
                Number of requests still allowed in the current window for the
                breached limit. Absent on the sandbox-quota variant.
              schema:
                type: integer
                minimum: 0
                example: 0
            X-RateLimit-Reset:
              description: >-
                UTC epoch seconds when the current rate-limit window resets.
                Absent on the sandbox-quota variant.
              schema:
                type: integer
                minimum: 0
                example: 1747497600
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    description: >-
                      Human-readable explanation of the rate-limit breach. Use
                      this message to distinguish which of the three limits was
                      hit.
                    example: >-
                      Session creation rate limit exceeded. You can make up to
                      600 requests per minute.
              examples:
                Session-create limit:
                  summary: More than 600 session-create requests per minute
                  value:
                    detail: >-
                      Session creation rate limit exceeded. You can make up to
                      600 requests per minute.
                Sandbox session quota:
                  summary: Sandbox application exceeded 500 sessions in 24 hours
                  value:
                    detail: >-
                      Sandbox session quota exceeded (500 sessions per 24h).
                      Wait for the window to reset. Expected available in 3600
                      seconds.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: |-
            curl -X POST 'https://verification.didit.me/v3/session/' \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "workflow_id": "11111111-2222-3333-4444-555555555555",
                "vendor_data": "user-123",
                "callback": "https://example.com/verification/callback",
                "callback_method": "both",
                "metadata": {"user_type": "premium", "account_id": "ABC123"},
                "language": "en",
                "contact_details": {
                  "email": "john.doe@example.com",
                  "send_notification_emails": true,
                  "email_lang": "en",
                  "phone": "+14155552671"
                },
                "expected_details": {
                  "first_name": "John",
                  "last_name": "Doe",
                  "date_of_birth": "1990-05-15",
                  "id_country": "USA",
                  "expected_document_types": ["P", "ID"]
                }
              }'
        - lang: python
          label: Python
          source: >-
            import requests


            url = "https://verification.didit.me/v3/session/"

            headers = {
                'x-api-key': 'YOUR_API_KEY',
                "Content-Type": "application/json",
            }

            payload = {
                "workflow_id": "11111111-2222-3333-4444-555555555555",
                "vendor_data": "user-123",
                "callback": "https://example.com/verification/callback",
                "callback_method": "both",
                "metadata": {"user_type": "premium", "account_id": "ABC123"},
                "language": "en",
                "contact_details": {
                    "email": "john.doe@example.com",
                    "send_notification_emails": True,
                    "email_lang": "en",
                    "phone": "+14155552671",
                },
                "expected_details": {
                    "first_name": "John",
                    "last_name": "Doe",
                    "date_of_birth": "1990-05-15",
                    "id_country": "USA",
                    "expected_document_types": ["P", "ID"],
                },
            }


            response = requests.post(url, json=payload, headers=headers,
            timeout=15)

            response.raise_for_status()

            session = response.json()

            print(session["session_id"], session["url"])
        - lang: javascript
          label: JavaScript
          source: >-
            const response = await
            fetch('https://verification.didit.me/v3/session/', {
              method: 'POST',
              headers: {
                'x-api-key': 'YOUR_API_KEY',
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({
                workflow_id: '11111111-2222-3333-4444-555555555555',
                vendor_data: 'user-123',
                callback: 'https://example.com/verification/callback',
                callback_method: 'both',
                metadata: { user_type: 'premium', account_id: 'ABC123' },
                language: 'en',
                contact_details: {
                  email: 'john.doe@example.com',
                  send_notification_emails: true,
                  email_lang: 'en',
                  phone: '+14155552671',
                },
                expected_details: {
                  first_name: 'John',
                  last_name: 'Doe',
                  date_of_birth: '1990-05-15',
                  id_country: 'USA',
                  expected_document_types: ['P', 'ID'],
                },
              }),
            });


            if (!response.ok) {
              throw new Error(`Session create failed: ${response.status}`);
            }

            const session = await response.json();

            console.log(session.session_id, session.url);
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````