{
  "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"
    }
  ],
  "tags": [],
  "paths": {
    "/system/healthcheck": {
      "get": {
        "summary": "Service healthcheck (public, unauthenticated)",
        "description": "Public unauthenticated liveness probe. Returns `HTTP 200` with `{status: \"ok\", timestamp}` when service, DB, cache, and broker are healthy.",
        "operationId": "get_system_healthcheck",
        "tags": [
          "System"
        ],
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Service is healthy and accepting requests. Body is always `{\"status\": \"ok\", \"timestamp\": \"<iso8601>\"}`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "timestamp"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "ok"
                      ],
                      "description": "Liveness marker. Always the literal string `\"ok\"` on a 200 response.",
                      "example": "ok"
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Server time at which the healthcheck was evaluated, in ISO 8601 with millisecond precision and a `Z` (UTC) suffix.",
                      "example": "2026-04-21T16:42:50.445Z"
                    }
                  }
                },
                "examples": {
                  "Healthy": {
                    "summary": "Service is up",
                    "value": {
                      "status": "ok",
                      "timestamp": "2026-04-21T16:42:50.445Z"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service is unhealthy — one of the configured health-check plugins (database, cache, broker) is failing. The response body is intentionally minimal; the HTTP status is the load-bearing signal. Retry with exponential backoff."
          },
          "5XX": {
            "description": "Verification service is degraded or unreachable. Treat any non-2xx response as \"unhealthy\" and retry with exponential backoff."
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -i https://verification.didit.me/system/healthcheck"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.get(\"https://verification.didit.me/system/healthcheck\", timeout=5)\nresp.raise_for_status()\nprint(resp.json())  # {'status': 'ok', 'timestamp': '2026-04-21T16:42:50.445Z'}"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(\"https://verification.didit.me/system/healthcheck\");\nif (!res.ok) throw new Error(`Didit unhealthy: ${res.status}`);\nconst body = await res.json();\nconsole.log(body); // { status: 'ok', timestamp: '2026-04-21T16:42:50.445Z' }"
          }
        ]
      }
    },
    "/v3/session/": {
      "post": {
        "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.\n\nCall 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.\n\n**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.\n\n**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",
        "tags": [
          "Sessions"
        ],
        "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.\n\nThe 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`, no reference face available for a Face Match workflow). 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."
                      ]
                    }
                  },
                  "No stored face for vendor_data": {
                    "summary": "Face Match workflow without portrait_image and no stored face for the vendor_data user",
                    "value": {
                      "portrait_image": "No stored face image was found for this user. Send a portrait_image, or complete an approved verification with face liveness or an ID document for this vendor_data first."
                    }
                  },
                  "Portrait image required without vendor_data": {
                    "summary": "Face Match workflow without portrait_image and without vendor_data",
                    "value": {
                      "portrait_image": "A portrait image is required to perform face match for this workflow. Send a portrait_image, or provide the vendor_data of a user with a previously stored face image to reuse it."
                    }
                  }
                }
              }
            }
          },
          "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.\n\n**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.\n\n**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/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"workflow_id\": \"11111111-2222-3333-4444-555555555555\",\n    \"vendor_data\": \"user-123\",\n    \"callback\": \"https://example.com/verification/callback\",\n    \"callback_method\": \"both\",\n    \"metadata\": {\"user_type\": \"premium\", \"account_id\": \"ABC123\"},\n    \"language\": \"en\",\n    \"contact_details\": {\n      \"email\": \"john.doe@example.com\",\n      \"send_notification_emails\": true,\n      \"email_lang\": \"en\",\n      \"phone\": \"+14155552671\"\n    },\n    \"expected_details\": {\n      \"first_name\": \"John\",\n      \"last_name\": \"Doe\",\n      \"date_of_birth\": \"1990-05-15\",\n      \"id_country\": \"USA\",\n      \"expected_document_types\": [\"P\", \"ID\"]\n    }\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = \"https://verification.didit.me/v3/session/\"\nheaders = {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Content-Type\": \"application/json\",\n}\npayload = {\n    \"workflow_id\": \"11111111-2222-3333-4444-555555555555\",\n    \"vendor_data\": \"user-123\",\n    \"callback\": \"https://example.com/verification/callback\",\n    \"callback_method\": \"both\",\n    \"metadata\": {\"user_type\": \"premium\", \"account_id\": \"ABC123\"},\n    \"language\": \"en\",\n    \"contact_details\": {\n        \"email\": \"john.doe@example.com\",\n        \"send_notification_emails\": True,\n        \"email_lang\": \"en\",\n        \"phone\": \"+14155552671\",\n    },\n    \"expected_details\": {\n        \"first_name\": \"John\",\n        \"last_name\": \"Doe\",\n        \"date_of_birth\": \"1990-05-15\",\n        \"id_country\": \"USA\",\n        \"expected_document_types\": [\"P\", \"ID\"],\n    },\n}\n\nresponse = requests.post(url, json=payload, headers=headers, timeout=15)\nresponse.raise_for_status()\nsession = response.json()\nprint(session[\"session_id\"], session[\"url\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch('https://verification.didit.me/v3/session/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    workflow_id: '11111111-2222-3333-4444-555555555555',\n    vendor_data: 'user-123',\n    callback: 'https://example.com/verification/callback',\n    callback_method: 'both',\n    metadata: { user_type: 'premium', account_id: 'ABC123' },\n    language: 'en',\n    contact_details: {\n      email: 'john.doe@example.com',\n      send_notification_emails: true,\n      email_lang: 'en',\n      phone: '+14155552671',\n    },\n    expected_details: {\n      first_name: 'John',\n      last_name: 'Doe',\n      date_of_birth: '1990-05-15',\n      id_country: 'USA',\n      expected_document_types: ['P', 'ID'],\n    },\n  }),\n});\n\nif (!response.ok) {\n  throw new Error(`Session create failed: ${response.status}`);\n}\nconst session = await response.json();\nconsole.log(session.session_id, session.url);"
          }
        ],
        "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. 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": {
                      "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"
                      },
                      "company_name": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Expected legal name of the business being verified. For example, `Tesco PLC`. Pre-fills the company-name field of the registry search in Business Verification (KYB) workflows; when `registration_number` is also provided, the registry lookup combines both identifiers. Ignored for KYC workflows.",
                        "example": "Tesco PLC",
                        "x-readme-id": "1.10"
                      },
                      "registry_country": {
                        "type": "string",
                        "maxLength": 10,
                        "description": "ISO 3166-1 alpha-2 code of the country whose company registry the business is registered in. For example, `GB`. Where company registries operate at state or province level, append the ISO 3166-2 subdivision code as `XX-YY`: `US-CA` targets the California registry. Same format as `country_code` on the [KYB Registry Search API](https://docs.didit.me/standalone-apis/kyb-registry-search). Pre-selects the registry country in Business Verification (KYB) workflows; ignored for KYC workflows.",
                        "example": "US-CA",
                        "x-readme-id": "1.11"
                      },
                      "registration_number": {
                        "type": "string",
                        "maxLength": 100,
                        "description": "Expected registration number of the business in the company registry. For example, `00445790`. Pre-fills the registration-number field of the registry search in Business Verification (KYB) workflows; when `company_name` is also provided, the registry lookup combines both identifiers. Ignored for KYC workflows.",
                        "example": "00445790",
                        "x-readme-id": "1.12"
                      }
                    },
                    "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). 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=",
                    "x-readme-id": "0.6"
                  },
                  "sandbox_scenario": {
                    "type": "string",
                    "description": "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](/integration/sandbox-testing). 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`.",
                    "example": "decline_document_expired"
                  }
                },
                "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. `expected_details` accepts the business fields (`company_name`, `registry_country`, `registration_number`), which pre-fill the company registry search; person-level fields and `contact_details.phone` are ignored for KYB workflows.",
                  "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"
                    },
                    "expected_details": {
                      "company_name": "Tesco PLC",
                      "registry_country": "GB",
                      "registration_number": "00445790"
                    },
                    "language": "en",
                    "contact_details": {
                      "email": "legal-rep@acme.example.com",
                      "send_notification_emails": true,
                      "email_lang": "en"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/session/imports/": {
      "post": {
        "summary": "Create a verification import job",
        "description": "Create an asynchronous import job for historical User Verification sessions, Business Verification sessions, transaction monitoring records, or workflow custom status rules. Use this to migrate from providers such as Sumsub, MetaMap, Veriff, Onfido, Persona, Trulioo, Jumio, Incode, iDenfy, or any other provider after transforming the export into Didit's canonical CSV/NDJSON format. Imports are disabled by default and must be enabled per organization by Didit.",
        "operationId": "post_v3_session_imports_create",
        "tags": [
          "Sessions"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "import_type": {
                    "type": "string",
                    "enum": [
                      "user_verification",
                      "business_verification",
                      "status_rules",
                      "transactions"
                    ],
                    "default": "user_verification",
                    "description": "What to import: historical User Verification sessions, historical Business Verification sessions, workflow custom status rules, or historical transaction monitoring records."
                  },
                  "workflow_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workflow UUID that imported sessions or status rules should be attached to. Required for user_verification, business_verification, and status_rules. Omit for transactions."
                  },
                  "source_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "ndjson"
                    ],
                    "description": "Optional. Inferred from the uploaded file extension when omitted."
                  },
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Canonical CSV or NDJSON file. Use NDJSON for large imports. Maximum upload size 25 MB (use `source_file_url` for bigger files). Mutually exclusive with `source_file_url`."
                  },
                  "source_file_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Alternative to file upload. Must be an HTTPS URL resolving to a public address (private/loopback networks are rejected). Mutually exclusive with `file`."
                  }
                }
              }
            },
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_file_url"
                ],
                "properties": {
                  "import_type": {
                    "type": "string",
                    "enum": [
                      "user_verification",
                      "business_verification",
                      "status_rules",
                      "transactions"
                    ],
                    "default": "user_verification",
                    "description": "What to import: historical User Verification sessions, historical Business Verification sessions, workflow custom status rules, or historical transaction monitoring records."
                  },
                  "workflow_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Workflow UUID that imported sessions or status rules should be attached to. Required for user_verification, business_verification, and status_rules. Omit for transactions."
                  },
                  "source_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "ndjson"
                    ],
                    "description": "Optional. Inferred from the URL's file extension when omitted."
                  },
                  "source_file_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Alternative to file upload. Must be an HTTPS URL resolving to a public address (private/loopback networks are rejected). Mutually exclusive with `file`."
                  }
                }
              },
              "examples": {
                "URL-based import": {
                  "value": {
                    "import_type": "user_verification",
                    "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab",
                    "source_file_url": "https://files.example.com/exports/sessions.ndjson"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Import job accepted. Poll the returned `uuid` until `status` is `COMPLETED` or `FAILED`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionImportJob"
                },
                "examples": {
                  "Job accepted": {
                    "value": {
                      "uuid": "3f9c1a2b-4d5e-4f60-8a7b-9c0d1e2f3a4b",
                      "status": "PENDING",
                      "import_type": "user_verification",
                      "source_format": "ndjson",
                      "source_filename": "sessions.ndjson",
                      "total_rows": 0,
                      "processed_rows": 0,
                      "checkpoint_row": 0,
                      "created_count": 0,
                      "updated_count": 0,
                      "skipped_count": 0,
                      "failed_count": 0,
                      "total_media_bytes": 0,
                      "last_error": null,
                      "created_at": "2026-05-17T08:42:11Z",
                      "updated_at": "2026-05-17T08:42:11Z",
                      "started_at": null,
                      "completed_at": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid workflow, file type, file size, source URL, or selector combination.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid workflow": {
                    "value": {
                      "workflow_id": [
                        "Invalid workflow_id."
                      ]
                    }
                  },
                  "Workflow required": {
                    "value": {
                      "workflow_id": [
                        "This field is required for this import type."
                      ]
                    }
                  },
                  "Wrong file type": {
                    "value": {
                      "file": [
                        "Only CSV and NDJSON files are supported."
                      ]
                    }
                  },
                  "File too large": {
                    "value": {
                      "file": [
                        "Import file exceeds the maximum allowed size."
                      ]
                    }
                  },
                  "No source": {
                    "value": {
                      "file": [
                        "Upload a file or provide source_file_url."
                      ]
                    }
                  },
                  "Both sources": {
                    "value": {
                      "file": [
                        "Use either file or source_file_url, not both."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Imports not enabled, sandbox application, or missing/invalid API key (this endpoint returns `403`, never `401`, for authentication failures). Verification imports are disabled by default and must be enabled per organization by Didit.",
            "content": {
              "application/json": {
                "examples": {
                  "Imports not enabled": {
                    "value": {
                      "detail": "Verification imports are not enabled for this organization."
                    }
                  },
                  "Sandbox": {
                    "value": {
                      "detail": "Verification imports are not enabled for sandbox applications."
                    }
                  },
                  "Bad API key": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "The application already has an active import job (PENDING/PROCESSING/PAUSED). Wait for it to finish before starting another.",
            "content": {
              "application/json": {
                "examples": {
                  "Active job": {
                    "value": {
                      "detail": "This application already has an active import job. Wait for it to finish before starting another."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/session/imports/template/": {
      "get": {
        "summary": "Download verification import template",
        "description": "Download the canonical CSV header for verification imports. The column set is determined by `import_type`. The first column of session templates is the idempotency key `external_id`.",
        "operationId": "get_v3_session_imports_template",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "import_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "user_verification",
                "business_verification",
                "status_rules",
                "transactions"
              ],
              "default": "user_verification"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV template file.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot download import templates. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "summary": "Missing, invalid, or revoked API key",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/session/imports/{importId}/": {
      "get": {
        "summary": "Retrieve a verification import job",
        "description": "Retrieve import progress counters and terminal status for one job. Poll until `status` is `COMPLETED` or `FAILED`; `failed_count` > 0 means some rows were rejected — list them via the errors endpoint.",
        "operationId": "get_v3_session_imports_retrieve",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Import job status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionImportJob"
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot read import jobs for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "summary": "Missing, invalid, or revoked API key",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Import job not found for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/session/imports/{importId}/errors/": {
      "get": {
        "summary": "List verification import row errors",
        "description": "List row-level errors for a verification import job, ordered by `row_number`. Use this endpoint to produce a corrected retry file containing only failed rows.",
        "operationId": "get_v3_session_imports_errors",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "importId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size. Defaults to 50 when omitted."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Zero-based offset of the first record to return."
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated row errors.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of error rows — exact up to 100, capped at 100 beyond that. Rely on `next` being `null` to detect the last page."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or `null` on the first page."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SessionImportError"
                      }
                    }
                  }
                },
                "examples": {
                  "Row errors": {
                    "value": {
                      "count": 1,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "uuid": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                          "row_number": 17,
                          "external_id": "ext-000017",
                          "error": "status: invalid value 'unknown_status'",
                          "raw_row": {
                            "external_id": "ext-000017",
                            "status": "unknown_status",
                            "first_name": "Jane"
                          },
                          "created_at": "2026-05-17T08:44:02Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot read import jobs for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "summary": "Missing, invalid, or revoked API key",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Import job not found for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/session/{sessionId}/decision/": {
      "get": {
        "summary": "Retrieve the full decision for a verification session",
        "description": "Retrieve the complete decision report for a verification session — the rolled-up session `status` plus one report per verification feature the workflow executed. This is the canonical read endpoint for both User Verification (KYC) and Business Verification (KYB) sessions: the same path serves both, and the top-level `session_kind` discriminator tells you which shape you received (`\"user\"` or `\"business\"`).\n\n**Every feature report is a plural array.** V3 workflows are graphs that can run the same feature more than once, so each block (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `phone_verifications`, `email_verifications`, `aml_screenings`, `ip_analyses`, `database_validations`, `questionnaire_responses`) is a JSON array whose items each carry a `node_id` identifying the workflow graph node that produced them. Read NFC as `response.nfc_verifications[0]` — never as a singular `nfc` field. A block is `null` until the workflow has run that feature at least once; features the workflow does not include never appear with data.\n\n**When to call.** The recommended pattern is event-driven: wait for the `status.updated` webhook that fires when the session reaches a decision (`Approved`, `Declined`, `In Review`), then call this endpoint once to fetch the full report. Polling also works — the endpoint can be called at any point in the session lifecycle and returns the data produced so far — but webhook-then-fetch is cheaper and faster. Media URLs in the response (document images, videos, PDFs) are short-lived presigned links: fetch them promptly or re-request the decision to get fresh ones, and do not persist them as long-term references.\n\n**Statuses.** The top-level `status` is the session lifecycle status (`Not Started`, `In Progress`, `Awaiting User`, `In Review`, `Approved`, `Declined`, `Resubmitted`, `Expired`, `Kyc Expired`, `Abandoned`). Each feature item additionally carries its own feature-level `status` (`Not Finished`, `Approved`, `Declined`, `In Review`, ...). A session can be `Approved` overall while an individual feature is `In Review`, depending on the workflow's decision rules.\n\n**Authentication and visibility.** Authenticate with the application API key in the `x-api-key` header (a console user Bearer token with the `read:sessions` permission also works). Only sessions owned by the authenticated application are visible. If your workflow restricts response attributes, non-allowed fields inside feature items are returned as `null` while `status`, `warnings`, and `node_id` are always preserved.\n\n**Rate limits.** GET requests are limited to 600 per minute per API key. Exceeding the limit returns `429` with `Retry-After` and `X-RateLimit-*` headers.",
        "operationId": "get_v3_session_decision",
        "responses": {
          "200": {
            "description": "Full decision report. `session_kind` discriminates the two shapes: `user` sessions carry the KYC feature arrays shown below; `business` sessions instead carry `registry_checks`, `document_verifications` and `key_people_checks` alongside the shared arrays. Feature arrays are `null` until the workflow has run that feature; media URLs are short-lived presigned links.",
            "content": {
              "application/json": {
                "examples": {
                  "Example": {
                    "summary": "Full Example",
                    "value": {
                      "session_id": "11111111-2222-3333-4444-555555555555",
                      "session_kind": "user",
                      "session_number": 43762,
                      "session_url": "https://verify.didit.me/session/11111111-2222-3333-4444-555555555555",
                      "status": "In Review",
                      "environment": "live",
                      "workflow_id": "11111111-2222-3333-4444-555555555555",
                      "features": [
                        "ID_VERIFICATION",
                        "NFC",
                        "LIVENESS",
                        "FACE_MATCH",
                        "POA",
                        "PHONE",
                        "DATABASE_VALIDATION",
                        "AML",
                        "IP_ANALYSIS"
                      ],
                      "vendor_data": "11111111-1111-1111-1111-111111111111",
                      "metadata": {
                        "user_type": "premium",
                        "account_id": "ABC123"
                      },
                      "expected_details": {
                        "first_name": "Carmen",
                        "last_name": "Española Española"
                      },
                      "contact_details": {
                        "email": "carmen@example.com",
                        "email_lang": "es",
                        "send_notification_emails": false,
                        "phone": null
                      },
                      "callback": "https://verify.didit.me/",
                      "id_verifications": [
                        {
                          "node_id": "feature_ocr_1",
                          "status": "Approved",
                          "document_type": "Identity Card",
                          "document_subtype": "ID_CARD_GENERIC",
                          "document_number": "CAA000000",
                          "personal_number": "99999999R",
                          "portrait_image": "https://<media-host>/portrait.jpg",
                          "front_image": "https://<media-host>/front.jpg",
                          "front_video": "https://<media-host>/front.mp4",
                          "back_image": "https://<media-host>/back.jpg",
                          "back_video": "https://<media-host>/back.mp4",
                          "full_front_image": "https://<media-host>/full_front.jpg",
                          "full_back_image": "https://<media-host>/full_back.jpg",
                          "front_image_camera_front": "https://<media-host>/front_camera_front.jpg",
                          "back_image_camera_front": "https://<media-host>/back_camera_front.jpg",
                          "date_of_birth": "1980-01-01",
                          "age": 45,
                          "expiration_date": "2031-06-02",
                          "date_of_issue": "2021-06-02",
                          "issuing_state": "ESP",
                          "issuing_state_name": "Spain",
                          "first_name": "Carmen",
                          "last_name": "Española Española",
                          "full_name": "Carmen Española Española",
                          "gender": "F",
                          "address": "Avda de Madrid 34, Madrid, Madrid",
                          "formatted_address": "Avda de Madrid 34, Madrid, Madrid 28822, Spain",
                          "place_of_birth": "Madrid",
                          "marital_status": "Single",
                          "nationality": "ESP",
                          "extra_fields": {
                            "dl_categories": [],
                            "blood_group": null
                          },
                          "mrz": {
                            "surname": "ESPANOLA ESPANOLA",
                            "name": "CARMEN",
                            "country": "ESP",
                            "nationality": "ESP",
                            "birth_date": "800101",
                            "expiry_date": "310602",
                            "sex": "F",
                            "document_type": "ID",
                            "document_number": "CAA000000",
                            "optional_data": "99999999R",
                            "optional_data_2": "",
                            "birth_date_hash": "4",
                            "expiry_date_hash": "8",
                            "document_number_hash": "4",
                            "final_hash": "1",
                            "personal_number": "99999999R",
                            "warnings": [],
                            "errors": [],
                            "mrz_type": "TD1",
                            "mrz_string": "IDESPCAA000000499999999R<<<<<<\n8001014F3106028ESP<<<<<<<<<<<1\nESPANOLA<ESPANOLA<<CARMEN<<<<<",
                            "mrz_key": "CAA000000480010143106028"
                          },
                          "parsed_address": {
                            "city": "Madrid",
                            "label": "Spain ID Card Address",
                            "region": "Madrid",
                            "country": "ES",
                            "category": "Residential",
                            "street_1": "Avda de Madrid 34",
                            "street_2": null,
                            "is_verified": true,
                            "postal_code": "28822",
                            "raw_results": {
                              "types": [
                                "street_address"
                              ],
                              "geometry": {
                                "location": {
                                  "lat": 37.4222804,
                                  "lng": -122.0843428
                                },
                                "viewport": {
                                  "northeast": {
                                    "lat": 37.4237349802915,
                                    "lng": -122.083183169709
                                  },
                                  "southwest": {
                                    "lat": 37.4210370197085,
                                    "lng": -122.085881130292
                                  }
                                },
                                "location_type": "ROOFTOP"
                              },
                              "place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
                              "formatted_address": "Avda de Madrid 34, 28822 Madrid, Spain",
                              "navigation_points": [
                                {
                                  "location": {
                                    "latitude": 37.4222804,
                                    "longitude": -122.0843428
                                  }
                                }
                              ],
                              "address_components": [
                                {
                                  "types": [
                                    "street_number"
                                  ],
                                  "long_name": "34",
                                  "short_name": "34"
                                },
                                {
                                  "types": [
                                    "route"
                                  ],
                                  "long_name": "Avda de Madrid",
                                  "short_name": "Avda de Madrid"
                                },
                                {
                                  "types": [
                                    "locality",
                                    "political"
                                  ],
                                  "long_name": "Madrid",
                                  "short_name": "Madrid"
                                },
                                {
                                  "types": [
                                    "country",
                                    "political"
                                  ],
                                  "long_name": "Spain",
                                  "short_name": "ES"
                                },
                                {
                                  "types": [
                                    "postal_code"
                                  ],
                                  "long_name": "28822",
                                  "short_name": "28822"
                                }
                              ]
                            },
                            "address_type": "Avda",
                            "document_location": {
                              "latitude": 37.4222804,
                              "longitude": -122.0843428
                            },
                            "formatted_address": "Avda de Madrid 34, 28822 Madrid, Spain"
                          },
                          "front_image_camera_front_face_match_score": 95.5,
                          "back_image_camera_front_face_match_score": 93.2,
                          "front_image_quality_score": {
                            "focus_score": 85.3,
                            "brightness_score": 92.1,
                            "brightness_issue": "ok",
                            "is_document_fully_visible": true,
                            "resolution_score": 72.4,
                            "overall_score": 88.7
                          },
                          "back_image_quality_score": {
                            "focus_score": 79.1,
                            "brightness_score": 90.5,
                            "brightness_issue": "ok",
                            "is_document_fully_visible": true,
                            "resolution_score": 72.4,
                            "overall_score": 85.2
                          },
                          "extra_files": [
                            "https://<media-host>/extra_id_verification.jpg"
                          ],
                          "matches": [
                            {
                              "session_id": "7d4f1f60-9c2e-4b8a-8f3d-2a1b5c6d7e8f",
                              "session_number": 1234,
                              "vendor_data": "user_ref_001",
                              "verification_date": "2024-06-15T10:30:00Z",
                              "user_details": {
                                "name": "John Smith",
                                "document_type": "ID",
                                "document_number": "CLM148418"
                              },
                              "status": "Approved",
                              "is_blocklisted": false,
                              "api_service": null,
                              "front_image_url": "https://<media-host>/document_front.jpg"
                            },
                            {
                              "session_id": "b2c8e4d1-6a3f-4e7b-95c0-1d8f2a6b4c9e",
                              "session_number": 1235,
                              "vendor_data": null,
                              "verification_date": "2024-05-20T14:45:00Z",
                              "user_details": {
                                "name": "John Smith",
                                "document_type": "ID",
                                "document_number": "CLM148418"
                              },
                              "status": "Declined",
                              "is_blocklisted": true,
                              "api_service": "ID_VERIFICATION",
                              "front_image_url": "https://<media-host>/document_front_2.jpg"
                            }
                          ],
                          "warnings": [
                            {
                              "feature": "ID_VERIFICATION",
                              "risk": "QR_NOT_DETECTED",
                              "additional_data": null,
                              "log_type": "information",
                              "short_description": "QR not detected",
                              "long_description": "The system couldn't find or read the QR code on the document, which is necessary for document verification. This could be due to poor image quality or an unsupported document type.",
                              "node_id": "feature_ocr_1"
                            }
                          ]
                        }
                      ],
                      "nfc_verifications": [
                        {
                          "node_id": "feature_nfc_1",
                          "status": "In Review",
                          "portrait_image": "https://<media-host>/portrait.jpg",
                          "signature_image": "https://<media-host>/signature.jpg",
                          "chip_data": {
                            "document_type": "ID",
                            "issuing_country": "ESP",
                            "document_number": "123456789",
                            "expiration_date": "2030-01-01",
                            "first_name": "John",
                            "last_name": "Smith",
                            "birth_date": "1990-05-15",
                            "gender": "M",
                            "nationality": "ESP",
                            "address": "CALLE MAYOR 123 4B, MADRID, MADRID",
                            "place_of_birth": "MADRID, MADRID"
                          },
                          "authenticity": {
                            "sod_integrity": true,
                            "dg_integrity": true
                          },
                          "certificate_summary": {
                            "issuer": "Common Name: CSCA SPAIN, Serial Number: 3, Organization: DIRECCION GENERAL DE LA POLICIA, Country: ES",
                            "subject": "Common Name: DS n-eID SPAIN 2, Organizational Unit: PASSPORT, Organization: DIRECCION GENERAL DE LA POLICIA, Country: ES",
                            "serial_number": "118120836164494130086420187336801405660",
                            "not_valid_after": "2031-02-18 10:21:11",
                            "not_valid_before": "2020-11-18 10:21:11"
                          },
                          "warnings": [
                            {
                              "feature": "NFC",
                              "risk": "DATA_INCONSISTENT",
                              "additional_data": null,
                              "log_type": "warning",
                              "short_description": "OCR and NFC mrz code extracted are not the same",
                              "long_description": "The Optical Character Recognition (OCR) data and the NFC chip data don't match, indicating potential document tampering or data inconsistency.",
                              "node_id": "feature_nfc_1"
                            }
                          ]
                        }
                      ],
                      "liveness_checks": [
                        {
                          "node_id": "feature_liveness_1",
                          "status": "Approved",
                          "method": "ACTIVE_3D",
                          "score": 89.92,
                          "reference_image": "https://<media-host>/reference.jpg",
                          "video_url": "https://<media-host>/video.mp4",
                          "age_estimation": 24.3,
                          "matches": [
                            {
                              "session_id": "7d4f1f60-9c2e-4b8a-8f3d-2a1b5c6d7e8f",
                              "session_number": 1234,
                              "vendor_data": "user_ref_001",
                              "verification_date": "2024-06-15T10:30:00Z",
                              "user_details": {
                                "full_name": "John Smith",
                                "document_type": "ID",
                                "document_number": "CLM148418"
                              },
                              "status": "Approved",
                              "is_blocklisted": false,
                              "is_allowlisted": false,
                              "api_service": null,
                              "source": "session",
                              "match_image_url": "https://<media-host>/face_match.jpg",
                              "similarity_percentage": 98.5
                            },
                            {
                              "session_id": "3e9a7c21-5b48-4f0e-9c6a-8d2f4b1e0a35",
                              "session_number": 1100,
                              "vendor_data": "customer_xyz",
                              "verification_date": "2024-04-10T09:15:00Z",
                              "user_details": {
                                "full_name": "John Smith",
                                "document_type": "P",
                                "document_number": "DZ8375806"
                              },
                              "status": "Approved",
                              "is_blocklisted": false,
                              "is_allowlisted": false,
                              "api_service": "PASSIVE_LIVENESS",
                              "source": "session",
                              "match_image_url": "https://<media-host>/face_match_2.jpg",
                              "similarity_percentage": 95.2
                            }
                          ],
                          "warnings": [
                            {
                              "feature": "LIVENESS",
                              "risk": "LOW_LIVENESS_SCORE",
                              "additional_data": null,
                              "log_type": "information",
                              "short_description": "Low liveness score",
                              "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data.",
                              "node_id": "feature_liveness_1"
                            }
                          ],
                          "face_quality": null,
                          "face_luminance": null
                        }
                      ],
                      "face_matches": [
                        {
                          "node_id": "feature_face_match_1",
                          "status": "In Review",
                          "score": 65.43,
                          "source_image_session_id": null,
                          "source_image": "https://<media-host>/source-image.jpg",
                          "target_image": "https://<media-host>/target-image.jpg",
                          "warnings": [
                            {
                              "feature": "FACEMATCH",
                              "risk": "LOW_FACE_MATCH_SIMILARITY",
                              "additional_data": null,
                              "log_type": "warning",
                              "short_description": "Low face match similarity",
                              "long_description": "The facial features of the provided image don't closely match the reference image, suggesting a potential identity mismatch.",
                              "node_id": "feature_face_match_1"
                            }
                          ]
                        }
                      ],
                      "phone_verifications": [
                        {
                          "node_id": "feature_phone_1",
                          "status": "Approved",
                          "phone_number_prefix": "+34",
                          "phone_number": "600600600",
                          "full_number": "+34600600600",
                          "country_code": "ES",
                          "country_name": "Spain",
                          "carrier": {
                            "name": "Orange",
                            "type": "mobile"
                          },
                          "is_disposable": false,
                          "is_virtual": false,
                          "verification_method": "sms",
                          "verification_attempts": 1,
                          "verified_at": "2024-07-28T06:47:35.654321Z",
                          "lifecycle": [
                            {
                              "type": "PHONE_VERIFICATION_MESSAGE_SENT",
                              "timestamp": "2025-08-24T09:12:39.580554+00:00",
                              "details": {
                                "status": "Success",
                                "reason": null,
                                "channel": "sms",
                                "actual_channel": "sms"
                              },
                              "fee": 0.1
                            },
                            {
                              "type": "VALID_CODE_ENTERED",
                              "timestamp": "2025-08-24T09:12:39.662157+00:00",
                              "details": {
                                "code_tried": "123456",
                                "status": "Approved"
                              },
                              "fee": 0
                            },
                            {
                              "type": "PHONE_VERIFICATION_APPROVED",
                              "timestamp": "2025-08-24T09:12:39.684292+00:00",
                              "details": null,
                              "fee": 0
                            }
                          ],
                          "warnings": [],
                          "matches": [
                            {
                              "session_id": null,
                              "session_number": null,
                              "vendor_data": null,
                              "verification_date": null,
                              "phone_number": "+34600000000",
                              "status": null,
                              "is_blocklisted": true,
                              "api_service": null,
                              "source": "list_entry"
                            }
                          ]
                        }
                      ],
                      "email_verifications": [
                        {
                          "node_id": "feature_email_1",
                          "status": "Approved",
                          "email": "test@example.com",
                          "is_breached": true,
                          "breaches": [
                            {
                              "name": "TAPAirPortugal",
                              "domain": "flytap.com",
                              "logo_path": "https://logos.haveibeenpwned.com/TAPAirPortugal.png",
                              "breach_date": "2022-08-25",
                              "description": "In August 2022, the Portuguese airline <a href=\"https://www.bleepingcomputer.com/news/security/ragnar-locker-ransomware-claims-attack-on-portugals-flag-airline/\" target=\"_blank\" rel=\"noopener\">TAP Air Portugal was the target of a ransomware attack perpetrated by the Ragnar Locker gang</a> who later leaked the compromised data via a public dark web site. Over 5M unique email addresses were exposed alongside other personal data including names, genders, DoBs, phone numbers and physical addresses.",
                              "is_verified": true,
                              "data_classes": [
                                "dates_of_birth",
                                "email_addresses",
                                "genders",
                                "names",
                                "nationalities",
                                "phone_numbers",
                                "physical_addresses",
                                "salutations",
                                "spoken_languages"
                              ],
                              "breach_emails_count": 6083479
                            }
                          ],
                          "is_disposable": false,
                          "is_undeliverable": false,
                          "verification_attempts": 1,
                          "verified_at": "2025-09-15T17:36:19.963451Z",
                          "warnings": [
                            {
                              "feature": "EMAIL",
                              "risk": "BREACHED_EMAIL_DETECTED",
                              "additional_data": null,
                              "log_type": "information",
                              "short_description": "Breached email detected",
                              "long_description": "This email address was found in one or more known data breaches.",
                              "node_id": "feature_email_1"
                            }
                          ],
                          "lifecycle": [
                            {
                              "type": "EMAIL_VERIFICATION_MESSAGE_SENT",
                              "timestamp": "2025-09-15T17:36:19.792684+00:00",
                              "details": {
                                "status": "Success",
                                "reason": null
                              },
                              "fee": 0.03
                            },
                            {
                              "type": "VALID_CODE_ENTERED",
                              "timestamp": "2025-09-15T17:36:19.963427+00:00",
                              "details": {
                                "code_tried": "123456",
                                "status": "Approved"
                              },
                              "fee": 0
                            },
                            {
                              "type": "EMAIL_VERIFICATION_DECLINED",
                              "timestamp": "2025-09-15T17:36:20.081471+00:00",
                              "details": {
                                "reason": "BREACHED_EMAIL_DETECTED"
                              },
                              "fee": 0
                            }
                          ],
                          "matches": [
                            {
                              "session_id": null,
                              "session_number": null,
                              "vendor_data": null,
                              "verification_date": null,
                              "email": "test@example.com",
                              "status": null,
                              "is_blocklisted": true,
                              "api_service": null,
                              "source": "list_entry"
                            }
                          ]
                        }
                      ],
                      "poa_verifications": [
                        {
                          "node_id": "feature_poa_1",
                          "status": "Declined",
                          "document_file": "https://<media-host>/poa_document.pdf",
                          "issuing_state": "AR",
                          "document_type": "UTILITY_BILL",
                          "document_subtype": null,
                          "document_language": "es",
                          "document_metadata": {
                            "file_size": 1000,
                            "content_type": "application/pdf",
                            "creation_date": "2021-01-01",
                            "modified_date": "2021-01-01",
                            "overlay_manipulation": null,
                            "creator": null,
                            "producer": "PDF Generator",
                            "software": null,
                            "encryption": null,
                            "is_signed": false,
                            "is_tampered": false,
                            "signature_info": null,
                            "exif_original_date": null,
                            "exif_digitized_date": null,
                            "processed_by_known_editor": false,
                            "has_different_creation_mod_date": true
                          },
                          "issuer": "Aguas del Norte",
                          "issue_date": "2021-02-01",
                          "expiration_date": null,
                          "poa_address": "AVDA. MONSEÑOR TAVELLA N° 3396, SALTA",
                          "poa_formatted_address": "Av. Monseñor Tavella 3396, A4400 Salta, Argentina",
                          "poa_parsed_address": {
                            "address_type": "Avenida",
                            "street_1": "Avenida Monseñor Tavella 3396",
                            "street_2": null,
                            "city": "Salta",
                            "region": "Salta",
                            "country": "AR",
                            "postal_code": "A4400",
                            "document_location": {
                              "latitude": -24.8208664,
                              "longitude": -65.4131
                            }
                          },
                          "name_on_document": "Fernando De Lima",
                          "name_match_score_expected_details": null,
                          "name_match_score_id_verification": 38.5,
                          "expected_details_address": null,
                          "expected_details_formatted_address": null,
                          "expected_details_parsed_address": {},
                          "extra_fields": {
                            "bank_account_number": "1234567890",
                            "bank_iban": null,
                            "bank_sort_code": null,
                            "bank_routing_number": null,
                            "bank_swift_bic": null,
                            "bank_branch_name": null,
                            "bank_branch_address": null,
                            "document_phone_number": "+5491112345678",
                            "additional_names": []
                          },
                          "extra_files": [
                            "https://<media-host>/extra_poa.pdf"
                          ],
                          "warnings": []
                        }
                      ],
                      "document_ai_documents": [
                        {
                          "status": "Approved",
                          "node_id": "feature_document_ai_1",
                          "items": [
                            {
                              "uuid": "c1a2b3c4-d5e6-7f80-9a1b-2c3d4e5f6071",
                              "node_id": "feature_document_ai_1",
                              "document_key": "proof_of_funds",
                              "original_filename": "bank_statement.pdf",
                              "file_size": 184320,
                              "file_url": "https://<media-host>/document_ai/proof_of_funds.pdf",
                              "status": "Approved",
                              "extracted_data": {
                                "account_holder": "John Doe",
                                "balance": 15230.55,
                                "currency": "EUR",
                                "statement_date": "2026-05-31"
                              },
                              "fields": [
                                {
                                  "key": "account_holder",
                                  "name": "Account holder",
                                  "instruction": "Extract the full name of the account holder.",
                                  "type": "text",
                                  "required": true
                                },
                                {
                                  "key": "balance",
                                  "name": "Closing balance",
                                  "instruction": "Extract the closing balance amount.",
                                  "type": "number",
                                  "required": true
                                }
                              ],
                              "document_metadata": {
                                "is_tampered": false
                              },
                              "cross_check_result": null,
                              "created_at": "2026-06-01T12:34:56Z"
                            }
                          ],
                          "warnings": []
                        }
                      ],
                      "questionnaire_responses": [
                        {
                          "node_id": "feature_questionnaire_1",
                          "questionnaire_id": "9f7485e0-b00b-4d56-9d3e-7859b68d1213",
                          "title": "Source of Funds",
                          "description": "Reply to this questionnaire to help us understand the source of funds.",
                          "languages": [
                            "en"
                          ],
                          "default_language": "en",
                          "is_active": true,
                          "is_simple_questionnaire": true,
                          "questionnaire_group_id": "2b6a9d40-13a8-4b1e-9a6c-3f1d2e4b5a60",
                          "version": 1,
                          "published_at": "2025-11-02T09:14:55.000000Z",
                          "sections": [
                            {
                              "title": null,
                              "description": null,
                              "items": [
                                {
                                  "uuid": "618636d9-7d5c-41ce-b8d0-1ab2a4be20db",
                                  "value": "question_1",
                                  "element_type": "DROPDOWN",
                                  "is_required": true,
                                  "title": "What is your primary source of funds?",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": [
                                    {
                                      "label": "Salary",
                                      "value": "salary"
                                    },
                                    {
                                      "label": "Business Income",
                                      "value": "business_income"
                                    },
                                    {
                                      "label": "Savings",
                                      "value": "savings"
                                    },
                                    {
                                      "label": "Investments",
                                      "value": "investments"
                                    },
                                    {
                                      "label": "Sale of Asset",
                                      "value": "sale_of_asset"
                                    },
                                    {
                                      "label": "Inheritance/Gift",
                                      "value": "inheritance_gift"
                                    },
                                    {
                                      "label": "Pension",
                                      "value": "pension"
                                    },
                                    {
                                      "label": "Crypto Proceeds",
                                      "value": "crypto_proceeds"
                                    },
                                    {
                                      "label": "Other",
                                      "value": "other",
                                      "requires_text_input": true
                                    }
                                  ],
                                  "max_files": 1,
                                  "answer": {
                                    "value": "salary"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "2d16b527-72be-42e0-8e96-b164b818386c",
                                  "value": "question_2",
                                  "element_type": "LONG_TEXT",
                                  "is_required": true,
                                  "title": "Provide details of the primary source.",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": null,
                                  "max_files": 1,
                                  "answer": {
                                    "value": "Test answer"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "a9239b7c-fbfb-4ea7-b7e4-c835cd4016e0",
                                  "value": "question_3",
                                  "element_type": "DROPDOWN",
                                  "is_required": true,
                                  "title": "What is your annual income range (pre-tax)?",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": [
                                    {
                                      "label": "Less than $25,000",
                                      "value": "<25k"
                                    },
                                    {
                                      "label": "$25,000 - $50,000",
                                      "value": "25k-50k"
                                    },
                                    {
                                      "label": "$50,001 - $100,000",
                                      "value": "50k-100k"
                                    },
                                    {
                                      "label": "$100,001 - $250,000",
                                      "value": "100k-250k"
                                    },
                                    {
                                      "label": "More than $250,000",
                                      "value": ">250k"
                                    }
                                  ],
                                  "max_files": 1,
                                  "answer": {
                                    "value": "<25k"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "93dede62-6d0c-4a7f-934d-42175eb94011",
                                  "value": "question_4",
                                  "element_type": "DROPDOWN",
                                  "is_required": true,
                                  "title": "What is your approximate net worth (range)?",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": [
                                    {
                                      "label": "Less than $50,000",
                                      "value": "<50k"
                                    },
                                    {
                                      "label": "$50,000 - $250,000",
                                      "value": "50k-250k"
                                    },
                                    {
                                      "label": "$250,001 - $1,000,000",
                                      "value": "250k-1m"
                                    },
                                    {
                                      "label": "$1,000,001 - $5,000,000",
                                      "value": "1m-5m"
                                    },
                                    {
                                      "label": "More than $5,000,000",
                                      "value": ">5m"
                                    }
                                  ],
                                  "max_files": 1,
                                  "answer": {
                                    "value": "<50k"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "2af60d6f-6961-4692-9ee7-24b3033610a6",
                                  "value": "question_5",
                                  "element_type": "LONG_TEXT",
                                  "is_required": true,
                                  "title": "For your initial deposit/funding, indicate the exact source and amount.",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": null,
                                  "max_files": 1,
                                  "answer": {
                                    "value": "Test answer"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "0ad9d1aa-736f-46cf-947b-9e1241b53e40",
                                  "value": "question_6",
                                  "element_type": "LONG_TEXT",
                                  "is_required": true,
                                  "title": "Breakdown of sources by percentage.",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": null,
                                  "max_files": 1,
                                  "answer": {
                                    "value": "Test answer"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "1bf8a2a3-14ee-48a8-bb3d-175481aae4aa",
                                  "value": "question_7",
                                  "element_type": "SINGLE_CHOICE",
                                  "is_required": true,
                                  "title": "Will any funds originate from third parties? If yes, please provide details.",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": [
                                    {
                                      "label": "Yes",
                                      "value": "yes",
                                      "requires_text_input": true
                                    },
                                    {
                                      "label": "No",
                                      "value": "no"
                                    }
                                  ],
                                  "max_files": 1,
                                  "answer": {
                                    "text": "Additional details",
                                    "value": "yes"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "e18d4dd3-b7bf-4883-ba18-d6046adbe313",
                                  "value": "question_8",
                                  "element_type": "SINGLE_CHOICE",
                                  "is_required": true,
                                  "title": "Do you hold or plan to use virtual assets (e.g., crypto) to fund this account? If yes, please provide details.",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": [
                                    {
                                      "label": "Yes",
                                      "value": "yes",
                                      "requires_text_input": true
                                    },
                                    {
                                      "label": "No",
                                      "value": "no"
                                    }
                                  ],
                                  "max_files": 1,
                                  "answer": {
                                    "text": "Additional details",
                                    "value": "yes"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "a90a073d-e098-404c-bbce-5130d8b12a13",
                                  "value": "question_9",
                                  "element_type": "FILE_UPLOAD",
                                  "is_required": true,
                                  "title": "Upload a proof of funds document",
                                  "description": "You can upload your three (3) most recent bank statements, three (3) most recent payslips, your most recent tax return, proof of company ownership, trading exchange statement, sale of property, or proof of inheritance.",
                                  "placeholder": null,
                                  "choices": null,
                                  "max_files": 3,
                                  "answer": {
                                    "files": [
                                      "/media/ocr/",
                                      "/media/ocr/"
                                    ]
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                },
                                {
                                  "uuid": "9503b486-7cb8-4c3b-a97d-4b64926a5a47",
                                  "value": "question_10",
                                  "element_type": "LONG_TEXT",
                                  "is_required": false,
                                  "title": "Would you like to add any details to help us expedite our review of the documents you submitted?",
                                  "description": null,
                                  "placeholder": null,
                                  "choices": null,
                                  "max_files": 1,
                                  "answer": {
                                    "value": "Test answer"
                                  },
                                  "required_if": null,
                                  "repeatable_config": null
                                }
                              ]
                            }
                          ],
                          "status": "Approved"
                        }
                      ],
                      "aml_screenings": [
                        {
                          "node_id": "feature_aml_1",
                          "status": "In Review",
                          "total_hits": 1,
                          "entity_type": "person",
                          "hits": [
                            {
                              "id": "cPtTGRKkyddAcAC4xgsLCm",
                              "url": "https://news.example.org/articles/sample",
                              "match": false,
                              "score": 0.73,
                              "target": null,
                              "caption": "Javier Ejemplo Modelo",
                              "datasets": [
                                "PEP"
                              ],
                              "features": null,
                              "rca_name": "",
                              "last_seen": "2025-06-13T00:00:00",
                              "risk_view": {
                                "crimes": {
                                  "score": 0,
                                  "weightage": 20,
                                  "risk_level": "Low",
                                  "risk_scores": {}
                                },
                                "countries": {
                                  "score": 0,
                                  "weightage": 30,
                                  "risk_level": "Low",
                                  "risk_scores": {}
                                },
                                "categories": {
                                  "score": 100,
                                  "weightage": 50,
                                  "risk_level": "High",
                                  "risk_scores": {
                                    "PEP": 100
                                  }
                                },
                                "custom_list": {}
                              },
                              "first_seen": "2025-01-18T00:00:00",
                              "properties": {
                                "name": [
                                  "Javier Ejemplo Modelo"
                                ],
                                "alias": [
                                  "Javier Ejemplo Modelo",
                                  "David Azagra"
                                ],
                                "notes": [
                                  "Spanish orchestra conductor"
                                ],
                                "title": null,
                                "gender": [
                                  "male"
                                ],
                                "height": null,
                                "topics": null,
                                "weight": null,
                                "address": null,
                                "country": null,
                                "website": null,
                                "eyeColor": null,
                                "keywords": null,
                                "lastName": [
                                  "Ejemplo",
                                  "Modelo"
                                ],
                                "position": null,
                                "religion": null,
                                "birthDate": [
                                  "1974"
                                ],
                                "education": [
                                  "Tokyo Arts and Space (2012-2012)",
                                  "Saint Petersburg Conservatory (-2010)",
                                  "Comillas Pontifical University"
                                ],
                                "ethnicity": null,
                                "firstName": [
                                  "David"
                                ],
                                "hairColor": null,
                                "weakAlias": null,
                                "birthPlace": [
                                  "Madrid"
                                ],
                                "modifiedAt": null,
                                "wikidataId": null,
                                "citizenship": null,
                                "nationality": null
                              },
                              "match_score": 98,
                              "risk_score": 73,
                              "review_status": "Unreviewed",
                              "score_breakdown": {
                                "name_score": 95,
                                "name_weight": 60,
                                "name_weight_normalized": 70.59,
                                "name_contribution": 67.06,
                                "dob_score": 100,
                                "dob_weight": 25,
                                "dob_weight_normalized": 29.41,
                                "dob_contribution": 29.41,
                                "country_score": 0,
                                "country_weight": 15,
                                "country_weight_normalized": 0,
                                "country_contribution": 0,
                                "document_number_match_type": "NEUTRAL",
                                "document_number_effect": "No document number provided for screening",
                                "total_score": 98
                              },
                              "pep_matches": [
                                {
                                  "aliases": [
                                    "Javier Ejemplo Modelo",
                                    "David Azagra"
                                  ],
                                  "education": [
                                    "Tokyo Arts and Space (2012-2012)",
                                    "Saint Petersburg Conservatory (-2010)",
                                    "Comillas Pontifical University"
                                  ],
                                  "list_name": "Wikidata",
                                  "publisher": "Wikidata",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "description": "Wikidata is the structured data project of the Wikipedia community, providing fact-based information edited by humans and machines",
                                  "matched_name": "Javier Ejemplo Modelo",
                                  "pep_position": "",
                                  "date_of_birth": "1974",
                                  "other_sources": [],
                                  "place_of_birth": "Madrid"
                                }
                              ],
                              "linked_entities": [
                                {
                                  "name": [
                                    "Antonio Ejemplo"
                                  ],
                                  "active": [],
                                  "status": [],
                                  "details": [],
                                  "relation": []
                                }
                              ],
                              "warning_matches": [],
                              "sanction_matches": [],
                              "adverse_media_details": {
                                "sentiment": "Moderately Negative",
                                "entity_type": "person",
                                "sentiment_score": -2,
                                "adverse_keywords": {
                                  "fraud": 3,
                                  "juicio": 1,
                                  "delitos": 2,
                                  "imputado": 1,
                                  "impunidad": 1,
                                  "corruption": 2,
                                  "favoritism": 2,
                                  "privilegio": 1,
                                  "corrupción": 1,
                                  "persecution": 2,
                                  "desconfianza": 1,
                                  "desprestigio": 1,
                                  "fraude de ley": 2,
                                  "manipulación": 1,
                                  "investigación": 3,
                                  "prevaricación": 8,
                                  "trato de favor": 1,
                                  "irregularidades": 2,
                                  "crisis de confianza": 1,
                                  "tráfico de influencias": 8
                                }
                              },
                              "adverse_media_matches": [
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo is implicated in legal violations related to favoritism and influence trafficking, leading to a negative portrayal in the article.",
                                  "headline": "La Diputación de Villaejemplo consolida la plaza a medida para el ex asesor de Moncloa amigo del \"hermanito\" de Ejemplo",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "fraud",
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "irregularidades",
                                    "imputado"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-10T20:46:38"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo is implicated in legal issues regarding favoritism and corruption, leading to a negative portrayal in the article.",
                                  "headline": "La Diputación de Villaejemplo consolida la plaza a medida para el ex asesor de Moncloa amigo del \"hermanito\" de Ejemplo",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "fraud",
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "irregularidades",
                                    "trato de favor"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-10T20:46:38"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo Modelo is mentioned in connection with Carlos Ficticio's corruption case, indicating his involvement in alleged influence peddling.",
                                  "headline": "España, paraíso del aforado",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "corrupción",
                                    "impunidad",
                                    "privilegio",
                                    "desconfianza",
                                    "manipulación",
                                    "fraude de ley"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-10T05:05:34"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo Modelo is implicated in the same investigation as Ficticio, which raises questions about his conduct and political ethics.",
                                  "headline": "Aforamientos: esa figura incómoda",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "investigación",
                                    "juicio",
                                    "fraude de ley",
                                    "desprestigio",
                                    "crisis de confianza",
                                    "delitos"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-08T06:01:00"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo is implicated in a legal investigation regarding irregularities in his hiring, which raises serious concerns about his conduct.",
                                  "headline": "La jueza del 'caso Javier Ejemplo' denuncia un «fraude de ley» en el aforamiento de Ficticio (Partido Ejemplo)",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "fraud",
                                    "irregularities",
                                    "prevaricación",
                                    "tráfico de influencias"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-09T10:19:10"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo Modelo is mentioned in connection with legal allegations of corruption, contributing to a negative perception.",
                                  "headline": "'Caso aforamiento': la letrada mayor de la Asamblea certifica a la juez que Ficticio es diputado desde antes de prometer su cargo en el Pleno",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "prevaricación",
                                    "tráfico de influencias"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-09T08:08:30"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo Modelo is implicated in the same investigation as Ficticio, raising concerns about political corruption and influence.",
                                  "headline": "Aforamientos: esa figura incómoda",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "investigación"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-08T06:01:00"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo Modelo is implicated in the same investigation as Ficticio, which raises concerns about political misconduct.",
                                  "headline": "Aforamientos: esa figura incómoda",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "prevaricación",
                                    "tráfico de influencias",
                                    "investigación",
                                    "delitos"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-08T06:00:59"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo is implicated in a case of favoritism and corruption, which casts a negative light on his character and actions.",
                                  "headline": "'Caso Javier Ejemplo': las asociaciones judiciales salen en defensa de la juez Luisa Inventada ante las \"presiones externas\" que sufre",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "favoritism",
                                    "corruption",
                                    "persecution"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-06T10:08:15"
                                },
                                {
                                  "country": "spain",
                                  "summary": "Javier Ejemplo is implicated in a case of alleged favoritism and corruption, which casts a negative light on his character and actions.",
                                  "headline": "'Caso Javier Ejemplo': las asociaciones judiciales salen en defensa de la juez Luisa Inventada ante las \"presiones externas\" que sufre",
                                  "sentiment": "moderately negative",
                                  "thumbnail": "https://news.example.org/articles/sample",
                                  "source_url": "https://news.example.org/articles/sample",
                                  "author_name": null,
                                  "other_sources": [],
                                  "adverse_keywords": [
                                    "favoritism",
                                    "corruption",
                                    "persecution"
                                  ],
                                  "sentiment_score": -2,
                                  "publication_date": "2025-06-06T08:08:15"
                                }
                              ],
                              "additional_information": {}
                            }
                          ],
                          "score": 80,
                          "screened_data": {
                            "full_name": "Javier Ejemplo Modelo",
                            "nationality": "ES",
                            "date_of_birth": "1974-01-01",
                            "document_number": null
                          },
                          "is_ongoing_monitoring_enabled": true,
                          "next_ongoing_monitoring_bill_date": "2026-07-24",
                          "warnings": [
                            {
                              "feature": "AML",
                              "risk": "POSSIBLE_MATCH_FOUND",
                              "additional_data": null,
                              "log_type": "warning",
                              "short_description": "Possible match found in AML screening",
                              "long_description": "The Anti-Money Laundering (AML) screening process identified potential matches with watchlists or high-risk databases, requiring further review.",
                              "node_id": "feature_aml_1"
                            }
                          ]
                        }
                      ],
                      "ip_analyses": [
                        {
                          "node_id": "feature_ip_1",
                          "status": "Approved",
                          "device_brand": "Apple",
                          "device_model": "iPhone",
                          "browser_family": "Mobile Safari",
                          "os_family": "iOS",
                          "platform": "mobile",
                          "device_fingerprint": "didit-fp-a1b2c3d4e5f6g7h8",
                          "ip_country": "Spain",
                          "ip_country_code": "ES",
                          "ip_state": "Barcelona",
                          "ip_city": "Barcelona",
                          "latitude": 41.4022,
                          "longitude": 2.1407,
                          "ip_address": "83.50.226.71",
                          "isp": null,
                          "organization": null,
                          "is_vpn_or_tor": false,
                          "is_data_center": false,
                          "time_zone": "Europe/Madrid",
                          "time_zone_offset": "+0100",
                          "ip": {
                            "location": {
                              "latitude": 40.2206327,
                              "longitude": 1.5770097
                            },
                            "distance_from_id_document": 23.4,
                            "distance_from_poa_document": 12.3
                          },
                          "id_document": {
                            "location": {
                              "latitude": 41.2706327,
                              "longitude": 1.9770097
                            },
                            "distance_from_ip": 23.4,
                            "distance_from_poa_document": 18.7
                          },
                          "poa_document": {
                            "location": {
                              "latitude": 41.2706327,
                              "longitude": 1.9770097
                            },
                            "distance_from_ip": 12.3,
                            "distance_from_id_document": 18.7
                          },
                          "warnings": [],
                          "matches": [
                            {
                              "session_id": "99999999-8888-7777-6666-555555555555",
                              "session_number": 41205,
                              "vendor_data": "22222222-2222-2222-2222-222222222222",
                              "verification_date": "2024-07-20T10:15:30Z",
                              "match_type": "ip_address",
                              "match_source": "ip_address",
                              "matched_value": "83.50.226.71",
                              "status": "Approved",
                              "is_blocklisted": false,
                              "api_service": null,
                              "source": "session",
                              "device_info": {
                                "device_brand": "Apple",
                                "device_model": "iPhone",
                                "browser_family": "Mobile Safari",
                                "os_family": "iOS",
                                "platform": "mobile",
                                "device_fingerprint": "didit-fp-f9e8d7c6b5a40312"
                              },
                              "location_info": {
                                "ip_address": "83.50.226.71",
                                "ip_country": "Spain",
                                "ip_country_code": "ES",
                                "ip_state": "Barcelona",
                                "ip_city": "Barcelona",
                                "is_vpn_or_tor": false,
                                "is_data_center": false
                              },
                              "confidence": 0,
                              "match_mode": "co_occurrence"
                            },
                            {
                              "session_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                              "session_number": 42830,
                              "vendor_data": "33333333-3333-3333-3333-333333333333",
                              "verification_date": "2024-06-30T18:22:05Z",
                              "match_type": "device_fingerprint",
                              "match_source": "recovered_high",
                              "matched_value": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
                              "status": "Declined",
                              "is_blocklisted": false,
                              "api_service": null,
                              "source": "session",
                              "device_info": {
                                "device_brand": "Samsung",
                                "device_model": "SM-G991B",
                                "browser_family": "Chrome Mobile",
                                "os_family": "Android",
                                "platform": "mobile",
                                "device_fingerprint": null
                              },
                              "location_info": {
                                "ip_address": "83.50.231.18",
                                "ip_country": "Spain",
                                "ip_country_code": "ES",
                                "ip_state": "Madrid",
                                "ip_city": "Madrid",
                                "is_vpn_or_tor": false,
                                "is_data_center": false
                              },
                              "confidence": 1,
                              "match_mode": "deterministic",
                              "recovery_similarity": 0.9874,
                              "tls_ja4_corroborated": true,
                              "recovery_gate_reason": "hardware_root_match"
                            }
                          ]
                        }
                      ],
                      "database_validations": [
                        {
                          "node_id": "feature_db_validation_1",
                          "issuing_state": "BRA",
                          "validation_type": "one_by_one",
                          "screened_data": {
                            "last_name": "De Lima",
                            "first_name": "Fernando",
                            "tax_number": "01234567890",
                            "date_of_birth": "1980-01-01",
                            "document_type": "ID",
                            "expiration_date": "2030-01-01"
                          },
                          "validations": [
                            {
                              "service_id": "bra_cpf",
                              "service_name": "Brazil - CPF status check",
                              "outcome_code": "MATCH",
                              "validation": {
                                "full_name": "full_match",
                                "date_of_birth": "full_match",
                                "identification_number": "full_match"
                              },
                              "source_data": {
                                "identification_number": "01234567890",
                                "first_name": "FERNANDO",
                                "last_name": "DE LIMA",
                                "date_of_birth": "1980-01-01",
                                "gender": "M",
                                "nationality": "BRA"
                              }
                            }
                          ],
                          "match_type": "full_match",
                          "status": "Approved",
                          "warnings": []
                        }
                      ],
                      "reviews": [
                        {
                          "user": "compliance@example.com",
                          "new_status": "Declined",
                          "comment": "Possible match found in AML screening",
                          "created_at": "2024-07-18T13:29:00.366811Z"
                        }
                      ],
                      "created_at": "2024-07-24T08:54:25.443172Z",
                      "expires_at": "2025-07-24T08:54:25.443172Z"
                    }
                  },
                  "BusinessExample": {
                    "summary": "Business (KYB) session example",
                    "description": "A `session_kind = \"business\"` decision: KYB sessions replace the KYC feature arrays with `registry_checks`, `document_verifications` and `key_people_checks`, while sharing `aml_screenings`, `phone_verifications`, `email_verifications`, `questionnaire_responses`, `ip_analyses`, `reviews` and the session envelope.",
                    "value": {
                      "session_id": "66666666-7777-8888-9999-000000000000",
                      "session_kind": "business",
                      "session_number": 31415,
                      "session_url": "https://verify.didit.me/session/business123",
                      "status": "In Review",
                      "environment": "live",
                      "workflow_id": "abababab-cdcd-efef-0101-232323232323",
                      "features": [
                        "KYB_REGISTRY",
                        "KYB_DOCUMENTS",
                        "KYB_KEY_PEOPLE"
                      ],
                      "vendor_data": "merchant-001234",
                      "metadata": null,
                      "callback": "https://example.com/verification/callback",
                      "registry_checks": [
                        {
                          "status": "Approved",
                          "node_id": "feature_kyb_registry_1",
                          "data_resolved": true,
                          "company": {
                            "uuid": "dddd4444-eeee-5555-ffff-666677778888",
                            "node_id": "feature_kyb_registry_1",
                            "status": "Approved",
                            "registry_status": "active",
                            "data_resolved": true,
                            "company_name": "ACME HOLDINGS LTD",
                            "registration_number": "12345678",
                            "country_code": "GB",
                            "region": null,
                            "company_type": "Private Limited Company",
                            "incorporation_date": "2015-03-12",
                            "registered_address": "1 Example Street, London, EC1A 1AA",
                            "tax_number": null,
                            "risk_level": null,
                            "verification_status": null,
                            "is_from_registry": true,
                            "fetch_status": "resolved",
                            "alternative_names": [],
                            "nature_of_business": "62012 - Business and domestic software development",
                            "registered_capital": "100 GBP",
                            "registered_capital_amount": "100.00",
                            "registered_capital_currency": "GBP",
                            "website": null,
                            "email": null,
                            "phone": null,
                            "legal_entity_identifier": null,
                            "location_of_registration": "England and Wales",
                            "financial_summary": null,
                            "officers": [
                              {
                                "uuid": "aaaa1111-bbbb-2222-cccc-333344445555",
                                "name": "Jane Smith",
                                "designation": "Director",
                                "role": "director",
                                "nationality": "British",
                                "is_active": true,
                                "kyc_status": "Approved",
                                "kyc_session_url": "https://verify.didit.me/session/abc123def456"
                              }
                            ],
                            "beneficial_owners": [
                              {
                                "uuid": "bbbb2222-cccc-3333-dddd-444455556666",
                                "name": "John Doe",
                                "first_name": "John",
                                "last_name": "Doe",
                                "entity_type": "person",
                                "roles": [
                                  "ubo",
                                  "shareholder"
                                ],
                                "ownership_min_shares": "50",
                                "ownership_max_shares": "75",
                                "is_active": true,
                                "kyc_status": "Pending",
                                "kyc_session_url": "https://verify.didit.me/session/def456ghi789",
                                "effective_ownership_percent": 75
                              }
                            ],
                            "addresses": [
                              {
                                "type": "registered",
                                "address": "1 Example Street, London, EC1A 1AA",
                                "country": "GB"
                              }
                            ],
                            "industries": [
                              {
                                "code": "62012",
                                "description": "Business and domestic software development"
                              }
                            ],
                            "accounts": null,
                            "registry_data": {
                              "addresses_detail": [
                                {
                                  "type": "registered",
                                  "address": "1 Example Street, London, EC1A 1AA",
                                  "country": "GB"
                                }
                              ],
                              "industries": [
                                {
                                  "code": "62012",
                                  "description": "Business and domestic software development"
                                }
                              ]
                            },
                            "user_provided_data": {
                              "company_name": "ACME HOLDINGS",
                              "registration_number": "12345678"
                            },
                            "confirmed_by_user_at": "2026-06-10T12:01:30.000000Z",
                            "last_console_edit_at": null,
                            "is_editable": false
                          },
                          "ownership_structure": {
                            "nodes": [
                              {
                                "id": "company",
                                "name": "ACME HOLDINGS LTD",
                                "type": "company"
                              },
                              {
                                "id": "ubo-1",
                                "name": "John Doe",
                                "type": "person",
                                "ownership_percent": 75
                              }
                            ]
                          },
                          "warnings": []
                        }
                      ],
                      "aml_screenings": null,
                      "document_verifications": [
                        {
                          "status": "In Review",
                          "node_id": "feature_kyb_documents_1",
                          "items": [
                            {
                              "uuid": "eeee5555-ffff-6666-0000-777788889999",
                              "document_type": "LEGAL_PRESENCE",
                              "document_subtype": "CERTIFICATE_OF_INCORPORATION",
                              "document_group": "LEGAL_PRESENCE",
                              "file_url": "https://<media-host>/kyb/certificate-of-incorporation.pdf",
                              "original_filename": "certificate_of_incorporation.pdf",
                              "file_size": 482133,
                              "status": "Approved",
                              "created_at": "2026-06-10T12:03:11.000000Z",
                              "cross_check_result": {
                                "critical": {
                                  "company_name": {
                                    "document": "ACME HOLDINGS LTD",
                                    "registry": "ACME HOLDINGS LTD",
                                    "score": 1,
                                    "match": true
                                  },
                                  "registration_number": {
                                    "document": "12345678",
                                    "registry": "12345678",
                                    "match": true
                                  }
                                },
                                "non_critical": {},
                                "people": {}
                              },
                              "document_metadata": {
                                "file_size": 482133,
                                "content_type": "application/pdf",
                                "creation_date": "2015-03-12T09:00:00",
                                "modified_date": "2015-03-12T09:00:00",
                                "creator": null,
                                "producer": null,
                                "software": null,
                                "encryption": null,
                                "is_signed": true,
                                "is_tampered": false,
                                "signature_info": null,
                                "exif_original_date": null,
                                "exif_digitized_date": null,
                                "processed_by_known_editor": false,
                                "has_different_creation_mod_date": false,
                                "overlay_manipulation": null
                              },
                              "description": null,
                              "is_requested": false,
                              "ocr_data": {
                                "company_name": "ACME HOLDINGS LTD",
                                "registration_number": "12345678",
                                "incorporation_date": "2015-03-12"
                              },
                              "is_console_upload": false,
                              "is_confirmed": true
                            },
                            {
                              "uuid": "ffff6666-0000-7777-1111-888899990000",
                              "document_type": "OWNERSHIP_STRUCTURE",
                              "document_subtype": null,
                              "document_group": "OWNERSHIP_STRUCTURE",
                              "file_url": null,
                              "original_filename": null,
                              "file_size": null,
                              "status": "Not Finished",
                              "created_at": "2026-06-10T15:40:02.000000Z",
                              "cross_check_result": null,
                              "document_metadata": null,
                              "description": "Please upload an up-to-date shareholder registry",
                              "is_requested": true,
                              "ocr_data": null,
                              "is_console_upload": false,
                              "is_confirmed": true
                            }
                          ],
                          "groups": {
                            "LEGAL_PRESENCE": {
                              "total": 1,
                              "approved": 1,
                              "pending": 0,
                              "declined": 0,
                              "in_review": 0,
                              "other": 0,
                              "missing": 0
                            },
                            "OWNERSHIP_STRUCTURE": {
                              "total": 1,
                              "approved": 0,
                              "pending": 0,
                              "declined": 0,
                              "in_review": 0,
                              "other": 0,
                              "missing": 1
                            }
                          },
                          "required_groups": [
                            "LEGAL_PRESENCE",
                            "OWNERSHIP_STRUCTURE"
                          ],
                          "warnings": []
                        }
                      ],
                      "key_people_checks": [
                        {
                          "status": "In Review",
                          "node_id": "feature_kyb_key_people_1",
                          "registry": {
                            "officers": [
                              {
                                "uuid": "aaaa1111-bbbb-2222-cccc-333344445555",
                                "name": "Jane Smith",
                                "designation": "Director",
                                "role": "director",
                                "nationality": "British",
                                "appointment_date": "2015-03-12",
                                "termination_date": null,
                                "is_active": true,
                                "address": "1 Example Street, London, EC1A 1AA",
                                "aml_status": "Approved",
                                "kyc_status": "Approved",
                                "kyc_session_url": "https://verify.didit.me/session/abc123def456",
                                "kyc_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb",
                                "verification_workflow_type": "kyc",
                                "verification_workflow_id": "12121212-3434-5656-7878-909090909090",
                                "verification_workflow_label": "Key People KYC",
                                "verification_workflow_features": [
                                  {
                                    "feature": "ID_VERIFICATION",
                                    "status": "Approved"
                                  },
                                  {
                                    "feature": "LIVENESS",
                                    "status": "Approved"
                                  },
                                  {
                                    "feature": "AML",
                                    "status": "Approved"
                                  }
                                ],
                                "didit_internal_id": "99999999-0000-1111-2222-333333333333",
                                "kyc_document_type": "P",
                                "kyc_nationality": "GBR",
                                "kyc_liveness_passed": true,
                                "kyc_verified_at": "2026-06-10T14:22:31.123456Z",
                                "kyc_aml_categories": {
                                  "sanctions": "clear",
                                  "pep": "clear",
                                  "adverse_media": "clear",
                                  "warnings": "clear"
                                },
                                "kyc_notification_sent_at": "2026-06-09T09:00:00.000000Z"
                              }
                            ],
                            "beneficial_owners": [
                              {
                                "uuid": "bbbb2222-cccc-3333-dddd-444455556666",
                                "name": "John Doe",
                                "first_name": "John",
                                "last_name": "Doe",
                                "date_of_birth": "1980-05-04",
                                "country_of_birth": null,
                                "email": null,
                                "company_name": null,
                                "registration_number": null,
                                "entity_type": "person",
                                "nationality": "British",
                                "designation": null,
                                "roles": [
                                  "ubo",
                                  "shareholder"
                                ],
                                "ownership_min_shares": "50",
                                "ownership_max_shares": "75",
                                "is_active": true,
                                "aml_status": null,
                                "kyc_status": "In Progress",
                                "kyc_session_url": "https://verify.didit.me/session/def456ghi789",
                                "kyc_session_id": "88888888-9999-aaaa-bbbb-cccccccccccc",
                                "verification_workflow_type": "kyc",
                                "verification_workflow_id": "12121212-3434-5656-7878-909090909090",
                                "verification_workflow_label": "Key People KYC",
                                "verification_workflow_features": [
                                  {
                                    "feature": "ID_VERIFICATION",
                                    "status": "Not Finished"
                                  },
                                  {
                                    "feature": "LIVENESS",
                                    "status": "Not Finished"
                                  },
                                  {
                                    "feature": "AML",
                                    "status": "Not Finished"
                                  }
                                ],
                                "didit_internal_id": null,
                                "kyc_document_type": null,
                                "kyc_nationality": null,
                                "kyc_liveness_passed": null,
                                "kyc_verified_at": null,
                                "kyc_aml_categories": null,
                                "kyc_notification_sent_at": null,
                                "child_beneficial_owners": [],
                                "effective_ownership_percent": 75
                              }
                            ]
                          },
                          "submitted": {
                            "parties": [
                              {
                                "uuid": "cccc3333-dddd-4444-eeee-555566667777",
                                "entity_type": "person",
                                "source": "USER",
                                "name": "John Doe",
                                "first_name": "John",
                                "last_name": "Doe",
                                "company_name": null,
                                "registration_number": null,
                                "email": "john.doe@example.com",
                                "nationality": "GBR",
                                "phone_number": "+447700900123",
                                "date_of_birth": "1980-05-04",
                                "position": "Chief Executive Officer",
                                "custom_fields": {},
                                "is_active": true,
                                "is_skipped": false,
                                "requires_verification": true,
                                "roles": [
                                  {
                                    "role": "ubo",
                                    "ownership_percent": 75,
                                    "voting_percent": 75
                                  },
                                  {
                                    "role": "director",
                                    "ownership_percent": null,
                                    "voting_percent": null
                                  }
                                ],
                                "kyc_session_id": "88888888-9999-aaaa-bbbb-cccccccccccc",
                                "kyc_session_status": "In Progress",
                                "kyc_session_url": "https://verify.didit.me/session/def456ghi789",
                                "kyb_sub_session_id": null,
                                "kyb_sub_session_status": null,
                                "kyb_sub_session_url": null,
                                "verification_workflow_type": "kyc",
                                "verification_workflow_id": "12121212-3434-5656-7878-909090909090",
                                "verification_workflow_label": "Key People KYC",
                                "verification_workflow_features": [
                                  {
                                    "feature": "ID_VERIFICATION",
                                    "status": "Not Finished"
                                  },
                                  {
                                    "feature": "LIVENESS",
                                    "status": "Not Finished"
                                  },
                                  {
                                    "feature": "AML",
                                    "status": "Not Finished"
                                  }
                                ],
                                "didit_internal_id": null
                              }
                            ]
                          },
                          "ubo_kyc_summary": {
                            "total": 1,
                            "approved": 0,
                            "flagged": 0,
                            "pending": 1
                          },
                          "warnings": []
                        }
                      ],
                      "phone_verifications": null,
                      "email_verifications": null,
                      "questionnaire_responses": null,
                      "ip_analyses": null,
                      "reviews": [],
                      "contact_details": null,
                      "expected_details": {
                        "company_name": "Tesco PLC",
                        "registry_country": "GB",
                        "registration_number": "00445790"
                      },
                      "created_at": "2026-06-10T11:58:04.000000Z",
                      "expires_at": "2026-06-24T11:58:04.000000Z"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The unique identifier of the session for which to retrieve verification results."
                    },
                    "session_kind": {
                      "type": "string",
                      "enum": [
                        "user",
                        "business"
                      ],
                      "description": "Discriminator indicating whether this is a User Verification (KYC) or Business Verification (KYB) session. When `user`, the KYC feature arrays (`id_verifications`, `nfc_verifications`, `liveness_checks`, `face_matches`, `poa_verifications`, `document_ai_documents`, `database_validations`, ...) apply and the business arrays are absent. When `business`, the payload instead carries `registry_checks`, `document_verifications`, `key_people_checks` plus the shared arrays (`aml_screenings`, `phone_verifications`, `email_verifications`, `ip_analyses`, `questionnaire_responses`). **Omitted when `include=events` is passed** — the events shape only exists for user sessions, so the discriminator is dropped from that variant."
                    },
                    "session_number": {
                      "type": "integer",
                      "description": "The number of the session.",
                      "nullable": true
                    },
                    "session_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The URL of the session.",
                      "nullable": true
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "Not Started",
                        "In Progress",
                        "Awaiting User",
                        "In Review",
                        "Approved",
                        "Declined",
                        "Resubmitted",
                        "Expired",
                        "Kyc Expired",
                        "Abandoned"
                      ],
                      "description": "Overall lifecycle status of the session — the rolled-up workflow result. Terminal values are `Approved`, `Declined`, `In Review`, `Expired`, `Kyc Expired`, and `Abandoned`; `Not Started`, `In Progress`, `Awaiting User`, and `Resubmitted` indicate the session is still moving through the workflow."
                    },
                    "workflow_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Stable identifier of the workflow group that produced this session. Workflows are versioned — this value identifies the workflow itself, not the specific version.",
                      "nullable": true
                    },
                    "features": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "description": "The feature of the session."
                      },
                      "description": "List of verification features configured for this session, as display names: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `PHONE`, `EMAIL_VERIFICATION`, `AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `QUESTIONNAIRE`, `AGE_ESTIMATION`, `FACE_SEARCH` (Business Verification sessions use the KYB feature set: `KYB_REGISTRY`, `KYB_DOCUMENTS`, `KYB_KEY_PEOPLE`, `AML`, `PHONE`, `EMAIL_VERIFICATION`, `IP_ANALYSIS`, `QUESTIONNAIRE`). With `include=events` each entry becomes an object `{feature, node_id}` instead of a plain string, and the email feature is renamed: it appears as `EMAIL` in events mode instead of the `EMAIL_VERIFICATION` used here."
                    },
                    "vendor_data": {
                      "type": "string",
                      "description": "The vendor data of the session.",
                      "nullable": true
                    },
                    "metadata": {
                      "type": "object",
                      "description": "The metadata of the session.",
                      "nullable": true
                    },
                    "expected_details": {
                      "type": "object",
                      "description": "Expected details supplied at session creation — used for cross-validation against extracted data (KYC) or to pre-fill the company registry search (KYB). Echoes back exactly the keys that were provided; `null` when none were supplied. KYC sessions carry the person fields; Business (KYB) sessions carry `company_name`, `registry_country` and `registration_number`.",
                      "properties": {
                        "first_name": {
                          "type": "string",
                          "description": "User's first name. For example, `John`."
                        },
                        "last_name": {
                          "type": "string",
                          "description": "User's last name. For example, `Doe`."
                        },
                        "date_of_birth": {
                          "type": "string",
                          "format": "date",
                          "description": "User's date of birth with format: YYYY-MM-DD. For example, `1990-05-15`."
                        },
                        "gender": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "M",
                            "F"
                          ],
                          "default": null,
                          "description": "User's gender. Must be either 'M', 'F', or null."
                        },
                        "nationality": {
                          "type": "string",
                          "description": "ISO 3166-1 alpha-3 country code representing the applicant's country of origin. For example, `USA`."
                        },
                        "country": {
                          "type": "string",
                          "description": "ISO 3166-1 alpha-3 country code representing the country of the Proof of Address document, or the country of the applicant's ID document, which may differ from nationality. For example, `GBR`."
                        },
                        "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`."
                        },
                        "identification_number": {
                          "type": "string",
                          "description": "The user's document number, personal number, or tax number. For example, `123456789`."
                        },
                        "ip_address": {
                          "type": "string",
                          "format": "ipv4",
                          "description": "Expected IP address for the session. If the actual IP address differs from this value, a warning will be logged. For example, `192.168.1.100`."
                        },
                        "expected_document_types": {
                          "type": "array",
                          "description": "Document types the ID verification step was restricted to at session creation. Empty/unset means no restriction. 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"
                            ]
                          }
                        },
                        "id_country": {
                          "type": "string",
                          "nullable": true,
                          "description": "Expected ID-document issuing country (ISO 3166-1 alpha-3), when restricted at creation time.",
                          "example": "ESP"
                        },
                        "poa_country": {
                          "type": "string",
                          "nullable": true,
                          "description": "Expected Proof of Address document country (ISO 3166-1 alpha-3), when restricted at creation time.",
                          "example": "ESP"
                        },
                        "company_name": {
                          "type": "string",
                          "description": "Expected legal name of the business. Business (KYB) sessions only."
                        },
                        "registry_country": {
                          "type": "string",
                          "description": "Expected registry country as `XX` or `XX-YY` (ISO 3166-1 alpha-2, optionally with an ISO 3166-2 subdivision for state-level registries, e.g. `US-CA`). Business (KYB) sessions only."
                        },
                        "registration_number": {
                          "type": "string",
                          "description": "Expected company registration number. Business (KYB) sessions only."
                        }
                      },
                      "nullable": true
                    },
                    "contact_details": {
                      "type": "object",
                      "description": "Contact details supplied when the session was created (used for notification emails, prefilled phone/email verification, and communication language). `null` when neither an email nor a phone number was provided.",
                      "properties": {
                        "email": {
                          "type": "string",
                          "description": "Email address of the user (e.g., \"john.doe@example.com\") that will be used during the Email Verification step. If not provided, the user must provide it during the verification flow."
                        },
                        "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\")."
                        },
                        "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."
                        },
                        "phone": {
                          "type": "string",
                          "nullable": true,
                          "description": "Phone number of the user in E.164 format if available (e.g., +14155552671). `null` when only an email was supplied at session creation. Note: This field returns the original phone number provided during session creation. If the number was invalid, it may not be in E.164 format."
                        }
                      },
                      "nullable": true
                    },
                    "callback": {
                      "type": "string",
                      "format": "uri",
                      "description": "The callback URL of the session.",
                      "nullable": true
                    },
                    "id_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of ID verification (OCR) reports** produced by ID-document steps in the workflow graph. Always a JSON array — read `id_verifications[0]` and iterate if your workflow can run more than one ID check (for example a primary check plus a step-up). `null` until at least one ID step has produced data. Each item is the full report, including `node_id`, feature-level `status`, extracted document fields, image URLs, quality scores, MRZ, address parsing, cross-session matches, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "description": "One ID verification report — the full report for a single OCR pass.",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Workflow graph node id of the OCR step that produced this report."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Expired"
                            ],
                            "description": "Feature-level status of this ID verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`."
                          },
                          "document_type": {
                            "type": "string",
                            "description": "Type of identity document"
                          },
                          "document_subtype": {
                            "type": "string",
                            "nullable": true,
                            "description": "Accepted document subtype code from the workflow document settings, including the document region when applicable. Examples: `ID_CARD_GENERIC`, `QUEENSLAND_DRIVER_LICENSE_GENERIC`."
                          },
                          "document_number": {
                            "type": "string",
                            "description": "Document identification number"
                          },
                          "personal_number": {
                            "type": "string",
                            "description": "Personal identification number"
                          },
                          "portrait_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the portrait image"
                          },
                          "front_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the front document image"
                          },
                          "front_video": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the front document video"
                          },
                          "back_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the back document image"
                          },
                          "back_video": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the back document video"
                          },
                          "full_front_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the full front document image"
                          },
                          "full_back_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the full back document image"
                          },
                          "front_image_camera_front": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the front document image captured from the front-facing camera"
                          },
                          "back_image_camera_front": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the back document image captured from the front-facing camera"
                          },
                          "date_of_birth": {
                            "type": "string",
                            "format": "date",
                            "description": "Date of birth from document"
                          },
                          "age": {
                            "type": "integer",
                            "description": "Calculated age from date of birth"
                          },
                          "expiration_date": {
                            "type": "string",
                            "format": "date",
                            "description": "Document expiration date"
                          },
                          "date_of_issue": {
                            "type": "string",
                            "format": "date",
                            "description": "Document issue date"
                          },
                          "issuing_state": {
                            "type": "string",
                            "description": "ISO code of issuing state"
                          },
                          "issuing_state_name": {
                            "type": "string",
                            "description": "Full name of issuing state"
                          },
                          "first_name": {
                            "type": "string",
                            "description": "First name from document"
                          },
                          "last_name": {
                            "type": "string",
                            "description": "Last name from document"
                          },
                          "full_name": {
                            "type": "string",
                            "description": "Full name from document"
                          },
                          "gender": {
                            "type": "string",
                            "description": "Gender from document"
                          },
                          "address": {
                            "type": "string",
                            "description": "Raw address from document"
                          },
                          "formatted_address": {
                            "type": "string",
                            "description": "Formatted full address"
                          },
                          "place_of_birth": {
                            "type": "string",
                            "description": "Place of birth from document"
                          },
                          "marital_status": {
                            "type": "string",
                            "description": "Marital status from document"
                          },
                          "nationality": {
                            "type": "string",
                            "description": "ISO nationality code"
                          },
                          "extra_fields": {
                            "type": "object",
                            "description": "Additional document fields. Includes document-specific attributes (e.g. `dl_categories`, `blood_group`, `sponsor`, `employer`, `dl_class_code_b_from`, `dl_class_code_b_to`). Driver license class validity dates are returned as `dl_class_code_<class>_from` and `dl_class_code_<class>_to`, and per-class restriction/information codes (e.g. column 12 of UK licences) as `dl_class_code_<class>_notes`. When the document carries names, address, or place of birth in multiple scripts, this object also includes alternate-script versions of those fields. The workflow setting `preferred_characters` (`latin` or `non_latin`) controls which script populates the top-level fields (`first_name`, `last_name`, `full_name`, `address`, `place_of_birth`); the opposite script lands here as `first_name_non_latin` / `last_name_non_latin` / `full_name_non_latin` / `middle_name_non_latin` / `address_non_latin` / `place_of_birth_non_latin` (when `preferred_characters` is `latin`) or with a `_latin` suffix (when `preferred_characters` is `non_latin`). Keys appear only when the OCR actually extracts a real alternate-script value — a field whose alternate value is in the same script as the preferred set (for example, a Cyrillic-labelled field whose OCR value came back in Latin homoglyphs) is skipped. When `preferred_characters` is `latin` but the document only carries non-Latin text, Didit transliterates the extracted values and exposes `first_name_latin` / `last_name_latin` / etc. as a fallback.",
                            "additionalProperties": true,
                            "properties": {
                              "dl_categories": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "blood_group": {
                                "type": "string",
                                "nullable": true
                              },
                              "first_name_non_latin": {
                                "type": "string",
                                "description": "First name in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document (e.g. `Айна` for a Kyrgyz ID)."
                              },
                              "last_name_non_latin": {
                                "type": "string",
                                "description": "Surname in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document."
                              },
                              "middle_name_non_latin": {
                                "type": "string",
                                "description": "Middle name / patronymic in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document."
                              },
                              "full_name_non_latin": {
                                "type": "string",
                                "description": "Composed full name in the non-Latin script. Reconstructed from the individual components when both first and last names are available."
                              },
                              "address_non_latin": {
                                "type": "string",
                                "description": "Address in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document."
                              },
                              "place_of_birth_non_latin": {
                                "type": "string",
                                "description": "Place of birth in the non-Latin script when `preferred_characters=latin` and a non-Latin version is present on the document."
                              },
                              "first_name_latin": {
                                "type": "string",
                                "description": "First name in Latin script. Populated when `preferred_characters=non_latin` and a Latin version is present on the document, or — as a transliteration fallback — when `preferred_characters=latin` but the document only carries non-Latin text."
                              },
                              "last_name_latin": {
                                "type": "string",
                                "description": "Surname in Latin script. Same rules as `first_name_latin`."
                              },
                              "middle_name_latin": {
                                "type": "string",
                                "description": "Middle name / patronymic in Latin script. Same rules as `first_name_latin`."
                              },
                              "full_name_latin": {
                                "type": "string",
                                "description": "Composed full name in Latin script. Always reconstructed from the individual components when both first and last names are available."
                              },
                              "address_latin": {
                                "type": "string",
                                "description": "Address in Latin script. Same rules as `first_name_latin`."
                              },
                              "place_of_birth_latin": {
                                "type": "string",
                                "description": "Place of birth in Latin script. Same rules as `first_name_latin`."
                              }
                            }
                          },
                          "mrz": {
                            "type": "object",
                            "nullable": true,
                            "description": "Parsed Machine-Readable Zone (MRZ) fields extracted from the document. `null` if no MRZ was detected.",
                            "properties": {
                              "surname": {
                                "type": "string",
                                "example": "MARTINEZ<SANCHEZ"
                              },
                              "name": {
                                "type": "string",
                                "example": "ELENA"
                              },
                              "country": {
                                "type": "string",
                                "example": "ESP"
                              },
                              "nationality": {
                                "type": "string",
                                "example": "ESP"
                              },
                              "birth_date": {
                                "type": "string",
                                "example": "850315"
                              },
                              "expiry_date": {
                                "type": "string",
                                "example": "220821"
                              },
                              "sex": {
                                "type": "string",
                                "example": "F"
                              },
                              "document_type": {
                                "type": "string",
                                "example": "ID"
                              },
                              "document_number": {
                                "type": "string",
                                "example": "YZA123456"
                              },
                              "optional_data": {
                                "type": "string",
                                "example": "X9876543L"
                              },
                              "optional_data_2": {
                                "type": "string",
                                "example": ""
                              },
                              "birth_date_hash": {
                                "type": "string",
                                "example": "7"
                              },
                              "expiry_date_hash": {
                                "type": "string",
                                "example": "4"
                              },
                              "document_number_hash": {
                                "type": "string",
                                "example": "9"
                              },
                              "final_hash": {
                                "type": "string",
                                "example": "2"
                              },
                              "personal_number": {
                                "type": "string",
                                "example": "X9876543L"
                              },
                              "warnings": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "errors": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "mrz_type": {
                                "type": "string",
                                "example": "TD1"
                              },
                              "mrz_string": {
                                "type": "string",
                                "example": "IDESPMARTINEZ<SANCHEZ<<<<<<<<<<\nYZA123456<9ESP8503157F3208214<X9876543L<<<<<<<2\nMARTINEZ<SANCHEZ<<ELENA<<<<<<<<<<"
                              },
                              "mrz_key": {
                                "type": "string",
                                "example": "YZA123456985031573208214"
                              }
                            }
                          },
                          "parsed_address": {
                            "type": "object",
                            "description": "Structured address details",
                            "properties": {
                              "city": {
                                "type": "string"
                              },
                              "label": {
                                "type": "string"
                              },
                              "region": {
                                "type": "string"
                              },
                              "country": {
                                "type": "string",
                                "description": "Two-letter country code"
                              },
                              "category": {
                                "type": "string",
                                "description": "Address category (e.g., Residential, Commercial)"
                              },
                              "street_1": {
                                "type": "string"
                              },
                              "street_2": {
                                "type": "string",
                                "nullable": true
                              },
                              "is_verified": {
                                "type": "boolean",
                                "description": "Whether the address has been verified"
                              },
                              "postal_code": {
                                "type": "string"
                              },
                              "raw_results": {
                                "type": "object",
                                "properties": {
                                  "types": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    }
                                  },
                                  "geometry": {
                                    "type": "object",
                                    "properties": {
                                      "location": {
                                        "type": "object",
                                        "properties": {
                                          "lat": {
                                            "type": "number"
                                          },
                                          "lng": {
                                            "type": "number"
                                          }
                                        }
                                      },
                                      "viewport": {
                                        "type": "object",
                                        "properties": {
                                          "northeast": {
                                            "type": "object",
                                            "properties": {
                                              "lat": {
                                                "type": "number"
                                              },
                                              "lng": {
                                                "type": "number"
                                              }
                                            }
                                          },
                                          "southwest": {
                                            "type": "object",
                                            "properties": {
                                              "lat": {
                                                "type": "number"
                                              },
                                              "lng": {
                                                "type": "number"
                                              }
                                            }
                                          }
                                        }
                                      },
                                      "location_type": {
                                        "type": "string"
                                      }
                                    }
                                  },
                                  "place_id": {
                                    "type": "string"
                                  },
                                  "formatted_address": {
                                    "type": "string"
                                  },
                                  "navigation_points": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "properties": {
                                        "location": {
                                          "type": "object",
                                          "properties": {
                                            "latitude": {
                                              "type": "number"
                                            },
                                            "longitude": {
                                              "type": "number"
                                            }
                                          }
                                        }
                                      }
                                    }
                                  },
                                  "address_components": {
                                    "type": "array",
                                    "items": {
                                      "type": "object",
                                      "properties": {
                                        "types": {
                                          "type": "array",
                                          "items": {
                                            "type": "string"
                                          }
                                        },
                                        "long_name": {
                                          "type": "string"
                                        },
                                        "short_name": {
                                          "type": "string"
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "address_type": {
                                "type": "string"
                              },
                              "document_location": {
                                "type": "object",
                                "properties": {
                                  "latitude": {
                                    "type": "number"
                                  },
                                  "longitude": {
                                    "type": "number"
                                  }
                                }
                              },
                              "formatted_address": {
                                "type": "string"
                              }
                            }
                          },
                          "extra_files": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "uri"
                            },
                            "description": "Additional document verification files"
                          },
                          "front_image_camera_front_face_match_score": {
                            "type": "number",
                            "nullable": true,
                            "description": "Face match score between the portrait extracted from the front document image and the front camera image"
                          },
                          "back_image_camera_front_face_match_score": {
                            "type": "number",
                            "nullable": true,
                            "description": "Face match score between the portrait extracted from the back document image and the front camera image"
                          },
                          "front_image_quality_score": {
                            "type": "object",
                            "nullable": true,
                            "description": "Quality metrics for the front document image. Each sub-score ranges from 0 to 100.",
                            "properties": {
                              "focus_score": {
                                "type": "number",
                                "description": "Sharpness of the image measured via top-10% local Laplacian variance. Robust to documents with large uniform backgrounds. Higher values indicate a sharper, less blurry image."
                              },
                              "brightness_score": {
                                "type": "number",
                                "description": "How well-lit the image is. Penalizes images that are too dark or overexposed, with ideal brightness around mid-range."
                              },
                              "brightness_issue": {
                                "type": "string",
                                "enum": [
                                  "ok",
                                  "too_dark",
                                  "too_bright"
                                ],
                                "description": "Indicates the direction of the brightness problem when the brightness_score is low. 'ok' means brightness is acceptable, 'too_dark' means the image is underexposed, 'too_bright' means the image is overexposed."
                              },
                              "is_document_fully_visible": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "true if all four corners of the document are fully visible inside the image frame, false if any corner is cut off. Null when corner coordinates are unavailable."
                              },
                              "resolution_score": {
                                "type": "number",
                                "description": "Effective resolution of the image based on total pixel count."
                              },
                              "overall_score": {
                                "type": "number",
                                "description": "Weighted composite of sub-scores (focus 45%, brightness 30%, resolution 25%)."
                              }
                            }
                          },
                          "back_image_quality_score": {
                            "type": "object",
                            "nullable": true,
                            "description": "Quality metrics for the back document image. Same structure as front_image_quality_score.",
                            "properties": {
                              "focus_score": {
                                "type": "number",
                                "description": "Sharpness of the image measured via top-10% local Laplacian variance. Robust to documents with large uniform backgrounds. Higher values indicate a sharper, less blurry image."
                              },
                              "brightness_score": {
                                "type": "number",
                                "description": "How well-lit the image is. Penalizes images that are too dark or overexposed, with ideal brightness around mid-range."
                              },
                              "brightness_issue": {
                                "type": "string",
                                "enum": [
                                  "ok",
                                  "too_dark",
                                  "too_bright"
                                ],
                                "description": "Indicates the direction of the brightness problem when the brightness_score is low. 'ok' means brightness is acceptable, 'too_dark' means the image is underexposed, 'too_bright' means the image is overexposed."
                              },
                              "is_document_fully_visible": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "true if all four corners of the document are fully visible inside the image frame, false if any corner is cut off. Null when corner coordinates are unavailable."
                              },
                              "resolution_score": {
                                "type": "number",
                                "description": "Effective resolution of the image based on total pixel count."
                              },
                              "overall_score": {
                                "type": "number",
                                "description": "Weighted composite of sub-scores (focus 45%, brightness 30%, resolution 25%)."
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning (e.g., `ID_VERIFICATION`, `NFC`, `LIVENESS`). The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module, but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Additional contextual data about the warning"
                                },
                                "log_type": {
                                  "type": "string",
                                  "description": "Type of log: information, warning, or error"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning (for graph-based workflows)"
                                }
                              }
                            },
                            "description": "Document verification warnings"
                          },
                          "matches": {
                            "type": "array",
                            "description": "Matching documents found during duplicate/blocklist detection (up to 5 matches)",
                            "items": {
                              "type": "object",
                              "properties": {
                                "session_id": {
                                  "type": "string",
                                  "format": "uuid",
                                  "description": "Session ID of the matching document"
                                },
                                "session_number": {
                                  "type": "integer",
                                  "description": "Session number of the matching document"
                                },
                                "vendor_data": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Vendor data associated with the matching session"
                                },
                                "verification_date": {
                                  "type": "string",
                                  "format": "date-time",
                                  "description": "Date when the matching session was verified"
                                },
                                "user_details": {
                                  "type": "object",
                                  "properties": {
                                    "name": {
                                      "type": "string",
                                      "description": "Full name from the matching document"
                                    },
                                    "document_type": {
                                      "type": "string",
                                      "description": "Type of the matching document"
                                    },
                                    "document_number": {
                                      "type": "string",
                                      "description": "Document number of the matching document"
                                    }
                                  }
                                },
                                "status": {
                                  "type": "string",
                                  "description": "Status of the matching session"
                                },
                                "is_blocklisted": {
                                  "type": "boolean",
                                  "description": "Whether the matching document is blocklisted"
                                },
                                "api_service": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "API service type of the matching session"
                                },
                                "front_image_url": {
                                  "type": "string",
                                  "format": "uri",
                                  "nullable": true,
                                  "description": "URL of the front document image from the matching document"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "nfc_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of NFC / ePassport reports** produced by NFC steps in the workflow graph. **This is always a JSON array, never a singular `nfc` object** — read `nfc_verifications[0]` and iterate if your workflow can run NFC more than once. `null` until at least one NFC step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `chip_data`, `authenticity` (SOD + DG integrity), `certificate_summary`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "description": "One NFC report — the full report for a single ePassport chip read.",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Workflow graph node id of the NFC step that produced this report. Use it to correlate the report with the workflow definition."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ],
                            "description": "Feature-level status of this NFC step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`."
                          },
                          "portrait_image": {
                            "type": "string",
                            "format": "uri"
                          },
                          "signature_image": {
                            "type": "string",
                            "format": "uri"
                          },
                          "chip_data": {
                            "type": "object",
                            "properties": {
                              "document_type": {
                                "type": "string"
                              },
                              "issuing_country": {
                                "type": "string"
                              },
                              "document_number": {
                                "type": "string"
                              },
                              "expiration_date": {
                                "type": "string",
                                "format": "date"
                              },
                              "first_name": {
                                "type": "string"
                              },
                              "last_name": {
                                "type": "string"
                              },
                              "birth_date": {
                                "type": "string",
                                "format": "date"
                              },
                              "gender": {
                                "type": "string"
                              },
                              "nationality": {
                                "type": "string"
                              },
                              "address": {
                                "type": "string"
                              },
                              "place_of_birth": {
                                "type": "string"
                              }
                            }
                          },
                          "authenticity": {
                            "type": "object",
                            "properties": {
                              "sod_integrity": {
                                "type": "boolean"
                              },
                              "dg_integrity": {
                                "type": "boolean"
                              }
                            }
                          },
                          "certificate_summary": {
                            "type": "object",
                            "properties": {
                              "issuer": {
                                "type": "string"
                              },
                              "subject": {
                                "type": "string"
                              },
                              "serial_number": {
                                "type": "string"
                              },
                              "not_valid_after": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "not_valid_before": {
                                "type": "string",
                                "format": "date-time"
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "liveness_checks": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of liveness reports** produced by liveness steps in the workflow graph. Always a JSON array — never a singular `liveness` object. `null` until at least one liveness step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `method`, `score`, `reference_image`, `video_url`, optional `age_estimation`, cross-session `matches[]`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "description": "One liveness report — the full report for a single liveness pass.",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Workflow graph node id of the liveness step that produced this report."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ],
                            "description": "Feature-level status of this liveness step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`."
                          },
                          "method": {
                            "type": "string",
                            "description": "Method used for liveness detection"
                          },
                          "score": {
                            "type": "number",
                            "description": "Confidence score of the liveness check"
                          },
                          "reference_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the reference image used"
                          },
                          "video_url": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the liveness check video"
                          },
                          "age_estimation": {
                            "type": "number",
                            "description": "Estimated age of the person"
                          },
                          "matches": {
                            "type": "array",
                            "description": "Similar faces found by the duplicate-face search (top matches, blocklisted/allowlisted entries first). Each entry's `source` tells you where the matching face came from: `session` (another verification session), `imported` (a face uploaded against a vendor user), or `list_entry` (an org-managed allow/block list face). `session_id`, `session_number`, `status` and `api_service` are `null` for non-session sources. `imported` matches still populate `vendor_data`, `verification_date` and — when the vendor user has a name on file — `user_details` (with `full_name` only) from the vendor-user record; `list_entry` matches return all of these as `null`.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "session_id": {
                                  "type": "string",
                                  "format": "uuid",
                                  "nullable": true,
                                  "description": "Session ID of the matching face. `null` when `source` is `imported` or `list_entry`."
                                },
                                "session_number": {
                                  "type": "integer",
                                  "nullable": true
                                },
                                "similarity_percentage": {
                                  "type": "number",
                                  "description": "Face similarity between the two images, clamped to 0–100."
                                },
                                "vendor_data": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "verification_date": {
                                  "type": "string",
                                  "format": "date-time",
                                  "nullable": true
                                },
                                "user_details": {
                                  "type": "object",
                                  "nullable": true,
                                  "properties": {
                                    "full_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "document_type": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "document_number": {
                                      "type": "string",
                                      "nullable": true
                                    }
                                  }
                                },
                                "match_image_url": {
                                  "type": "string",
                                  "format": "uri",
                                  "nullable": true
                                },
                                "status": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Lifecycle status of the matching session. `null` for non-session sources."
                                },
                                "is_blocklisted": {
                                  "type": "boolean",
                                  "description": "Whether the matching face is on a blocklist."
                                },
                                "is_allowlisted": {
                                  "type": "boolean",
                                  "description": "Whether the matching face is on an allowlist."
                                },
                                "api_service": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Standalone API service of the matching session (e.g. `PASSIVE_LIVENESS`, `FACE_MATCH`); `null` for workflow sessions and non-session sources."
                                },
                                "source": {
                                  "type": "string",
                                  "enum": [
                                    "session",
                                    "imported",
                                    "list_entry"
                                  ],
                                  "description": "Origin of the matching face: `session`, `imported` (vendor-user face upload), or `list_entry` (allow/block list)."
                                }
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          },
                          "face_quality": {
                            "type": "number",
                            "nullable": true,
                            "description": "Face image quality score (0-100%). Only available for passive liveness."
                          },
                          "face_luminance": {
                            "type": "number",
                            "nullable": true,
                            "description": "Face luminance/brightness score (0-100%). Only available for passive liveness."
                          }
                        }
                      }
                    },
                    "face_matches": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of face-match reports** produced by face-match steps in the workflow graph. Always a JSON array — never a singular `face_match` object. `null` until at least one face-match step has produced data. Each item is the full report, including `node_id`, feature-level `status`, similarity `score`, `source_image`, `target_image`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "description": "One face-match report — the full report.",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Workflow graph node id of the face-match step that produced this report."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ],
                            "description": "Feature-level status of this face-match step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`."
                          },
                          "score": {
                            "type": "number",
                            "description": "Confidence score of the face match"
                          },
                          "source_image_session_id": {
                            "type": "string",
                            "format": "uuid",
                            "nullable": true,
                            "description": "Session id under which the source (reference) portrait is stored. In biometric-authentication flows this is the session's own id (the submitted portrait is stored under the new session). `null` when the source image is the same session's document portrait."
                          },
                          "source_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the source image used for comparison"
                          },
                          "target_image": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the target image used for comparison"
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            },
                            "description": "Face match verification warnings"
                          }
                        }
                      }
                    },
                    "phone_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of phone verification reports** — always a JSON array, never a singular `phone` object. `null` until at least one phone step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the phone number and country metadata, `lifecycle[]`, `matches[]`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this phone step in the workflow graph."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this phone verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Expired"
                            ]
                          },
                          "phone_number_prefix": {
                            "type": "string",
                            "description": "Country calling code prefix"
                          },
                          "phone_number": {
                            "type": "string",
                            "description": "Phone number without country prefix"
                          },
                          "full_number": {
                            "type": "string",
                            "description": "Complete phone number with country prefix"
                          },
                          "country_code": {
                            "type": "string",
                            "description": "Two-letter country code (ISO 3166-1 alpha-2)"
                          },
                          "country_name": {
                            "type": "string",
                            "description": "Full country name"
                          },
                          "carrier": {
                            "type": "object",
                            "properties": {
                              "name": {
                                "type": "string",
                                "description": "Name of the phone carrier"
                              },
                              "type": {
                                "type": "string",
                                "description": "Type of phone line (mobile, landline, etc)"
                              }
                            }
                          },
                          "is_disposable": {
                            "type": "boolean",
                            "description": "Whether the phone number is from a disposable service"
                          },
                          "is_virtual": {
                            "type": "boolean",
                            "description": "Whether the phone number is virtual"
                          },
                          "verification_method": {
                            "type": "string",
                            "description": "Method used to verify the phone number"
                          },
                          "verification_attempts": {
                            "type": "integer",
                            "description": "Number of verification attempts made"
                          },
                          "verified_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Timestamp when the phone was verified"
                          },
                          "lifecycle": {
                            "type": "array",
                            "description": "Chronological list of events in the phone verification lifecycle.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string"
                                },
                                "timestamp": {
                                  "type": "string",
                                  "format": "date-time"
                                },
                                "details": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Event-specific payload. Send events carry `status`, `reason`, `channel`, `actual_channel`; delivery events carry `channel`, `status`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `PHONE_VERIFICATION_APPROVED`).",
                                  "properties": {
                                    "status": {
                                      "type": "string"
                                    },
                                    "reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "channel": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "actual_channel": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "code_tried": {
                                      "type": "string"
                                    }
                                  }
                                },
                                "fee": {
                                  "type": "number"
                                }
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "description": "List of warnings related to phone verification",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          },
                          "matches": {
                            "type": "array",
                            "description": "Matches found for this phone number. Each match has a `source`: `session` (another verification session in your application matched on the same number, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the number is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "session_id": {
                                  "type": "string",
                                  "format": "uuid",
                                  "nullable": true
                                },
                                "session_number": {
                                  "type": "integer",
                                  "nullable": true
                                },
                                "vendor_data": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "verification_date": {
                                  "type": "string",
                                  "format": "date-time",
                                  "nullable": true
                                },
                                "phone_number": {
                                  "type": "string"
                                },
                                "status": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "is_blocklisted": {
                                  "type": "boolean"
                                },
                                "api_service": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "source": {
                                  "type": "string",
                                  "enum": [
                                    "session",
                                    "list_entry"
                                  ],
                                  "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "email_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of email verification reports** — always a JSON array, never a singular `email` object. `null` until at least one email step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the email address, breach data, `lifecycle[]`, `matches[]`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this email step in the workflow graph."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this email verification step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Expired"
                            ]
                          },
                          "email": {
                            "type": "string",
                            "description": "The email address being verified"
                          },
                          "is_breached": {
                            "type": "boolean",
                            "description": "Indicates if the email was found in known data breaches"
                          },
                          "breaches": {
                            "type": "array",
                            "description": "List of known breaches this email was found in",
                            "items": {
                              "type": "object",
                              "properties": {
                                "name": {
                                  "type": "string",
                                  "description": "Name of the breach"
                                },
                                "domain": {
                                  "type": "string",
                                  "description": "Domain affected by the breach"
                                },
                                "logo_path": {
                                  "type": "string",
                                  "description": "URL to the logo of the breached service"
                                },
                                "breach_date": {
                                  "type": "string",
                                  "format": "date",
                                  "description": "Date when the breach occurred"
                                },
                                "description": {
                                  "type": "string",
                                  "description": "Description of the breach"
                                },
                                "is_verified": {
                                  "type": "boolean",
                                  "description": "Whether the breach is verified"
                                },
                                "data_classes": {
                                  "type": "array",
                                  "description": "Types of data exposed in the breach",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "breach_emails_count": {
                                  "type": "integer",
                                  "description": "Number of email addresses affected in the breach"
                                }
                              }
                            }
                          },
                          "is_disposable": {
                            "type": "boolean",
                            "description": "Indicates if the email is from a disposable provider"
                          },
                          "is_undeliverable": {
                            "type": "boolean",
                            "description": "Indicates if the email address is undeliverable"
                          },
                          "verification_attempts": {
                            "type": "integer",
                            "description": "Number of verification attempts for this email"
                          },
                          "verified_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Timestamp when the email was verified"
                          },
                          "warnings": {
                            "type": "array",
                            "description": "List of warnings related to email verification",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          },
                          "lifecycle": {
                            "type": "array",
                            "description": "Chronological list of events in the email verification lifecycle.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string"
                                },
                                "timestamp": {
                                  "type": "string",
                                  "format": "date-time"
                                },
                                "details": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Event-specific payload. Send events carry `status`, `reason`; code-check events carry `code_tried`, `status`; final status events carry `reason` — or `null` when there is no reason to report (e.g. `EMAIL_VERIFICATION_APPROVED`).",
                                  "properties": {
                                    "status": {
                                      "type": "string"
                                    },
                                    "reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "code_tried": {
                                      "type": "string"
                                    }
                                  }
                                },
                                "fee": {
                                  "type": "number"
                                }
                              }
                            }
                          },
                          "matches": {
                            "type": "array",
                            "description": "Matches found for this email address. Each match has a `source`: `session` (another verification session in your application matched on the same email, returned with `session_id`/`session_number`/`vendor_data`/`verification_date`) or `list_entry` (the email is on an org-managed allow/block list — see `/management-api/lists/overview`). Cross-session matches use different `vendor_data` and are capped at 5.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "session_id": {
                                  "type": "string",
                                  "format": "uuid",
                                  "nullable": true
                                },
                                "session_number": {
                                  "type": "integer",
                                  "nullable": true
                                },
                                "vendor_data": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "verification_date": {
                                  "type": "string",
                                  "format": "date-time",
                                  "nullable": true
                                },
                                "email": {
                                  "type": "string",
                                  "format": "email"
                                },
                                "status": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "is_blocklisted": {
                                  "type": "boolean"
                                },
                                "api_service": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "source": {
                                  "type": "string",
                                  "enum": [
                                    "session",
                                    "list_entry"
                                  ],
                                  "description": "Where this match came from. `session` means another verification session in your application matched on this value. `list_entry` means the value was hit against an org-managed list (allowlist/blocklist) — see the management API lists overview at `/management-api/lists/overview` for how to manage those lists."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "poa_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of Proof of Address (POA) reports** — always a JSON array, never a singular `poa` object. `null` until at least one POA step has produced data. Each item is the full report, including `node_id`, feature-level `status`, the document file URL, issuer, dates, parsed address, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this POA step in the workflow graph."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this Proof of Address step. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ]
                          },
                          "document_file": {
                            "type": "string",
                            "format": "uri",
                            "description": "URL of the proof of address document"
                          },
                          "issuing_state": {
                            "type": "string",
                            "description": "Two-letter country code of the issuing state"
                          },
                          "document_type": {
                            "type": "string",
                            "description": "Type of proof of address document"
                          },
                          "document_subtype": {
                            "type": "string",
                            "nullable": true,
                            "description": "More specific classification of the POA document within its `document_type`, when one was detected. `null` otherwise."
                          },
                          "document_language": {
                            "type": "string",
                            "description": "Language code of the document"
                          },
                          "document_metadata": {
                            "type": "object",
                            "properties": {
                              "file_size": {
                                "type": "integer",
                                "description": "Size of the document"
                              },
                              "content_type": {
                                "type": "string",
                                "description": "Content type of the document"
                              },
                              "creation_date": {
                                "type": "string",
                                "format": "date",
                                "description": "Date when the document was created"
                              },
                              "modified_date": {
                                "type": "string",
                                "format": "date",
                                "description": "Date when the document was modified"
                              },
                              "overlay_manipulation": {
                                "type": "object",
                                "nullable": true,
                                "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.",
                                "properties": {
                                  "detected": {
                                    "type": "boolean",
                                    "description": "Whether overlay-text manipulation evidence was detected."
                                  },
                                  "analyzed": {
                                    "type": "boolean",
                                    "description": "Whether the PDF could be analyzed."
                                  },
                                  "signals": {
                                    "type": "array",
                                    "items": {
                                      "type": "string"
                                    },
                                    "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation."
                                  },
                                  "manipulated_regions": {
                                    "type": "array",
                                    "description": "Suspected manipulated areas in PDF page coordinates.",
                                    "items": {
                                      "type": "object",
                                      "properties": {
                                        "page": {
                                          "type": "integer"
                                        },
                                        "x": {
                                          "type": "number"
                                        },
                                        "y": {
                                          "type": "number"
                                        },
                                        "width": {
                                          "type": "number"
                                        },
                                        "height": {
                                          "type": "number"
                                        },
                                        "page_width": {
                                          "type": "number"
                                        },
                                        "page_height": {
                                          "type": "number"
                                        }
                                      }
                                    }
                                  }
                                }
                              },
                              "creator": {
                                "type": "string",
                                "nullable": true,
                                "description": "Creator application recorded in the document metadata."
                              },
                              "producer": {
                                "type": "string",
                                "nullable": true,
                                "description": "Producer software recorded in the PDF metadata."
                              },
                              "software": {
                                "type": "string",
                                "nullable": true,
                                "description": "Editing/processing software recorded in the file metadata."
                              },
                              "encryption": {
                                "type": "string",
                                "nullable": true,
                                "description": "Encryption scheme of the document, when encrypted."
                              },
                              "is_signed": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "Whether the PDF carries a digital signature."
                              },
                              "is_tampered": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "Whether signature validation indicates the file was modified after signing."
                              },
                              "signature_info": {
                                "type": "object",
                                "nullable": true,
                                "description": "Details of the digital signature(s), when present."
                              },
                              "exif_original_date": {
                                "type": "string",
                                "nullable": true,
                                "description": "EXIF original capture date, for image uploads."
                              },
                              "exif_digitized_date": {
                                "type": "string",
                                "nullable": true,
                                "description": "EXIF digitization date, for image uploads."
                              },
                              "processed_by_known_editor": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "Whether the file metadata indicates processing by a known document editor."
                              },
                              "has_different_creation_mod_date": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "Whether the creation and modification dates differ — a common manipulation signal."
                              }
                            },
                            "description": "File-forensics metadata extracted from the uploaded POA document. Always contains the keys below (values are `null` when not applicable, e.g. EXIF dates for PDFs). `null` when no metadata could be extracted.",
                            "nullable": true
                          },
                          "issuer": {
                            "type": "string",
                            "description": "Name of the document issuer"
                          },
                          "issue_date": {
                            "type": "string",
                            "format": "date",
                            "description": "Date when the document was issued"
                          },
                          "expiration_date": {
                            "type": "string",
                            "format": "date",
                            "nullable": true,
                            "description": "Expiration date of the document, if available"
                          },
                          "poa_address": {
                            "type": "string",
                            "description": "Raw address from the proof of address document"
                          },
                          "poa_formatted_address": {
                            "type": "string",
                            "description": "Formatted address from the proof of address document"
                          },
                          "poa_parsed_address": {
                            "type": "object",
                            "properties": {
                              "address_type": {
                                "type": "string",
                                "description": "Type of address (Street, Avenue, etc)"
                              },
                              "street_1": {
                                "type": "string",
                                "description": "Primary street address"
                              },
                              "street_2": {
                                "type": "string",
                                "nullable": true,
                                "description": "Secondary street address"
                              },
                              "city": {
                                "type": "string",
                                "description": "City name"
                              },
                              "region": {
                                "type": "string",
                                "description": "Region or state name"
                              },
                              "country": {
                                "type": "string",
                                "description": "Two-letter country code"
                              },
                              "postal_code": {
                                "type": "string",
                                "description": "Postal code"
                              },
                              "document_location": {
                                "type": "object",
                                "properties": {
                                  "latitude": {
                                    "type": "number",
                                    "description": "Latitude coordinate"
                                  },
                                  "longitude": {
                                    "type": "number",
                                    "description": "Longitude coordinate"
                                  }
                                }
                              }
                            }
                          },
                          "name_on_document": {
                            "type": "string",
                            "nullable": true,
                            "description": "Account-holder / addressee name extracted from the POA document, used for name matching. `null` when no name could be extracted."
                          },
                          "name_match_score_expected_details": {
                            "type": "number",
                            "nullable": true,
                            "description": "Similarity score (0-100) between `name_on_document` and the expected name supplied at session creation. `null` when either side is missing."
                          },
                          "name_match_score_id_verification": {
                            "type": "number",
                            "nullable": true,
                            "description": "Similarity score (0-100) between `name_on_document` and the name extracted by the ID verification step. `null` when either side is missing."
                          },
                          "expected_details_address": {
                            "type": "string",
                            "nullable": true,
                            "description": "Expected raw address for verification"
                          },
                          "expected_details_formatted_address": {
                            "type": "string",
                            "nullable": true,
                            "description": "Expected formatted address for verification"
                          },
                          "expected_details_parsed_address": {
                            "type": "object",
                            "description": "Expected parsed address details for verification"
                          },
                          "extra_fields": {
                            "type": "object",
                            "description": "Additional banking and contact details extracted from the document.",
                            "properties": {
                              "bank_account_number": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_iban": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_sort_code": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_routing_number": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_swift_bic": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_branch_name": {
                                "type": "string",
                                "nullable": true
                              },
                              "bank_branch_address": {
                                "type": "string",
                                "nullable": true
                              },
                              "document_phone_number": {
                                "type": "string",
                                "nullable": true
                              },
                              "additional_names": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "extra_files": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "uri"
                            },
                            "description": "Additional proof of address files"
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "document_ai_documents": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Document AI reports grouped by workflow node** — always a JSON array, never a singular object. `null` until at least one Document AI step has produced data. Present only when `session_kind = user`. Each item is a node group with an aggregate `status`, the `node_id`, an `items[]` array of the per-document results (extracted data keyed by the configured field key), and `warnings`.",
                      "items": {
                        "type": "object",
                        "description": "Document AI results grouped by workflow node.",
                        "properties": {
                          "status": {
                            "type": "string",
                            "description": "Aggregate Document AI status for this node, rolled up from its `items[]`.",
                            "enum": [
                              "Approved",
                              "Declined",
                              "In Review",
                              "Not Finished"
                            ]
                          },
                          "node_id": {
                            "type": "string",
                            "nullable": true,
                            "description": "Unique identifier for this Document AI step in the workflow graph."
                          },
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "uuid": {
                                  "type": "string",
                                  "format": "uuid"
                                },
                                "node_id": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Workflow graph node that produced this document."
                                },
                                "document_key": {
                                  "type": "string",
                                  "description": "Key of the configured Document AI document this file corresponds to."
                                },
                                "original_filename": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Filename as uploaded by the user."
                                },
                                "file_size": {
                                  "type": "integer",
                                  "nullable": true,
                                  "description": "File size in bytes."
                                },
                                "file_url": {
                                  "type": "string",
                                  "format": "uri",
                                  "nullable": true,
                                  "description": "Short-lived presigned link to the uploaded file."
                                },
                                "status": {
                                  "type": "string",
                                  "enum": [
                                    "Approved",
                                    "Declined",
                                    "In Review",
                                    "Not Finished"
                                  ],
                                  "description": "Per-document Document AI status."
                                },
                                "extracted_data": {
                                  "type": "object",
                                  "additionalProperties": true,
                                  "nullable": true,
                                  "description": "Values extracted by Document AI, keyed by the configured field `key`. Each value is a string, number, or `null`."
                                },
                                "fields": {
                                  "type": "array",
                                  "description": "The configured extraction fields for this document.",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "key": {
                                        "type": "string",
                                        "description": "Stable field key used as the key in `extracted_data`."
                                      },
                                      "name": {
                                        "type": "string",
                                        "description": "Human-readable field name."
                                      },
                                      "instruction": {
                                        "type": "string",
                                        "description": "Extraction instruction shown to the model."
                                      },
                                      "type": {
                                        "type": "string",
                                        "enum": [
                                          "text",
                                          "number",
                                          "date"
                                        ],
                                        "description": "Expected value type of the extracted field."
                                      },
                                      "required": {
                                        "type": "boolean",
                                        "description": "Whether this field is required."
                                      }
                                    }
                                  }
                                },
                                "document_metadata": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "File-forensics metadata extracted from the uploaded document (e.g. `is_tampered`). `null` when nothing could be extracted."
                                },
                                "cross_check_result": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Cross-reference of the extracted document fields against other session data. `null` when no cross-check was performed."
                                },
                                "created_at": {
                                  "type": "string",
                                  "format": "date-time"
                                }
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "nullable": true,
                            "items": {
                              "type": "object"
                            },
                            "description": "Warning logs raised for this Document AI node."
                          }
                        }
                      }
                    },
                    "questionnaire_responses": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of questionnaire response reports** — always a JSON array, never a singular `questionnaire` object. `null` until at least one questionnaire step has produced data. Each item is the full report, including `node_id`, the questionnaire metadata, sections with form elements, and the user's answers.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this questionnaire step in the workflow graph."
                          },
                          "questionnaire_id": {
                            "type": "string",
                            "description": "Unique identifier for the questionnaire"
                          },
                          "title": {
                            "type": "string",
                            "description": "Title of the questionnaire"
                          },
                          "description": {
                            "type": "string",
                            "description": "Description of the questionnaire"
                          },
                          "languages": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Supported languages"
                          },
                          "default_language": {
                            "type": "string",
                            "description": "Default language"
                          },
                          "is_active": {
                            "type": "boolean",
                            "description": "Whether the questionnaire is active"
                          },
                          "is_simple_questionnaire": {
                            "type": "boolean",
                            "description": "Whether the questionnaire is a simple linear form (no branching graph)."
                          },
                          "questionnaire_group_id": {
                            "type": "string",
                            "format": "uuid",
                            "nullable": true,
                            "description": "Stable identifier of the questionnaire group across versions."
                          },
                          "version": {
                            "type": "integer",
                            "nullable": true,
                            "description": "Version number of the questionnaire that was answered."
                          },
                          "published_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true,
                            "description": "Timestamp at which this questionnaire version was published."
                          },
                          "sections": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "title": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Section title"
                                },
                                "description": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Section description"
                                },
                                "items": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "uuid": {
                                        "type": "string",
                                        "description": "Unique identifier for the item"
                                      },
                                      "value": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Stable node identifier of this form element within the questionnaire graph."
                                      },
                                      "element_type": {
                                        "type": "string",
                                        "description": "Type of the questionnaire element"
                                      },
                                      "is_required": {
                                        "type": "boolean",
                                        "description": "Whether the item is required"
                                      },
                                      "title": {
                                        "type": "string",
                                        "description": "Title of the item"
                                      },
                                      "description": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Description of the item"
                                      },
                                      "placeholder": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Placeholder text"
                                      },
                                      "choices": {
                                        "type": "array",
                                        "nullable": true,
                                        "items": {
                                          "type": "object",
                                          "properties": {
                                            "label": {
                                              "type": "string"
                                            },
                                            "value": {
                                              "type": "string"
                                            },
                                            "requires_text_input": {
                                              "type": "boolean",
                                              "description": "Whether this choice requires additional text input",
                                              "nullable": true
                                            }
                                          },
                                          "required": [
                                            "label",
                                            "value"
                                          ]
                                        },
                                        "description": "Choices for dropdown or single choice elements"
                                      },
                                      "max_files": {
                                        "type": "integer",
                                        "description": "Maximum number of files allowed",
                                        "nullable": true
                                      },
                                      "answer": {
                                        "type": "object",
                                        "description": "Answer provided for the item",
                                        "properties": {
                                          "value": {
                                            "type": "string",
                                            "nullable": true
                                          },
                                          "text": {
                                            "type": "string",
                                            "nullable": true
                                          },
                                          "files": {
                                            "type": "array",
                                            "items": {
                                              "type": "string"
                                            },
                                            "nullable": true
                                          }
                                        }
                                      },
                                      "required_if": {
                                        "type": "object",
                                        "nullable": true,
                                        "description": "Conditional-requirement rule, when the element is only required for certain answers."
                                      },
                                      "repeatable_config": {
                                        "type": "object",
                                        "nullable": true,
                                        "description": "Configuration for repeatable element groups, when applicable."
                                      }
                                    },
                                    "required": [
                                      "uuid",
                                      "value",
                                      "element_type",
                                      "is_required",
                                      "title"
                                    ]
                                  }
                                }
                              }
                            }
                          },
                          "status": {
                            "type": "string",
                            "description": "Status of the questionnaire"
                          }
                        }
                      }
                    },
                    "aml_screenings": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of AML screening reports** — always a JSON array, never a singular `aml` object. `null` until at least one AML step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `total_hits`, `entity_type`, `hits[]`, `score`, `screened_data`, `is_ongoing_monitoring_enabled`, and `warnings[]`.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this AML step in the workflow graph."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this AML screening. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ]
                          },
                          "total_hits": {
                            "type": "integer",
                            "description": "Total number of matches found"
                          },
                          "entity_type": {
                            "type": "string",
                            "description": "Type of entity screened: person or company"
                          },
                          "hits": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "string",
                                  "description": "Unique identifier of the match"
                                },
                                "url": {
                                  "type": "string",
                                  "format": "uri",
                                  "description": "Source URL for the match"
                                },
                                "match": {
                                  "type": "boolean",
                                  "description": "Whether this is a confirmed match"
                                },
                                "score": {
                                  "type": "number",
                                  "format": "float",
                                  "nullable": true,
                                  "minimum": 0,
                                  "maximum": 1,
                                  "description": "Deprecated weighted match score in the 0-1 range (e.g. `0.73`). Use `match_score` (0-100) instead.",
                                  "deprecated": true
                                },
                                "target": {
                                  "type": "boolean",
                                  "nullable": true,
                                  "description": "Whether the matched entity is the direct target of the listing (as opposed to a related party). `null` when the source does not specify it."
                                },
                                "caption": {
                                  "type": "string",
                                  "description": "Caption or title of the match"
                                },
                                "datasets": {
                                  "type": "array",
                                  "items": {
                                    "type": "string",
                                    "enum": [
                                      "PEP",
                                      "PEP Level 1",
                                      "PEP Level 2",
                                      "PEP Level 3",
                                      "PEP Level 4",
                                      "Sanctions",
                                      "Adverse Media",
                                      "Warnings and Regulatory Enforcement",
                                      "Fitness and Probity",
                                      "SIP",
                                      "SIE",
                                      "Insolvency"
                                    ]
                                  },
                                  "description": "List of dataset categories where the match was found. Indicates whether the entity appeared in PEP lists (with level), sanctions databases, adverse media, warnings/regulatory enforcement, or other compliance datasets."
                                },
                                "features": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Additional features of the match"
                                },
                                "rca_name": {
                                  "type": "string",
                                  "description": "RCA name if applicable"
                                },
                                "last_seen": {
                                  "type": "string",
                                  "format": "date-time",
                                  "description": "Date when match was last seen"
                                },
                                "risk_view": {
                                  "type": "object",
                                  "properties": {
                                    "crimes": {
                                      "type": "object",
                                      "properties": {
                                        "score": {
                                          "type": "integer"
                                        },
                                        "weightage": {
                                          "type": "integer"
                                        },
                                        "risk_level": {
                                          "type": "string"
                                        },
                                        "risk_scores": {
                                          "type": "object"
                                        }
                                      }
                                    },
                                    "countries": {
                                      "type": "object",
                                      "properties": {
                                        "score": {
                                          "type": "integer"
                                        },
                                        "weightage": {
                                          "type": "integer"
                                        },
                                        "risk_level": {
                                          "type": "string"
                                        },
                                        "risk_scores": {
                                          "type": "object"
                                        }
                                      }
                                    },
                                    "categories": {
                                      "type": "object",
                                      "properties": {
                                        "score": {
                                          "type": "integer"
                                        },
                                        "weightage": {
                                          "type": "integer"
                                        },
                                        "risk_level": {
                                          "type": "string"
                                        },
                                        "risk_scores": {
                                          "type": "object"
                                        }
                                      }
                                    },
                                    "custom_list": {
                                      "type": "object"
                                    }
                                  }
                                },
                                "first_seen": {
                                  "type": "string",
                                  "format": "date-time",
                                  "description": "Date when match was first seen"
                                },
                                "properties": {
                                  "type": "object",
                                  "properties": {
                                    "name": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "alias": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "notes": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "title": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "gender": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "height": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "topics": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "weight": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "address": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "country": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "website": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "eyeColor": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "keywords": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "lastName": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "position": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "religion": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "birthDate": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "education": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "ethnicity": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "firstName": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "hairColor": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "weakAlias": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "birthPlace": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "modifiedAt": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "wikidataId": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "citizenship": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "nationality": {
                                      "type": "string",
                                      "nullable": true
                                    }
                                  }
                                },
                                "match_score": {
                                  "type": "number",
                                  "description": "Weighted identity confidence score (0-100) calculated from name similarity, date of birth, and country matching. Used to determine if hit is a False Positive or Possible Match.",
                                  "format": "float",
                                  "nullable": true
                                },
                                "risk_score": {
                                  "type": "number",
                                  "description": "Entity risk score (0-100). Used to determine final AML status (Approved/In Review/Declined) based on the highest risk_score among non-False-Positive hits."
                                },
                                "review_status": {
                                  "type": "string",
                                  "description": "Review status for this hit. Can be 'Unreviewed', 'Confirmed Match', 'False Positive', or 'Inconclusive'.",
                                  "enum": [
                                    "Unreviewed",
                                    "Confirmed Match",
                                    "False Positive",
                                    "Inconclusive"
                                  ]
                                },
                                "score_breakdown": {
                                  "type": "object",
                                  "description": "Detailed breakdown of how the match_score was calculated.",
                                  "properties": {
                                    "name_score": {
                                      "type": "integer",
                                      "description": "Raw name similarity percentage (0-100)."
                                    },
                                    "name_weight": {
                                      "type": "integer",
                                      "description": "Original weight for name from config (0-100)."
                                    },
                                    "name_weight_normalized": {
                                      "type": "number",
                                      "description": "Normalized weight for name after re-weighting for missing data (0-100)."
                                    },
                                    "name_contribution": {
                                      "type": "number",
                                      "description": "Points contributed by name to the total score."
                                    },
                                    "dob_score": {
                                      "type": "integer",
                                      "description": "DOB match score (-100 to 100)."
                                    },
                                    "dob_weight": {
                                      "type": "integer",
                                      "description": "Original weight for DOB from config (0-100)."
                                    },
                                    "dob_weight_normalized": {
                                      "type": "number",
                                      "description": "Normalized weight for DOB after re-weighting for missing data (0-100)."
                                    },
                                    "dob_contribution": {
                                      "type": "number",
                                      "description": "Points contributed by DOB to the total score."
                                    },
                                    "country_score": {
                                      "type": "integer",
                                      "description": "Country match score (-100 to 100)."
                                    },
                                    "country_weight": {
                                      "type": "integer",
                                      "description": "Original weight for country from config (0-100)."
                                    },
                                    "country_weight_normalized": {
                                      "type": "number",
                                      "description": "Normalized weight for country after re-weighting for missing data (0-100)."
                                    },
                                    "country_contribution": {
                                      "type": "number",
                                      "description": "Points contributed by country to the total score."
                                    },
                                    "document_number_match_type": {
                                      "type": "string",
                                      "description": "Result of document number 'Golden Key' evaluation.",
                                      "enum": [
                                        "MATCH",
                                        "NEUTRAL",
                                        "HARD_MISMATCH"
                                      ]
                                    },
                                    "document_number_effect": {
                                      "type": "string",
                                      "description": "Human-readable description of the document number effect on the score."
                                    },
                                    "total_score": {
                                      "type": "integer",
                                      "description": "Final weighted match score (0-100)."
                                    }
                                  }
                                },
                                "pep_matches": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "aliases": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "education": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "list_name": {
                                        "type": "string"
                                      },
                                      "publisher": {
                                        "type": "string"
                                      },
                                      "source_url": {
                                        "type": "string",
                                        "format": "uri"
                                      },
                                      "description": {
                                        "type": "string"
                                      },
                                      "matched_name": {
                                        "type": "string"
                                      },
                                      "pep_position": {
                                        "type": "string"
                                      },
                                      "date_of_birth": {
                                        "type": "string"
                                      },
                                      "other_sources": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "place_of_birth": {
                                        "type": "string"
                                      }
                                    }
                                  }
                                },
                                "linked_entities": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "name": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "active": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "status": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "details": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "relation": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      }
                                    }
                                  }
                                },
                                "warning_matches": {
                                  "type": "array",
                                  "description": "List of warning and regulatory enforcement matches. Includes entries from categories: Warning, SIP, Warnings and Regulatory Enforcement, Fitness and Probity, SIE, and Insolvency.",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "list_name": {
                                        "type": "string",
                                        "description": "Name of the warning list publisher"
                                      },
                                      "matched_name": {
                                        "type": "string",
                                        "description": "Name matched in the warning list"
                                      },
                                      "description": {
                                        "type": "string",
                                        "description": "Description of the warning entry"
                                      },
                                      "publisher": {
                                        "type": "string",
                                        "description": "Publisher or source of the warning"
                                      },
                                      "source_url": {
                                        "type": "string",
                                        "format": "uri",
                                        "description": "URL to the source of the warning"
                                      },
                                      "other_sources": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Additional source URLs"
                                      },
                                      "countries": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "nullable": true,
                                        "description": "Countries associated with the warning"
                                      },
                                      "created_at": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Date the warning entry was created"
                                      },
                                      "updated_at": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Date the warning entry was last updated"
                                      },
                                      "additional_data": {
                                        "type": "object",
                                        "description": "Additional data from the warning source. May contain a `data` object with `case_details` array.",
                                        "properties": {
                                          "data": {
                                            "type": "object",
                                            "properties": {
                                              "case_details": {
                                                "type": "array",
                                                "description": "Details of warning cases associated with this match",
                                                "items": {
                                                  "type": "object",
                                                  "properties": {
                                                    "case_type": {
                                                      "type": "string",
                                                      "description": "Type of the warning case",
                                                      "enum": [
                                                        "Accusation",
                                                        "Arrest",
                                                        "Bribery",
                                                        "Criminal",
                                                        "Fraud",
                                                        "Illegal Fundraising",
                                                        "Non-conviction Terror",
                                                        "Proclaimed Offender",
                                                        "Regulatory Enforcement",
                                                        "Terrorism",
                                                        "Terrorism-related",
                                                        "Terrorism-related listing"
                                                      ]
                                                    }
                                                  }
                                                }
                                              }
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                },
                                "sanction_matches": {
                                  "type": "array",
                                  "description": "List of sanction list matches found for this entity.",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "list_name": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "nullable": true,
                                        "description": "Name(s) of the sanction list(s)"
                                      },
                                      "matched_name": {
                                        "type": "string",
                                        "description": "Name matched in the sanction list"
                                      },
                                      "description": {
                                        "type": "string",
                                        "description": "Description of the sanction entry"
                                      },
                                      "reason": {
                                        "type": "string",
                                        "nullable": true,
                                        "description": "Free-text reason why the entity was sanctioned. Joined from the sanction list's reason entries. Examples: 'Executive Order 13224 (Terrorism)', 'Support for actions undermining Ukraine's sovereignty', 'Listed due to national security concerns.'"
                                      },
                                      "source_url": {
                                        "type": "string",
                                        "format": "uri",
                                        "description": "URL to the source of the sanction"
                                      },
                                      "other_sources": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Additional source URLs"
                                      },
                                      "details": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Additional details about the sanction"
                                      },
                                      "legal_basis": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Legal basis for the sanction"
                                      },
                                      "listed_on": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "nullable": true,
                                        "description": "Date(s) when the entity was listed"
                                      },
                                      "sanction_list": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "nullable": true,
                                        "description": "Sanction list identifier(s)"
                                      },
                                      "sanction_program": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Sanction program(s) the entity is listed under"
                                      },
                                      "sanctioning_authority": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Authority/authorities that imposed the sanction"
                                      },
                                      "updated_on": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        },
                                        "description": "Date(s) when the sanction entry was last updated"
                                      },
                                      "additional_data": {
                                        "type": "object",
                                        "description": "Additional data from the sanction source not captured in the main fields"
                                      }
                                    }
                                  }
                                },
                                "adverse_media_details": {
                                  "type": "object",
                                  "properties": {
                                    "sentiment": {
                                      "type": "string"
                                    },
                                    "entity_type": {
                                      "type": "string"
                                    },
                                    "sentiment_score": {
                                      "type": "integer"
                                    },
                                    "adverse_keywords": {
                                      "type": "object",
                                      "additionalProperties": {
                                        "type": "integer"
                                      }
                                    }
                                  }
                                },
                                "adverse_media_matches": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "country": {
                                        "type": "string"
                                      },
                                      "summary": {
                                        "type": "string"
                                      },
                                      "headline": {
                                        "type": "string"
                                      },
                                      "sentiment": {
                                        "type": "string"
                                      },
                                      "thumbnail": {
                                        "type": "string",
                                        "format": "uri"
                                      },
                                      "source_url": {
                                        "type": "string",
                                        "format": "uri"
                                      },
                                      "author_name": {
                                        "type": "string",
                                        "nullable": true
                                      },
                                      "other_sources": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "adverse_keywords": {
                                        "type": "array",
                                        "items": {
                                          "type": "string"
                                        }
                                      },
                                      "sentiment_score": {
                                        "type": "integer"
                                      },
                                      "publication_date": {
                                        "type": "string",
                                        "format": "date-time"
                                      }
                                    }
                                  }
                                },
                                "additional_information": {
                                  "type": "object"
                                }
                              }
                            }
                          },
                          "score": {
                            "type": "number",
                            "format": "float",
                            "nullable": true,
                            "minimum": 0,
                            "maximum": 100,
                            "description": "Overall AML screening score — the highest risk score across all hits (float, 0-100). `null` until computed."
                          },
                          "screened_data": {
                            "type": "object",
                            "properties": {
                              "full_name": {
                                "type": "string"
                              },
                              "nationality": {
                                "type": "string"
                              },
                              "date_of_birth": {
                                "type": "string",
                                "format": "date"
                              },
                              "document_number": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          },
                          "is_ongoing_monitoring_enabled": {
                            "type": "boolean",
                            "description": "Whether AML ongoing monitoring is enabled for this screening. When `true`, the entity is re-screened periodically and any new hits update the report."
                          },
                          "next_ongoing_monitoring_bill_date": {
                            "type": "string",
                            "format": "date",
                            "nullable": true,
                            "description": "Date (YYYY-MM-DD) when this ongoing-monitoring subscription will next be billed. Computed as the date of the last ongoing-monitoring billing plus 365 days when `is_ongoing_monitoring_enabled = true`; otherwise `null`."
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Within an AML screening this is typically `AML`, but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "ip_analyses": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of Device & IP Analysis reports** — always a JSON array, never a singular `ip_analysis` object. `null` until at least one IP-analysis step has produced data. Each item is the full report, including `node_id`, feature-level `status`, IP and geolocation data, device fingerprint, and `warnings[]`. Entries with duplicate `(node_id, ip_address, device_fingerprint)` are deduplicated server-side.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this Device & IP Analysis step in the workflow graph."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this Device & IP Analysis entry. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ]
                          },
                          "device_brand": {
                            "type": "string",
                            "description": "Brand of the device used"
                          },
                          "device_model": {
                            "type": "string",
                            "description": "Model of the device used"
                          },
                          "browser_family": {
                            "type": "string",
                            "description": "Browser family used"
                          },
                          "os_family": {
                            "type": "string",
                            "description": "Operating system family"
                          },
                          "platform": {
                            "type": "string",
                            "description": "Platform type (mobile, desktop, etc)"
                          },
                          "device_fingerprint": {
                            "type": "string",
                            "nullable": true,
                            "description": "Unique device fingerprint identifier"
                          },
                          "ip_country": {
                            "type": "string",
                            "description": "Country detected from IP"
                          },
                          "ip_country_code": {
                            "type": "string",
                            "description": "Two-letter country code from IP"
                          },
                          "ip_state": {
                            "type": "string",
                            "description": "State/region detected from IP"
                          },
                          "ip_city": {
                            "type": "string",
                            "description": "City detected from IP"
                          },
                          "latitude": {
                            "type": "number",
                            "description": "Latitude coordinate of IP location"
                          },
                          "longitude": {
                            "type": "number",
                            "description": "Longitude coordinate of IP location"
                          },
                          "ip_address": {
                            "type": "string",
                            "description": "IP address"
                          },
                          "isp": {
                            "type": "string",
                            "nullable": true,
                            "description": "Internet Service Provider"
                          },
                          "organization": {
                            "type": "string",
                            "nullable": true,
                            "description": "Organization associated with IP"
                          },
                          "is_vpn_or_tor": {
                            "type": "boolean",
                            "description": "Whether IP is from VPN or Tor network"
                          },
                          "is_data_center": {
                            "type": "boolean",
                            "description": "Whether IP is from a data center"
                          },
                          "time_zone": {
                            "type": "string",
                            "description": "Time zone detected from IP"
                          },
                          "time_zone_offset": {
                            "type": "string",
                            "description": "Time zone offset"
                          },
                          "ip": {
                            "type": "object",
                            "description": "IP geolocation of this entry plus great-circle distances (km) to the ID document address and the POA document address. Distances are `null` when either side has no resolvable location.",
                            "properties": {
                              "location": {
                                "type": "object",
                                "nullable": true,
                                "properties": {
                                  "latitude": {
                                    "type": "number",
                                    "example": 41.2706327
                                  },
                                  "longitude": {
                                    "type": "number",
                                    "example": 1.9770097
                                  }
                                },
                                "description": "Coordinates, or `null` when unavailable."
                              },
                              "distance_from_id_document": {
                                "type": "number",
                                "nullable": true,
                                "example": 23.4
                              },
                              "distance_from_poa_document": {
                                "type": "number",
                                "nullable": true,
                                "example": 12.3
                              }
                            }
                          },
                          "id_document": {
                            "type": "object",
                            "description": "Location extracted from the ID document address plus distances (km) to the IP geolocation and the POA document address. `location` is `null` when no document address could be geocoded.",
                            "properties": {
                              "location": {
                                "type": "object",
                                "nullable": true,
                                "properties": {
                                  "latitude": {
                                    "type": "number",
                                    "example": 41.2706327
                                  },
                                  "longitude": {
                                    "type": "number",
                                    "example": 1.9770097
                                  }
                                },
                                "description": "Coordinates, or `null` when unavailable."
                              },
                              "distance_from_ip": {
                                "type": "number",
                                "nullable": true,
                                "example": 23.4
                              },
                              "distance_from_poa_document": {
                                "type": "number",
                                "nullable": true,
                                "example": 18.7
                              }
                            }
                          },
                          "poa_document": {
                            "type": "object",
                            "description": "Location extracted from the Proof of Address document plus distances (km) to the IP geolocation and the ID document address. `location` is `null` when the session has no POA step or the address could not be geocoded.",
                            "properties": {
                              "location": {
                                "type": "object",
                                "nullable": true,
                                "properties": {
                                  "latitude": {
                                    "type": "number",
                                    "example": 41.2706327
                                  },
                                  "longitude": {
                                    "type": "number",
                                    "example": 1.9770097
                                  }
                                },
                                "description": "Coordinates, or `null` when unavailable."
                              },
                              "distance_from_ip": {
                                "type": "number",
                                "nullable": true,
                                "example": 12.3
                              },
                              "distance_from_id_document": {
                                "type": "number",
                                "nullable": true,
                                "example": 18.7
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            }
                          },
                          "matches": {
                            "type": "array",
                            "description": "Cross-session matches found for this entry's IP address or device identity. Every match is another verification session in your application (`source` is always `session`) that shares the same value, always belonging to a different vendor user / `vendor_data` than the current session, capped at 5 per collector (`device_fingerprint` sums two independently capped collectors, so up to 10 entries are possible). Every item carries `match_source`, `confidence`, `match_mode`, `device_info` and `location_info`; the keys `recovery_similarity`, `tls_ja4_corroborated` and `recovery_gate_reason` are present only when `match_source` is `recovered_high`.",
                            "items": {
                              "type": "object",
                              "additionalProperties": false,
                              "properties": {
                                "session_id": {
                                  "type": "string",
                                  "format": "uuid",
                                  "description": "Session ID of the matching session."
                                },
                                "session_number": {
                                  "type": "integer",
                                  "description": "Sequential number of the matching session."
                                },
                                "vendor_data": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "`vendor_data` of the matching session — always different from the current session's vendor user."
                                },
                                "verification_date": {
                                  "type": "string",
                                  "format": "date-time",
                                  "nullable": true,
                                  "description": "Creation date of the matching session (`YYYY-MM-DDTHH:MM:SSZ`)."
                                },
                                "match_type": {
                                  "type": "string",
                                  "enum": [
                                    "ip_address",
                                    "device_fingerprint"
                                  ],
                                  "description": "Whether the match was on IP address or device fingerprint"
                                },
                                "match_source": {
                                  "type": "string",
                                  "enum": [
                                    "ip_address",
                                    "persistent_id",
                                    "legacy_fp",
                                    "recovered_high"
                                  ],
                                  "description": "How this match was reached: `ip_address` (shared IP address — network co-location, not a device-identity claim), `persistent_id` (exact stored persistent device identifier), `legacy_fp` (legacy `didit-fp-*` device-fingerprint hash), or `recovered_high` (high-confidence fuzzy fingerprint-recovery hit). Lower-confidence recovery candidates surface as risk warnings only, never in this array."
                                },
                                "matched_value": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "The value that matched: the shared IP address, the persistent device identifier or legacy fingerprint hash, or — for `recovered_high` — the UUID of the recovered device."
                                },
                                "status": {
                                  "type": "string",
                                  "description": "Lifecycle status of the matching session."
                                },
                                "is_blocklisted": {
                                  "type": "boolean",
                                  "description": "Whether the matched value is on a blocklist. Currently always `false` for device/IP matches."
                                },
                                "api_service": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Standalone API service of the matching session; `null` for workflow sessions."
                                },
                                "source": {
                                  "type": "string",
                                  "enum": [
                                    "session"
                                  ],
                                  "description": "Always `session` for device/IP matches — every match is another verification session in your application."
                                },
                                "device_info": {
                                  "type": "object",
                                  "description": "Device details observed on the matching session.",
                                  "properties": {
                                    "device_brand": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "device_model": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "browser_family": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "os_family": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "platform": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "device_fingerprint": {
                                      "type": "string",
                                      "nullable": true
                                    }
                                  }
                                },
                                "location_info": {
                                  "type": "object",
                                  "description": "Network and geolocation details observed on the matching session.",
                                  "properties": {
                                    "ip_address": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "ip_country": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "ip_country_code": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "ip_state": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "ip_city": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "is_vpn_or_tor": {
                                      "type": "boolean"
                                    },
                                    "is_data_center": {
                                      "type": "boolean"
                                    }
                                  }
                                },
                                "confidence": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1,
                                  "description": "Server-side identification confidence, computed as `1 - P(false positive)`: `1.0` for deterministic matches (`persistent_id`, or hardware-rooted `recovered_high`), `0.5` for `legacy_fp`, `0.0` for `ip_address` (co-location only), and a corroboration-adjusted value below `1.0` for probabilistic recovered matches (higher when the TLS JA4 fingerprint and IP country independently agree)."
                                },
                                "match_mode": {
                                  "type": "string",
                                  "enum": [
                                    "deterministic",
                                    "probabilistic",
                                    "co_occurrence"
                                  ],
                                  "description": "Evidence class of the match: `deterministic` (exact stored or hardware-rooted identifier that cannot collide between physical devices), `probabilistic` (fuzzy fingerprint evidence), or `co_occurrence` (shared network only, no device-identity claim)."
                                },
                                "recovery_similarity": {
                                  "type": "number",
                                  "description": "Cosine similarity of the fingerprint-recovery match, rounded to 4 decimals. Present only when `match_source` is `recovered_high`."
                                },
                                "tls_ja4_corroborated": {
                                  "type": "boolean",
                                  "description": "Whether the TLS JA4 fingerprints of the two observations agree — a server-observed signal the browser cannot spoof. Present only when `match_source` is `recovered_high`."
                                },
                                "recovery_gate_reason": {
                                  "type": "string",
                                  "description": "Why the recovery match passed the false-positive hard gate: `hardware_root_match` (a hardware-rooted device identifier matched exactly) or, on configurations that admit probe-only evidence, `exact_composite_plus_<n>_families` / `very_high_similarity_plus_<n>_families`. Present only when `match_source` is `recovered_high`."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "database_validations": {
                      "type": "array",
                      "nullable": true,
                      "description": "**Array of Database Validation reports** — always a JSON array, never a singular `database_validation` object. `null` until at least one DB validation step has produced data. Each item is the full report, including `node_id`, feature-level `status`, `screened_data`, the per-service `validations[]` (each with its registry `source_data`), and `warnings[]`. The `screened_data` and `source_data` fields vary by country:\n\n**ARG (Argentina)**: screened_data: {document_number, selfie, gender, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, date_of_death, tax_id, tax_id_type, face_match_score, sit_1_since, banks, last_position, highest_position, rejected_checks}. Uses RENAPER biometric face-match validation. Gender is auto-inferred from face analysis or given name when not present in the document (e.g., driver licenses)\n\n**BOL (Bolivia)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender}\n\n**BRA (Brazil)**: screened_data: {tax_number, first_name, last_name, date_of_birth}. source_data always includes {identification_number, first_name, last_name, date_of_birth, lgpd_minor, minor_under_18, minor_under_16} for successful CPF matches. Successful adult lookups set those flags to false. The Receita Federal Consulta CPF service may return HTTP 422 (minor under 18) or 451 (under 16) with LGPD-withheld data; in those cases source_data sets the minor flags to true as appropriate, includes the upstream HTTP status, and field-level validation is no_match. See the Brazil section of the Database Validation Outcome Codes reference for the full code list.\n\n**CHL (Chile)**: screened_data: {personal_number, first_name, last_name, date_of_birth}. source_data: {identification_number, first_name, last_name, date_of_birth, gender}\n\n**COL (Colombia)**: screened_data: {document_number, document_type, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, document_type}\n\n**CRI (Costa Rica)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**DOM (Dominican Republic)**: screened_data: {personal_number}. source_data: {identification_number} (returns valid/invalid only)\n\n**ECU (Ecuador)**: screened_data: {personal_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, street, formatted_address, gender, nationality, education, marital_status, spouse, mother_name, father_name, profession}\n\n**ESP (Spain)**: screened_data: {personal_number, document_type, expiration_date, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, document_type, expiration_date}\n\n**GTM (Guatemala)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**HND (Honduras)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**MEX (Mexico)**: screened_data: {personal_number (CURP), first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, date_of_birth, gender, nationality, curp_status, state_of_birth, doc_probatorio, registration_year, registration_state, registration_municipality, num_acta, crip, issuing_state_code, foreign_registry_number, folio_carta, folio_certificado}. Uses the RENAPO civil registry. A separate INE validity-check service is also available for MEX as an additional one-by-one validation.\n\n**PAN (Panama)**: screened_data: {personal_number, selfie}. source_data: {identification_number}. Uses SIB biometric validation\n\n**PER (Peru)**: screened_data: {personal_number (DNI), first_name, last_name}. source_data: {identification_number, first_name, last_name, paternal_name, maternal_name, verification_number, verification_letter}\n\n**PRY (Paraguay)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**SLV (El Salvador)**: screened_data: {document_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**URY (Uruguay)**: screened_data: {personal_number, date_of_birth, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name}\n\n**VEN (Venezuela)**: screened_data: {document_number, first_name, last_name}. source_data: {identification_number, first_name, last_name, full_name, gender, date_of_birth, document_type}",
                      "items": {
                        "type": "object",
                        "properties": {
                          "node_id": {
                            "type": "string",
                            "description": "Unique identifier for this database validation step in the workflow graph."
                          },
                          "issuing_state": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-3 code of the country whose government/registry sources were queried (e.g. `BRA`, `MEX`, `ESP`, `NGA`, `IDN`). Database Validation covers 60+ countries across the Americas, Europe, Africa, the Middle East and Asia-Pacific, plus global multi-country sources — coverage expands regularly, so treat the value as an open set rather than a fixed enum."
                          },
                          "validation_type": {
                            "type": "string",
                            "enum": [
                              "one_by_one",
                              "two_by_two",
                              "not_enabled"
                            ],
                            "description": "How the result was corroborated: `one_by_one` (single-service outcome), `two_by_two` (two or more distinct services independently full-matched the identification number), or `not_enabled` (persisted when no service produced a usable match). The value is never `null`. Dedicated biometric 2x2 validation modes are currently available for `ECU` and `PER`."
                          },
                          "screened_data": {
                            "type": "object",
                            "description": "The input data used for validation. Fields vary by country — see the Database Validation docs (`/core-technology/database-validation/overview`) for country-specific formats.",
                            "properties": {
                              "last_name": {
                                "type": "string"
                              },
                              "first_name": {
                                "type": "string"
                              },
                              "tax_number": {
                                "type": "string",
                                "description": "Brazil: CPF (11 digits)"
                              },
                              "personal_number": {
                                "type": "string",
                                "description": "Chile: RUT, DOM: Cédula (11 digits), ECU: Cédula (10 digits), ESP: DNI/NIE, MEX: CURP (18 chars), PER: DNI (8 digits), URY: CI"
                              },
                              "document_number": {
                                "type": "string",
                                "description": "ARG: DNI, BOL: CI, COL: Cédula, GTM: DPI, HND: DNI, PAN: Cédula, PRY: CI, SLV: DUI, VEN: Cédula"
                              },
                              "date_of_birth": {
                                "type": "string",
                                "format": "date"
                              },
                              "document_type": {
                                "type": "string",
                                "description": "COL: CC/CE/TI/NIT, ESP: ID/PASSPORT"
                              },
                              "expiration_date": {
                                "type": "string",
                                "format": "date",
                                "description": "Required for ESP"
                              }
                            }
                          },
                          "validations": {
                            "type": "array",
                            "description": "One entry per registry service that returned a validation payload. Each entry identifies the service (`service_id`, `service_name`) and carries the per-field `validation` comparison, the service `outcome_code` / `outcome_detail` when reported, and the normalised registry record in `source_data`.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "service_id": {
                                  "type": "string",
                                  "description": "Identifier of the registry service that produced this validation result.",
                                  "example": "bra_cpf"
                                },
                                "service_name": {
                                  "type": "string",
                                  "description": "Human-readable name of the registry service.",
                                  "example": "Brazil - CPF status check"
                                },
                                "validation": {
                                  "type": "object",
                                  "description": "The results of the field validations.",
                                  "properties": {
                                    "full_name": {
                                      "type": "string",
                                      "description": "Match result for full name (e.g., 'full_match', 'partial_match', 'no_match')."
                                    },
                                    "date_of_birth": {
                                      "type": "string",
                                      "description": "Match result for date of birth."
                                    },
                                    "identification_number": {
                                      "type": "string",
                                      "description": "Match result for identification number."
                                    }
                                  }
                                },
                                "outcome_code": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Machine-readable outcome of the registry lookup (e.g. `MATCH`, `NO_MATCH`, `PARTIAL_MATCH`, `INCONCLUSIVE`, source-specific codes). Present when the service reports one."
                                },
                                "outcome_detail": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Human-readable explanation of `outcome_code`, when provided by the service."
                                },
                                "source_data": {
                                  "type": "object",
                                  "description": "The data returned from the government database. Common fields: identification_number, first_name, last_name, full_name, date_of_birth, gender. Additional fields by country: BRA always includes lgpd_minor, minor_under_18, and minor_under_16 on successful CPF matches; it also includes the upstream HTTP status when CPF data is withheld under LGPD (HTTP 422/451). ECU adds street, formatted_address, nationality, education, marital_status, spouse, mother_name, father_name, profession. PER adds paternal_name, maternal_name, verification_number, verification_letter. ESP adds document_type, expiration_date. VEN/COL add document_type.",
                                  "additionalProperties": true
                                }
                              }
                            }
                          },
                          "errors": {
                            "type": "array",
                            "description": "Errors returned by registry services while validating, each tagged with the `service_id` that produced it. **This key is omitted entirely when there are no errors.**",
                            "items": {
                              "type": "object",
                              "description": "The upstream service's error object spread as-is — its keys vary by service (commonly a message/code and any provider-specific details) — plus `service_id` identifying the service that errored.",
                              "properties": {
                                "service_id": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Identifier of the registry service that errored."
                                }
                              },
                              "additionalProperties": true
                            }
                          },
                          "match_type": {
                            "type": "string",
                            "enum": [
                              "no_match",
                              "partial_match",
                              "full_match"
                            ],
                            "nullable": true,
                            "description": "The aggregate match outcome across the queried services. `null` until the validation has been evaluated."
                          },
                          "status": {
                            "type": "string",
                            "description": "Feature-level status of this database validation. Independent of the top-level session `status` — a session can reach its overall decision while an individual feature stays `In Review`.",
                            "enum": [
                              "Not Finished",
                              "Approved",
                              "Declined",
                              "In Review",
                              "Resub Requested"
                            ]
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "feature": {
                                  "type": "string",
                                  "enum": [
                                    "ID_VERIFICATION",
                                    "NFC",
                                    "LIVENESS",
                                    "FACEMATCH",
                                    "PROOF_OF_ADDRESS",
                                    "PHONE",
                                    "EMAIL",
                                    "AML",
                                    "LOCATION",
                                    "DATABASE_VALIDATION",
                                    "QUESTIONNAIRE",
                                    "DOCUMENT_AI",
                                    "KYB_REGISTRY",
                                    "KYB_DOCUMENTS",
                                    "KYB_KEY_PEOPLE"
                                  ],
                                  "description": "The feature that generated this warning. The value identifies the feature block the warning belongs to. Each module's warnings array typically uses the value matching its module (e.g. `PHONE` inside `phone_verifications[].warnings`), but the enum above is authoritative across all feature warnings on the decision payload."
                                },
                                "risk": {
                                  "type": "string"
                                },
                                "additional_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "log_type": {
                                  "type": "string"
                                },
                                "short_description": {
                                  "type": "string"
                                },
                                "long_description": {
                                  "type": "string"
                                },
                                "node_id": {
                                  "type": "string",
                                  "description": "The workflow graph node ID that generated this warning"
                                }
                              }
                            },
                            "description": "Warnings related to the database validation."
                          }
                        }
                      }
                    },
                    "reviews": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "user": {
                            "type": "string",
                            "description": "Who performed the review. Usually the console reviewer's identifier (their email), but the value is `\"API Client\"` for status updates made with an API key and `\"Unknown\"` when no actor could be resolved — so do not parse this field as an email address."
                          },
                          "new_status": {
                            "type": "string"
                          },
                          "comment": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Timestamp at which the session was created (ISO 8601, UTC)."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Timestamp at which this session is scheduled to expire. Set on User Verification (KYC) sessions when an expiration was configured at creation time (or once `Approved` status triggers KYC expiry computation). `null` for sessions without a configured expiration, and for kinds that do not use expiry (e.g. Business Verification (KYB) sessions that never received an `expires_at`)."
                    },
                    "registry_checks": {
                      "type": "array",
                      "nullable": true,
                      "description": "Company registry checks. Present only when `session_kind = business`. Each item wraps the full company-registry payload.",
                      "items": {
                        "type": "object",
                        "description": "One company registry check — the full company-registry payload enriched with ownership structure and warnings. Present only when `session_kind = business`.",
                        "properties": {
                          "status": {
                            "type": "string",
                            "description": "Feature lifecycle status."
                          },
                          "node_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "data_resolved": {
                            "type": "boolean"
                          },
                          "company": {
                            "type": "object",
                            "description": "Full registry payload for the company (registry record merged with any user-provided or console-edited data).",
                            "properties": {
                              "uuid": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "node_id": {
                                "type": "string",
                                "nullable": true
                              },
                              "status": {
                                "type": "string"
                              },
                              "registry_status": {
                                "type": "string",
                                "nullable": true
                              },
                              "data_resolved": {
                                "type": "boolean"
                              },
                              "company_name": {
                                "type": "string"
                              },
                              "registration_number": {
                                "type": "string",
                                "nullable": true
                              },
                              "country_code": {
                                "type": "string",
                                "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `DE`, `US`)."
                              },
                              "region": {
                                "type": "string",
                                "nullable": true,
                                "description": "ISO 3166-2 subdivision code when applicable (e.g. `CA`, `NY` for US states)."
                              },
                              "company_type": {
                                "type": "string",
                                "nullable": true
                              },
                              "incorporation_date": {
                                "type": "string",
                                "format": "date",
                                "nullable": true
                              },
                              "registered_address": {
                                "type": "string",
                                "nullable": true
                              },
                              "tax_number": {
                                "type": "string",
                                "nullable": true
                              },
                              "risk_level": {
                                "type": "string",
                                "enum": [
                                  "LOW",
                                  "MEDIUM",
                                  "HIGH"
                                ],
                                "nullable": true
                              },
                              "verification_status": {
                                "type": "string",
                                "nullable": true
                              },
                              "is_from_registry": {
                                "type": "boolean"
                              },
                              "fetch_status": {
                                "type": "string",
                                "nullable": true
                              },
                              "alternative_names": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "nature_of_business": {
                                "type": "string",
                                "nullable": true
                              },
                              "registered_capital": {
                                "type": "string",
                                "nullable": true
                              },
                              "registered_capital_amount": {
                                "type": "string",
                                "format": "decimal",
                                "nullable": true,
                                "description": "Registered/share capital amount as a decimal string (e.g. `\"100.00\"`), parsed from the registry filing. `null` when not available."
                              },
                              "registered_capital_currency": {
                                "type": "string",
                                "nullable": true,
                                "description": "ISO 4217 currency code of `registered_capital_amount` (e.g. `\"GBP\"`). `null` when not available."
                              },
                              "website": {
                                "type": "string",
                                "nullable": true
                              },
                              "email": {
                                "type": "string",
                                "format": "email",
                                "nullable": true
                              },
                              "phone": {
                                "type": "string",
                                "nullable": true
                              },
                              "legal_entity_identifier": {
                                "type": "string",
                                "nullable": true
                              },
                              "location_of_registration": {
                                "type": "string",
                                "nullable": true
                              },
                              "vat_number": {
                                "type": "string",
                                "nullable": true,
                                "description": "EU VAT number, when collected during the hosted registry step or edited afterwards."
                              },
                              "vat_validation_status": {
                                "type": "string",
                                "enum": [
                                  "valid",
                                  "invalid",
                                  "could_not_validate",
                                  "not_applicable"
                                ],
                                "description": "Result of the EU VIES check run at registry submit. `not_applicable` when no VAT number was provided or the company is outside the EU VAT area (EU-27 plus Northern Ireland)."
                              },
                              "vat_validated_name": {
                                "type": "string",
                                "nullable": true,
                                "description": "Trader name registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                              },
                              "vat_validated_address": {
                                "type": "string",
                                "nullable": true,
                                "description": "Trader address registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                              },
                              "vat_checked_at": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "Timestamp of the VIES check. `null` when no check ran."
                              },
                              "financial_summary": {
                                "type": "object",
                                "nullable": true
                              },
                              "officers": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "uuid": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "name": {
                                      "type": "string"
                                    },
                                    "designation": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "role": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "nationality": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "is_active": {
                                      "type": "boolean"
                                    },
                                    "kyc_status": {
                                      "type": "string",
                                      "enum": [
                                        "Approved",
                                        "Declined",
                                        "Pending"
                                      ],
                                      "nullable": true
                                    },
                                    "kyc_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true
                                    }
                                  }
                                }
                              },
                              "beneficial_owners": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "uuid": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "name": {
                                      "type": "string"
                                    },
                                    "first_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "last_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "entity_type": {
                                      "type": "string",
                                      "enum": [
                                        "person",
                                        "company"
                                      ]
                                    },
                                    "roles": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "ownership_min_shares": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25.00\"`, `\"50\"`); use `effective_ownership_percent` for a parsed numeric value."
                                    },
                                    "ownership_max_shares": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value."
                                    },
                                    "is_active": {
                                      "type": "boolean"
                                    },
                                    "kyc_status": {
                                      "type": "string",
                                      "enum": [
                                        "Approved",
                                        "Declined",
                                        "Pending"
                                      ],
                                      "nullable": true
                                    },
                                    "kyc_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true
                                    },
                                    "effective_ownership_percent": {
                                      "type": "number",
                                      "nullable": true
                                    }
                                  }
                                }
                              },
                              "addresses": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "object"
                                }
                              },
                              "industries": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "object"
                                }
                              },
                              "accounts": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "object"
                                }
                              },
                              "registry_data": {
                                "type": "object",
                                "description": "Raw payload returned by the registry provider."
                              },
                              "user_provided_data": {
                                "type": "object",
                                "description": "Data confirmed or edited by the end user."
                              },
                              "confirmed_by_user_at": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                              },
                              "last_console_edit_at": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true
                              },
                              "is_editable": {
                                "type": "boolean"
                              }
                            }
                          },
                          "ownership_structure": {
                            "type": "object",
                            "description": "Raw ownership-chain JSON as returned by the registry provider."
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object"
                            }
                          }
                        }
                      }
                    },
                    "key_people_checks": {
                      "type": "array",
                      "nullable": true,
                      "description": "Aggregate key-people checks. Present only when `session_kind = business`. Each item uses the two-bucket shape (`registry`, `submitted`, `ubo_kyc_summary`).",
                      "items": {
                        "type": "object",
                        "description": "Aggregate check over the company's officers + UBOs. Uses the `registry` / `submitted` shape. Present only when session_kind = business.",
                        "properties": {
                          "status": {
                            "type": "string"
                          },
                          "node_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "registry": {
                            "type": "object",
                            "description": "Parties discovered via the registry check.",
                            "properties": {
                              "officers": {
                                "type": "array",
                                "description": "Officers discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "uuid": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "name": {
                                      "type": "string",
                                      "description": "Officer's full name as recorded in the registry."
                                    },
                                    "designation": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Free-text designation from the registry (e.g. `Director`, `Company Secretary`)."
                                    },
                                    "role": {
                                      "type": "string",
                                      "enum": [
                                        "director",
                                        "non_executive_director",
                                        "secretary",
                                        "chairman",
                                        "ubo",
                                        "shareholder",
                                        "representative",
                                        "founder",
                                        "legal_advisor",
                                        "authorized_signatory",
                                        "trustee",
                                        "beneficiary",
                                        "settlor",
                                        "protector",
                                        "company_officer",
                                        "investor",
                                        "other"
                                      ],
                                      "description": "Normalized officer role."
                                    },
                                    "nationality": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "appointment_date": {
                                      "type": "string",
                                      "format": "date",
                                      "nullable": true,
                                      "description": "Date the officer was appointed, when the registry provides it."
                                    },
                                    "termination_date": {
                                      "type": "string",
                                      "format": "date",
                                      "nullable": true,
                                      "description": "Date the appointment ended. `null` for active officers."
                                    },
                                    "is_active": {
                                      "type": "boolean"
                                    },
                                    "address": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Officer's address as recorded in the registry."
                                    },
                                    "aml_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Finished",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Resub Requested"
                                      ],
                                      "nullable": true,
                                      "description": "Status of the AML screening run for this officer. `null` when no AML screening was run."
                                    },
                                    "kyc_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Started",
                                        "In Progress",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Expired",
                                        "Abandoned",
                                        "Kyc Expired",
                                        "Resubmitted",
                                        "Awaiting User"
                                      ],
                                      "nullable": true,
                                      "description": "Raw lifecycle status of the child KYC session spawned for this officer. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists."
                                    },
                                    "kyc_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true,
                                      "description": "Shareable URL of the child KYC session. `null` when no child session exists."
                                    },
                                    "kyc_session_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists."
                                    },
                                    "verification_workflow_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists."
                                    },
                                    "verification_workflow_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Workflow id used by the child session. `null` when no child session exists."
                                    },
                                    "verification_workflow_label": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Human-readable label of the child session's workflow. `null` when unset."
                                    },
                                    "verification_workflow_features": {
                                      "type": "array",
                                      "nullable": true,
                                      "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.",
                                      "items": {
                                        "type": "object",
                                        "properties": {
                                          "feature": {
                                            "type": "string",
                                            "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)."
                                          },
                                          "status": {
                                            "type": "string",
                                            "enum": [
                                              "Not Finished",
                                              "Approved",
                                              "Declined",
                                              "In Review",
                                              "Resub Requested"
                                            ],
                                            "description": "Feature-level status inside the child session."
                                          }
                                        }
                                      }
                                    },
                                    "didit_internal_id": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet."
                                    },
                                    "kyc_document_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result."
                                    },
                                    "kyc_nationality": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Nationality extracted from the document in the child KYC session. `null` until available."
                                    },
                                    "kyc_liveness_passed": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run."
                                    },
                                    "kyc_verified_at": {
                                      "type": "string",
                                      "format": "date-time",
                                      "nullable": true,
                                      "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status."
                                    },
                                    "kyc_aml_categories": {
                                      "type": "object",
                                      "nullable": true,
                                      "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.",
                                      "properties": {
                                        "sanctions": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "pep": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "adverse_media": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "warnings": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        }
                                      }
                                    },
                                    "kyc_notification_sent_at": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent."
                                    }
                                  }
                                }
                              },
                              "beneficial_owners": {
                                "type": "array",
                                "description": "Beneficial owners discovered via the registry check, each enriched with the status of any child KYC session and AML screening spawned for them.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "uuid": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "name": {
                                      "type": "string",
                                      "description": "Beneficial owner's full name (or company name for corporate UBOs)."
                                    },
                                    "first_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "last_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "date_of_birth": {
                                      "type": "string",
                                      "format": "date",
                                      "nullable": true
                                    },
                                    "country_of_birth": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "email": {
                                      "type": "string",
                                      "format": "email",
                                      "nullable": true
                                    },
                                    "company_name": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "For corporate UBOs (`entity_type = company`): the owning company's name."
                                    },
                                    "registration_number": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "For corporate UBOs: the owning company's registration number."
                                    },
                                    "entity_type": {
                                      "type": "string",
                                      "enum": [
                                        "person",
                                        "company"
                                      ]
                                    },
                                    "nationality": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "designation": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "roles": {
                                      "type": "array",
                                      "description": "Role names held by this owner, e.g. `[\"ubo\", \"shareholder\"]`.",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "ownership_min_shares": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Lower bound of the ownership percentage as reported by the registry. Free-form string (e.g. `\"25\"`, `\"25-50%\"`); use `effective_ownership_percent` for a parsed numeric value."
                                    },
                                    "ownership_max_shares": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Upper bound of the ownership percentage as reported by the registry. Free-form string; use `effective_ownership_percent` for a parsed numeric value."
                                    },
                                    "is_active": {
                                      "type": "boolean"
                                    },
                                    "aml_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Finished",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Resub Requested"
                                      ],
                                      "nullable": true,
                                      "description": "Status of the AML screening run for this beneficial owner. `null` when no AML screening was run."
                                    },
                                    "kyc_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Started",
                                        "In Progress",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Expired",
                                        "Abandoned",
                                        "Kyc Expired",
                                        "Resubmitted",
                                        "Awaiting User"
                                      ],
                                      "nullable": true,
                                      "description": "Raw lifecycle status of the child KYC session spawned for this beneficial owner. This is the full session status enum (not a reduced Approved/Declined/Pending set). `null` when no child KYC session exists."
                                    },
                                    "kyc_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true,
                                      "description": "Shareable URL of the child KYC session. `null` when no child session exists."
                                    },
                                    "kyc_session_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Session id of the child KYC session. Pass it to this same decision endpoint to fetch the person's full KYC report. `null` when no child session exists."
                                    },
                                    "verification_workflow_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Base workflow type of the child session's workflow (e.g. `kyc`). `null` for graph-based workflows or when no child session exists."
                                    },
                                    "verification_workflow_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Workflow id used by the child session. `null` when no child session exists."
                                    },
                                    "verification_workflow_label": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Human-readable label of the child session's workflow. `null` when unset."
                                    },
                                    "verification_workflow_features": {
                                      "type": "array",
                                      "nullable": true,
                                      "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.",
                                      "items": {
                                        "type": "object",
                                        "properties": {
                                          "feature": {
                                            "type": "string",
                                            "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)."
                                          },
                                          "status": {
                                            "type": "string",
                                            "enum": [
                                              "Not Finished",
                                              "Approved",
                                              "Declined",
                                              "In Review",
                                              "Resub Requested"
                                            ],
                                            "description": "Feature-level status inside the child session."
                                          }
                                        }
                                      }
                                    },
                                    "didit_internal_id": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Stable internal identifier of the verified individual behind the child session — useful for cross-session linking. `null` when no child session exists or the user has not been resolved yet."
                                    },
                                    "kyc_document_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Document type presented in the child KYC session (e.g. `P`, `ID`, `DL`). `null` until the child session has an ID verification result."
                                    },
                                    "kyc_nationality": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Nationality extracted from the document in the child KYC session. `null` until available."
                                    },
                                    "kyc_liveness_passed": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether the child KYC session's liveness check passed. `null` when no liveness check has run."
                                    },
                                    "kyc_verified_at": {
                                      "type": "string",
                                      "format": "date-time",
                                      "nullable": true,
                                      "description": "When the child KYC session reached `Approved`. `null` while the child session has any other status."
                                    },
                                    "kyc_aml_categories": {
                                      "type": "object",
                                      "nullable": true,
                                      "description": "Per-category summary derived from the person's AML hits. `null` when there are no hits.",
                                      "properties": {
                                        "sanctions": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "pep": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "adverse_media": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        },
                                        "warnings": {
                                          "type": "string",
                                          "enum": [
                                            "match",
                                            "clear"
                                          ]
                                        }
                                      }
                                    },
                                    "kyc_notification_sent_at": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "ISO 8601 timestamp of the last verification-invite notification sent for this person's child session. `null` when none was sent."
                                    },
                                    "child_beneficial_owners": {
                                      "type": "array",
                                      "description": "For corporate UBOs: the owning entity's own beneficial owners, as raw objects extracted from the registry. Empty array when not applicable.",
                                      "items": {
                                        "type": "object"
                                      }
                                    },
                                    "effective_ownership_percent": {
                                      "type": "number",
                                      "nullable": true,
                                      "description": "Parsed numeric ownership percentage — the max bound when available, otherwise the min bound. `null` when neither bound could be parsed."
                                    }
                                  }
                                }
                              }
                            }
                          },
                          "submitted": {
                            "type": "object",
                            "description": "Parties submitted by the business admin during the Key People flow (BusinessParty rows where source=USER).",
                            "properties": {
                              "parties": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "description": "Party submitted by the business admin via the Key People flow (source=USER).",
                                  "properties": {
                                    "uuid": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "entity_type": {
                                      "type": "string",
                                      "enum": [
                                        "person",
                                        "company"
                                      ]
                                    },
                                    "source": {
                                      "type": "string",
                                      "enum": [
                                        "USER"
                                      ],
                                      "description": "Always `USER` here — this bucket only contains parties entered or confirmed by the end user in the Key People flow (registry-discovered people appear under `registry`)."
                                    },
                                    "name": {
                                      "type": "string"
                                    },
                                    "first_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "last_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "company_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "registration_number": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "email": {
                                      "type": "string",
                                      "format": "email",
                                      "nullable": true
                                    },
                                    "nationality": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Nationality for persons; country of incorporation for company parties."
                                    },
                                    "phone_number": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "date_of_birth": {
                                      "type": "string",
                                      "format": "date",
                                      "nullable": true
                                    },
                                    "position": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Free-text job title / position (persons only)."
                                    },
                                    "custom_fields": {
                                      "type": "object",
                                      "description": "Workflow-defined custom fields collected for this party; keys are defined by the workflow's key-people field descriptors."
                                    },
                                    "is_active": {
                                      "type": "boolean"
                                    },
                                    "is_skipped": {
                                      "type": "boolean",
                                      "description": "True when the end user skipped verification for this party (only possible when the role allows skipping)."
                                    },
                                    "requires_verification": {
                                      "type": "boolean",
                                      "description": "True when a child KYC / KYB sub-session is required for this party based on role settings and ownership thresholds."
                                    },
                                    "roles": {
                                      "type": "array",
                                      "description": "All roles held by this party, each with its own ownership and voting percentages. There is no scalar `role`/`ownership_percent` on the party itself.",
                                      "items": {
                                        "type": "object",
                                        "properties": {
                                          "role": {
                                            "type": "string",
                                            "enum": [
                                              "director",
                                              "non_executive_director",
                                              "secretary",
                                              "chairman",
                                              "ubo",
                                              "shareholder",
                                              "representative",
                                              "founder",
                                              "legal_advisor",
                                              "authorized_signatory",
                                              "trustee",
                                              "beneficiary",
                                              "settlor",
                                              "protector",
                                              "company_officer",
                                              "investor",
                                              "other"
                                            ]
                                          },
                                          "ownership_percent": {
                                            "type": "number",
                                            "nullable": true
                                          },
                                          "voting_percent": {
                                            "type": "number",
                                            "nullable": true
                                          }
                                        }
                                      }
                                    },
                                    "kyc_session_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Child KYC session id for person parties requiring verification. `null` otherwise."
                                    },
                                    "kyc_session_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Started",
                                        "In Progress",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Expired",
                                        "Abandoned",
                                        "Kyc Expired",
                                        "Resubmitted",
                                        "Awaiting User"
                                      ],
                                      "nullable": true,
                                      "description": "Lifecycle status of the child KYC session. `null` when no child KYC session exists."
                                    },
                                    "kyc_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true
                                    },
                                    "kyb_sub_session_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Child KYB sub-session id for corporate parties requiring verification. `null` otherwise."
                                    },
                                    "kyb_sub_session_status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Started",
                                        "In Progress",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Expired",
                                        "Abandoned",
                                        "Kyc Expired",
                                        "Resubmitted",
                                        "Awaiting User"
                                      ],
                                      "nullable": true,
                                      "description": "Lifecycle status of the child KYB sub-session. `null` when no sub-session exists."
                                    },
                                    "kyb_sub_session_url": {
                                      "type": "string",
                                      "format": "uri",
                                      "nullable": true,
                                      "description": "Shareable URL of the child KYB sub-session. `null` when no sub-session exists."
                                    },
                                    "verification_workflow_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Base workflow type of the child session's workflow (KYC session preferred, falling back to the KYB sub-session). `null` for graph-based workflows or when no child session exists."
                                    },
                                    "verification_workflow_id": {
                                      "type": "string",
                                      "format": "uuid",
                                      "nullable": true,
                                      "description": "Workflow id used by the child session. `null` when no child session exists."
                                    },
                                    "verification_workflow_label": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Human-readable label of the child session's workflow. `null` when unset."
                                    },
                                    "verification_workflow_features": {
                                      "type": "array",
                                      "nullable": true,
                                      "description": "Per-feature progress of the child verification session, e.g. `[{\"feature\": \"ID_VERIFICATION\", \"status\": \"Approved\"}]`. `null` when the child session has no resolvable feature list.",
                                      "items": {
                                        "type": "object",
                                        "properties": {
                                          "feature": {
                                            "type": "string",
                                            "description": "Feature display name (e.g. `ID_VERIFICATION`, `LIVENESS`, `AML`)."
                                          },
                                          "status": {
                                            "type": "string",
                                            "enum": [
                                              "Not Finished",
                                              "Approved",
                                              "Declined",
                                              "In Review",
                                              "Resub Requested"
                                            ],
                                            "description": "Feature-level status inside the child session."
                                          }
                                        }
                                      }
                                    },
                                    "didit_internal_id": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Stable internal identifier of the verified individual behind the child session. `null` when not resolved."
                                    }
                                  }
                                }
                              }
                            }
                          },
                          "ubo_kyc_summary": {
                            "type": "object",
                            "nullable": true,
                            "description": "Aggregate UBO KYC progress, or null when no UBOs have linked KYC sessions.",
                            "properties": {
                              "total": {
                                "type": "integer"
                              },
                              "approved": {
                                "type": "integer"
                              },
                              "flagged": {
                                "type": "integer",
                                "description": "UBOs whose KYC is Declined or In Review."
                              },
                              "pending": {
                                "type": "integer"
                              }
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object"
                            }
                          }
                        }
                      }
                    },
                    "document_verifications": {
                      "type": "array",
                      "nullable": true,
                      "description": "Document verification checks grouped by workflow node. Present only when `session_kind = business`.",
                      "items": {
                        "type": "object",
                        "description": "Documents grouped by workflow node. Present only when session_kind = business.",
                        "properties": {
                          "status": {
                            "type": "string"
                          },
                          "node_id": {
                            "type": "string",
                            "nullable": true
                          },
                          "items": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "uuid": {
                                  "type": "string",
                                  "format": "uuid"
                                },
                                "document_type": {
                                  "type": "string",
                                  "enum": [
                                    "LEGAL_PRESENCE",
                                    "COMPANY_DETAILS",
                                    "OWNERSHIP_STRUCTURE",
                                    "REPRESENTATIVES_AUTHORIZATION",
                                    "OTHER"
                                  ],
                                  "nullable": true,
                                  "description": "High-level KYB document group the file was classified into. `null` while unclassified."
                                },
                                "document_subtype": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Specific OCR-detected subtype (e.g. `CERTIFICATE_OF_INCORPORATION`, `SHAREHOLDER_REGISTRY`); `UNKNOWN` when the OCR could not confidently classify the document."
                                },
                                "document_group": {
                                  "type": "string",
                                  "enum": [
                                    "LEGAL_PRESENCE",
                                    "COMPANY_DETAILS",
                                    "OWNERSHIP_STRUCTURE",
                                    "REPRESENTATIVES_AUTHORIZATION",
                                    "OTHER"
                                  ],
                                  "nullable": true,
                                  "description": "Alias of `document_type` — the group name used by `groups` and `required_groups`."
                                },
                                "file_url": {
                                  "type": "string",
                                  "format": "uri",
                                  "nullable": true,
                                  "description": "Short-lived presigned link to the uploaded file. `null` for analyst-created document request placeholders (`is_requested = true`) that have not been fulfilled yet."
                                },
                                "original_filename": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Filename as uploaded by the user or analyst."
                                },
                                "file_size": {
                                  "type": "integer",
                                  "nullable": true,
                                  "description": "File size in bytes."
                                },
                                "status": {
                                  "type": "string",
                                  "enum": [
                                    "Not Finished",
                                    "Approved",
                                    "Declined",
                                    "In Review",
                                    "Resub Requested"
                                  ],
                                  "description": "Per-document verification status."
                                },
                                "created_at": {
                                  "type": "string",
                                  "format": "date-time"
                                },
                                "cross_check_result": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Cross-reference of the OCR-extracted document fields against the registry company profile. An object with `critical`, `non_critical` and `people` sections, each mapping a field name (e.g. `company_name`, `registration_number`, `incorporation_date`, `registered_address`) to a comparison record like `{\"document\": \"...\", \"registry\": \"...\", \"match\": true, \"score\": 0.97}`. `null` when the document had no OCR data or there was no registry company to compare against."
                                },
                                "document_metadata": {
                                  "type": "object",
                                  "nullable": true,
                                  "description": "Metadata extracted from the uploaded corporate document. When metadata extraction succeeded, all 16 keys are always present (individual values are `null` when not applicable to the file type); the whole object is `null` when nothing could be extracted. For PDFs, `overlay_manipulation` carries forensic overlay-text analysis.",
                                  "properties": {
                                    "file_size": {
                                      "type": "integer",
                                      "nullable": true,
                                      "description": "Size of the document."
                                    },
                                    "content_type": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Content type of the document."
                                    },
                                    "creation_date": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Date when the document was created."
                                    },
                                    "modified_date": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Date when the document was modified."
                                    },
                                    "creator": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Authoring tool/user recorded in the file metadata (PDF `Creator`)."
                                    },
                                    "producer": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Software that produced the file (PDF `Producer`)."
                                    },
                                    "software": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Editing software recorded in the file metadata."
                                    },
                                    "encryption": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "Encryption scheme of the file, when encrypted."
                                    },
                                    "is_signed": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether the document carries a digital signature."
                                    },
                                    "is_tampered": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether the digital signature failed validation (post-signing modification)."
                                    },
                                    "signature_info": {
                                      "type": "object",
                                      "nullable": true,
                                      "description": "Details of the digital signature(s), when present."
                                    },
                                    "exif_original_date": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "EXIF original capture date, for image uploads."
                                    },
                                    "exif_digitized_date": {
                                      "type": "string",
                                      "nullable": true,
                                      "description": "EXIF digitization date, for image uploads."
                                    },
                                    "processed_by_known_editor": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether metadata indicates the file passed through a known image/PDF editor."
                                    },
                                    "has_different_creation_mod_date": {
                                      "type": "boolean",
                                      "nullable": true,
                                      "description": "Whether creation and modification dates differ — a possible (but not conclusive) editing signal."
                                    },
                                    "overlay_manipulation": {
                                      "type": "object",
                                      "nullable": true,
                                      "description": "PDF forensic analysis for suspected overlay-text manipulation. Null when no overlay manipulation evidence was found or the document was not a PDF.",
                                      "properties": {
                                        "detected": {
                                          "type": "boolean",
                                          "description": "Whether overlay-text manipulation evidence was detected."
                                        },
                                        "analyzed": {
                                          "type": "boolean",
                                          "description": "Whether the PDF could be analyzed."
                                        },
                                        "signals": {
                                          "type": "array",
                                          "items": {
                                            "type": "string"
                                          },
                                          "description": "Forensic signals that fired, such as duplicate_font_subset or glyph_fragmentation."
                                        },
                                        "manipulated_regions": {
                                          "type": "array",
                                          "description": "Suspected manipulated areas in PDF page coordinates.",
                                          "items": {
                                            "type": "object",
                                            "properties": {
                                              "page": {
                                                "type": "integer"
                                              },
                                              "x": {
                                                "type": "number"
                                              },
                                              "y": {
                                                "type": "number"
                                              },
                                              "width": {
                                                "type": "number"
                                              },
                                              "height": {
                                                "type": "number"
                                              },
                                              "page_width": {
                                                "type": "number"
                                              },
                                              "page_height": {
                                                "type": "number"
                                              }
                                            }
                                          }
                                        }
                                      }
                                    }
                                  }
                                },
                                "description": {
                                  "type": "string",
                                  "nullable": true,
                                  "description": "Analyst-provided label or reason for a document request. `null` for regular uploads."
                                },
                                "is_requested": {
                                  "type": "boolean",
                                  "description": "True for analyst-created document request placeholders awaiting an upload."
                                },
                                "ocr_data": {
                                  "type": "object",
                                  "nullable": true
                                },
                                "is_console_upload": {
                                  "type": "boolean",
                                  "description": "True for documents uploaded from the console rather than the end-user flow. Console uploads never change the feature-level aggregate status."
                                },
                                "is_confirmed": {
                                  "type": "boolean",
                                  "description": "Console uploads start `false` and flip `true` once the admin confirms them; end-user uploads are always `true`."
                                }
                              }
                            }
                          },
                          "groups": {
                            "type": "object",
                            "description": "Per-group progress counters keyed by group name (e.g. legal_presence, ownership_structure).",
                            "additionalProperties": {
                              "type": "object",
                              "properties": {
                                "total": {
                                  "type": "integer"
                                },
                                "approved": {
                                  "type": "integer"
                                },
                                "pending": {
                                  "type": "integer"
                                },
                                "declined": {
                                  "type": "integer"
                                },
                                "in_review": {
                                  "type": "integer"
                                },
                                "other": {
                                  "type": "integer"
                                },
                                "missing": {
                                  "type": "integer",
                                  "description": "Requested placeholders with no file uploaded yet."
                                }
                              }
                            }
                          },
                          "required_groups": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "warnings": {
                            "type": "array",
                            "items": {
                              "type": "object"
                            }
                          }
                        }
                      }
                    },
                    "environment": {
                      "type": "string",
                      "enum": [
                        "live",
                        "sandbox"
                      ],
                      "description": "Whether the session was created by a live application (`live`) or a sandbox application (`sandbox`). Sandbox sessions are free, fully simulated, and never bill credits."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Authentication or authorization failed. **This endpoint never returns `401`** — Didit's API-key authentication does not emit a `WWW-Authenticate` challenge, so missing or invalid credentials also surface as `403`. Two `detail` strings distinguish the failure modes:\n\n- `\"Authentication credentials were not provided or are invalid.\"` — no `x-api-key` header (and no `Authorization: Bearer` token), or the supplied credential failed introspection (revoked, expired, or malformed).\n- `\"You do not have permission to perform this action.\"` — the credential is valid but not allowed to read this session: a console-user Bearer token lacks the `read:sessions` permission or cannot access the owning application, or an API key belongs to a different organization than the session.\n\nNote that for a completely unknown `sessionId` the endpoint resolves the owning application before validating credentials, so a missing session returns `404` even when the credential is missing or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Authentication credentials were not provided or are invalid."
                    }
                  }
                },
                "examples": {
                  "Missing or invalid credentials": {
                    "summary": "No x-api-key header, or the key failed introspection",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Insufficient permissions": {
                    "summary": "Valid credential lacking read:sessions or access to the owning application",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Session not found. Returned when no live session matches the supplied `sessionId` in either the User Verification (KYC) or Business Verification (KYB) table — including soft-deleted sessions. Body is the standard `{\"detail\": ...}` envelope.\n\n**Two distinct `detail` strings.** If the `sessionId` does not exist in either table, the pre-auth owner lookup fails and you receive `{\"detail\": \"Not found.\"}` — this happens before credential validation, so it is returned even for unauthenticated calls. If the session exists but cannot be retrieved under the authenticated application (for example a stale or just-deleted record), the view returns `{\"detail\": \"Session not found.\"}`. Both indicate the same outcome — the session is not accessible — so match on the `404` status code rather than the string.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Not found."
                    }
                  }
                },
                "examples": {
                  "Unknown session id": {
                    "summary": "sessionId does not exist in either session table",
                    "value": {
                      "detail": "Not found."
                    }
                  },
                  "Session not retrievable": {
                    "summary": "Session exists but is not retrievable for this application",
                    "value": {
                      "detail": "Session not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. GET requests are limited to **600 per minute per credential** (API key, Bearer token, or source IP when unauthenticated). Inspect `Retry-After` / `X-RateLimit-Reset` and back off before retrying. If you hit this limit while polling for decisions, switch to the `status.updated` webhook and fetch the decision once per status change instead.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying the request.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "example": 30
                }
              },
              "X-RateLimit-Limit": {
                "description": "Maximum number of requests allowed in the current window (600 for GET requests).",
                "schema": {
                  "type": "integer",
                  "example": 600
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests remaining in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix timestamp (seconds) at which the current window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000000
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "description": "Human-readable explanation of the rate-limit breach.",
                      "example": "GET request rate limit exceeded. You can make up to 600 requests per minute."
                    }
                  }
                },
                "examples": {
                  "Throttled": {
                    "summary": "More than 600 GET requests in one minute from the same credential",
                    "value": {
                      "detail": "GET request rate limit exceeded. You can make up to 600 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "description": "UUID of the verification session whose decision you want to retrieve. This is the `session_id` returned by `POST /v3/session/`. The same path works for both User Verification (KYC) and Business Verification (KYB) sessions — the server looks the id up in both tables.\n\n**Must be a valid UUID.** The route only matches well-formed UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. Validate the UUID format client-side so you can distinguish an input error from a genuine missing-session `404`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Comma-separated list of extra payload sections. The only supported value is `events`. When `include=events` is passed, the response additionally contains the session event timeline (`events`), the full activity log (`activity_reviews`), blocklist flags (`blocklisted`), a per-session `cost_breakdown`, workflow metadata (`workflow_type`, `workflow_version`, `workflow_version_id`), and session metadata (`api_service` — string or `null`, set only for standalone-API sessions; `session_type` — `API`, `HOSTED`, or `MIGRATED`; `has_device_nfc_support` — boolean). Two fields change relative to the plain shape: the top-level `session_kind` discriminator is **omitted** (the events shape only exists for user sessions), and the `features` array switches from plain strings to `{feature, node_id}` objects — where the email feature is reported as `EMAIL` instead of `EMAIL_VERIFICATION`. This expanded shape is primarily used by the Didit Console; most API integrations should omit the parameter. **KYB limitation:** `include=events` only applies to user (KYC) sessions. Business sessions (`session_kind = \"business\"`) always return the standard business decision shape — `events`, `activity_reviews`, `blocklisted`, `cost_breakdown` and the extra `workflow_*`/session metadata are never returned for business sessions, and their `features` array stays a plain string array.",
            "schema": {
              "type": "string",
              "enum": [
                "events"
              ],
              "example": "events"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET 'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/decision/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Accept: application/json'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nsession_id = \"11111111-2222-3333-4444-555555555555\"\nurl = f\"https://verification.didit.me/v3/session/{session_id}/decision/\"\nheaders = {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Accept\": \"application/json\",\n}\n\nresponse = requests.get(url, headers=headers, timeout=15)\nresponse.raise_for_status()\ndecision = response.json()\n\nprint(\"session_kind:\", decision[\"session_kind\"])\nprint(\"top-level status:\", decision[\"status\"])\n\n# nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor nfc in decision.get(\"nfc_verifications\") or []:\n    print(\"nfc node_id:\", nfc[\"node_id\"], \"status:\", nfc[\"status\"])\n\nfor id_check in decision.get(\"id_verifications\") or []:\n    print(\"id node_id:\", id_check[\"node_id\"], \"status:\", id_check[\"status\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const sessionId = '11111111-2222-3333-4444-555555555555';\nconst response = await fetch(\n  `https://verification.didit.me/v3/session/${sessionId}/decision/`,\n  {\n    method: 'GET',\n    headers: {\n      'x-api-key': 'YOUR_API_KEY',\n      Accept: 'application/json',\n    },\n  }\n);\n\nif (!response.ok) {\n  throw new Error(`Decision fetch failed: ${response.status}`);\n}\nconst decision = await response.json();\n\nconsole.log('session_kind:', decision.session_kind);\nconsole.log('top-level status:', decision.status);\n\n// nfc_verifications is ALWAYS an array — never a singular `nfc` field.\nfor (const nfc of decision.nfc_verifications ?? []) {\n  console.log('nfc node_id:', nfc.node_id, 'status:', nfc.status);\n}\n\nfor (const idCheck of decision.id_verifications ?? []) {\n  console.log('id node_id:', idCheck.node_id, 'status:', idCheck.status);\n}"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/delete/": {
      "delete": {
        "summary": "Soft-delete a verification session (KYC or KYB)",
        "description": "Soft-delete a single verification session — User Verification (KYC) or Business Verification (KYB) — by its `session_id`. The id is resolved against KYC sessions first, then KYB sessions, so both kinds are deleted through this one URL.\n\n**What happens on deletion:**\n- The session is stamped with a deletion timestamp and immediately disappears from every read endpoint — `GET /v3/session/{sessionId}/decision/` returns `404` and `GET /v3/sessions/` stops listing it.\n- For KYC sessions, related face, liveness, and face-match records are soft-deleted together with the session.\n- A background job then moves every stored media file owned by the session to a quarantined storage prefix: document front/back photos (full, cropped, and privacy-blurred variants), document-capture videos, portrait crops, NFC chip portrait and signature images, face reference images, liveness videos, face-match source/target images, Proof of Address documents, and any extra uploaded files (KYC); uploaded company documents and extra files (KYB). Previously issued media URLs (`https://<media-host>/...`) stop resolving once the move completes — the move is asynchronous, so a URL issued just before deletion may keep working for a short window after the `204`.\n\n**What is retained:** the underlying database records (decision, extracted data, audit events) are kept internally, marked with the deletion timestamp — they become unreachable through the API but are not erased at the moment of the call. Blocklist entries created from this session (face or document) are **not** removed — manage those with the blocklist endpoints. Credits already consumed are not refunded.\n\n**Irreversible:** there is no restore/undelete endpoint.\n\n**Side effects:** no webhook is emitted for deletions.\n\n**Idempotency:** not idempotent at the HTTP level — the first call returns `204`; repeating it returns `404` because the session no longer resolves.\n\n**Authentication:** send your application's API key in the `x-api-key` header; the session must belong to that application. Console user access tokens (`Authorization: Bearer ...`) may also call this endpoint when they carry the `delete:sessions` permission. Authentication and permission failures both return `403` — this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nTo bulk-delete KYC sessions by their numeric `session_number`, use `POST /v3/sessions/delete/` instead.",
        "operationId": "delete_v3_session_by_id",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID (`session_id`) of the User Verification (KYC) or Business Verification (KYB) session to delete, as returned when the session was created. Must be a canonical hyphenated UUID — a non-UUID value does not match the route and returns `404`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Session soft-deleted. Empty body. The media quarantine continues asynchronously after the response."
          },
          "403": {
            "description": "Authentication or permission failure. Missing/invalid credentials also return `403` — this API never returns `401`. Exception: when the `sessionId` does not resolve to a live session, the pre-auth owner lookup returns `404` first, even with missing or invalid credentials.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No x-api-key header, or the key is invalid",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Insufficient permission": {
                    "summary": "User token without delete:sessions, or credentials for an application that does not own the session",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No live KYC or KYB session with this `session_id` exists — the id is unknown or the session was already soft-deleted (repeating a successful delete lands here).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Not Found": {
                    "summary": "Unknown or already-deleted session",
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Rate limited": {
                    "summary": "Write budget exhausted",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/ \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.delete(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n)\nresponse.raise_for_status()  # 204 on success; 404 if unknown or already deleted"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/delete/',\n  {\n    method: 'DELETE',\n    headers: { 'x-api-key': 'YOUR_API_KEY' },\n  },\n);\nif (response.status !== 204) throw new Error(`HTTP ${response.status}`);"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/generate-pdf/": {
      "get": {
        "summary": "Download a PDF report for a verification session",
        "description": "Render and download a compliance-ready PDF report for a finished verification session. The same path serves both User Verification (KYC) and Business Verification (KYB) sessions — the server looks the `sessionId` up in both tables and picks the matching report template.\n\n**Eligible statuses.** The session must already carry a final or reviewable status:\n\n- KYC sessions: `Approved`, `Declined`, `In Review`, or `Kyc Expired`.\n- KYB sessions: `Approved`, `Declined`, or `In Review`.\n\nAny other status (`Not Started`, `In Progress`, `Expired`, `Abandoned`, …) returns `403` with an explanatory `detail` string — see the `403` response below.\n\n**What the report contains.** The PDF mirrors the console session view at the moment of the request:\n\n- **KYC** — rolled-up session status, the warnings list, and one section per executed feature: ID Verification (document images plus extracted fields), NFC, Liveness, Face Match, Email Verification, Phone Verification, Proof of Address, Questionnaire answers, Database Validation, AML Screening, and IP Analysis — followed by session tags, console review activity (comments and status changes), and the session event timeline.\n- **KYB** — registry checks (company data), AML screenings, business document verifications, key-people checks, questionnaire responses, phone/email verifications, IP analyses, and the session event timeline, followed by tags and review activity.\n\n**Branding.** If your application has white-label customization, the report is rendered with your logo and links to your privacy-policy URL; otherwise it carries Didit branding. Reports are rendered in English — there is no language parameter.\n\n**No caching.** Every call re-renders the PDF from the current session data, so a report generated after a manual review reflects the reviewer’s decision. Two calls for the same session can produce byte-different files — archive the downloaded file if you need an immutable copy.\n\n**Latency.** Rendering downloads every stored image (document sides, selfies, extra files) before composing the PDF, so media-heavy sessions can take several seconds. Use a generous client read timeout (60 s recommended) and stream the body to disk.\n\n**Trailing slash.** The canonical route ends with a trailing slash (`…/generate-pdf/`). Requests without it receive a `301` redirect to the slashed URL — most HTTP clients follow it automatically for `GET`, but plain `curl` needs `-L` (or call the slashed URL directly, as in the samples).\n\nIf you need the underlying data as JSON instead of a rendered report, use `GET /v3/session/{sessionId}/decision/`.",
        "operationId": "get_v3_session_generate_pdf",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "description": "UUID of the User Verification (KYC) or Business Verification (KYB) session to render — the `session_id` returned by `POST /v3/session/`. The same path works for both session kinds; use the `Content-Disposition` filename on the response to tell which report type you received.\n\n**Must be a well-formed, lowercase UUID.** The route only matches valid UUIDs, so a malformed value is rejected by the URL router before any application code runs and the response is a `404` **HTML** page rather than the JSON `{\"detail\": ...}` envelope shown below. The route's UUID converter only matches the canonical lowercase form — uppercase-hex UUIDs are rejected with the HTML 404.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The rendered PDF document, returned directly as binary `application/pdf` — there is no JSON wrapper and no download-URL indirection. The body starts with the `%PDF` magic bytes (PDF 1.7). Save it to a `.pdf` file or stream it through to your caller.",
            "headers": {
              "Content-Disposition": {
                "description": "`attachment; filename=session_{sessionId}.pdf` for KYC sessions, `attachment; filename=business_{sessionId}.pdf` for KYB sessions — the prefix tells you which report template was rendered.",
                "schema": {
                  "type": "string",
                  "example": "attachment; filename=session_11111111-2222-3333-4444-555555555555.pdf"
                }
              },
              "Content-Length": {
                "description": "Total size of the PDF in bytes; the body is not chunked.",
                "schema": {
                  "type": "integer",
                  "example": 25191
                }
              }
            },
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "403": {
            "description": "Authentication, authorization, or session-status failure. **This endpoint never returns `401`** — Didit's API-key authentication does not emit a `WWW-Authenticate` challenge, so missing or invalid credentials also surface as `403`. Distinguish the failure modes by the `detail` string:\n\n- `\"Authentication credentials were not provided or are invalid.\"` — no `x-api-key` header (and no `Authorization: Bearer` token), or the supplied credential failed introspection (revoked, expired, or malformed).\n- `\"You do not have permission to perform this action.\"` — the credential is valid but cannot read this session: a console-user Bearer token lacks the `read:sessions` permission or cannot access the owning application, or an API key belongs to a different organization than the session.\n- `\"You can only generate a PDF for sessions in review, declined, approved or kyc expired\"` — the KYC session exists but is not in an eligible status yet (for example `Not Started` or `In Progress`). Wait for the `status.updated` webhook before requesting the report.\n- `\"You can only generate a PDF for sessions in review, declined or approved\"` — same condition for KYB sessions.\n\nNote that for a completely unknown `sessionId` the endpoint resolves the owning application before validating credentials, so a missing session returns `404` even when the credential is missing or invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "You can only generate a PDF for sessions in review, declined, approved or kyc expired"
                    }
                  }
                },
                "examples": {
                  "Missing or invalid credentials": {
                    "summary": "No x-api-key header, or the key failed introspection",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Insufficient permissions": {
                    "summary": "Valid credential lacking read:sessions or access to the owning application",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "KYC session not finished": {
                    "summary": "KYC session still Not Started / In Progress",
                    "value": {
                      "detail": "You can only generate a PDF for sessions in review, declined, approved or kyc expired"
                    }
                  },
                  "KYB session not finished": {
                    "summary": "KYB session still Not Started / In Progress",
                    "value": {
                      "detail": "You can only generate a PDF for sessions in review, declined or approved"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No active session with this `sessionId` exists — it was never created, or it has been deleted. Owner resolution happens before credential validation, so this `404` is returned even for unauthenticated requests. Depending on which lookup misses, the `detail` string is `\"Not found.\"` (the common case) or `\"Session not found.\"`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Not found."
                    }
                  }
                },
                "examples": {
                  "Unknown session": {
                    "summary": "sessionId does not match any KYC or KYB session",
                    "value": {
                      "detail": "Not found."
                    }
                  },
                  "Recently deleted session": {
                    "summary": "Session was deleted between lookups",
                    "value": {
                      "detail": "Session not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. PDF generation has a dedicated limit of **50 requests per minute per credential** (API key, Bearer token, or source IP when unauthenticated), in addition to the global 600/min GET limit. Wait `Retry-After` seconds (also reflected in the `X-RateLimit-*` headers), then retry. Reports are not cached server-side, so download each report once and store the file instead of re-fetching it.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying the request.",
                "schema": {
                  "type": "integer",
                  "minimum": 1,
                  "example": 30
                }
              },
              "X-RateLimit-Limit": {
                "description": "Maximum number of PDF generations allowed in the current window (50).",
                "schema": {
                  "type": "integer",
                  "example": 50
                }
              },
              "X-RateLimit-Remaining": {
                "description": "Requests remaining in the current window.",
                "schema": {
                  "type": "integer",
                  "example": 0
                }
              },
              "X-RateLimit-Reset": {
                "description": "Unix timestamp (seconds) at which the current window resets.",
                "schema": {
                  "type": "integer",
                  "example": 1750000000
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "description": "Human-readable explanation of the rate-limit breach.",
                      "example": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute."
                    }
                  }
                },
                "examples": {
                  "Throttled": {
                    "summary": "More than 50 PDF generations in one minute from the same credential",
                    "value": {
                      "detail": "Session PDF generation rate limit exceeded. You can make up to 50 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl --fail \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  --output report.pdf"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.get(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    stream=True,\n    timeout=60,\n)\nresponse.raise_for_status()\nwith open(\"report.pdf\", \"wb\") as fh:\n    for chunk in response.iter_content(chunk_size=8192):\n        fh.write(chunk)"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import { writeFile } from 'node:fs/promises';\n\nconst response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/generate-pdf/',\n  { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!response.ok) throw new Error(`PDF generation failed: HTTP ${response.status}`);\nawait writeFile('report.pdf', Buffer.from(await response.arrayBuffer()));"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/sandbox/arm/": {
      "post": {
        "summary": "Arm a sandbox scenario on a session",
        "description": "Persist a sandbox scenario on a `Not Started` session and expand its bundled magic values (email, phone, or `expected_details` fields) onto the session, so the mocked providers reproduce the scenario's outcome once the flow runs. This is the endpoint the pre-flow scenario picker in Didit's hosted verification UI calls when a tester chooses an outcome before starting capture. Build your own picker against it, or call it directly from a sandbox test harness.\n\nAuthenticate with the session's own `Session-Token` (returned in `session_token` by [Create Session](/sessions-api/create-session)), not your application `x-api-key`: this endpoint is meant to be called from the client, not your backend. A scenario is only accepted while `status` is `Not Started`. Calling it again while still `Not Started` re-arms the session: it swaps in the new scenario's magic values and clears only the values the *previous* scenario wrote, so any value the user already typed by hand (email, first name, etc.) survives the re-arm. Arming a session that has moved past `Not Started`, or that belongs to a live (non-sandbox) application, is rejected.\n\nSee [Sandbox & Test Data](/integration/sandbox-testing) for the full scenario catalog and the other two ways to drive an outcome: `sandbox_scenario` at session create, and typing magic values directly.",
        "operationId": "post_v3_session_sandbox_arm",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "SessionTokenAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the sandbox verification session to arm. Must match the session encoded in the Session-Token used to authenticate.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scenario"
                ],
                "properties": {
                  "scenario": {
                    "type": "string",
                    "description": "Slug of the scenario to arm. See the catalog at GET /v1/sandbox/scenarios/ or the scenario table in [Sandbox & Test Data](/integration/sandbox-testing).",
                    "example": "decline_document_expired"
                  }
                }
              },
              "examples": {
                "Arm": {
                  "summary": "Arm the AML PEP-hit scenario",
                  "value": {
                    "scenario": "decline_aml_hit"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Scenario armed. Its magic values are now on the session. `status` reflects the session's current status, which this call never changes: it is always `Not Started` on success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "scenario": {
                      "type": "string",
                      "description": "The armed scenario slug (echoes the request)."
                    },
                    "status": {
                      "type": "string",
                      "example": "Not Started"
                    }
                  }
                },
                "examples": {
                  "Armed": {
                    "summary": "Scenario armed on a Not Started session",
                    "value": {
                      "session_id": "11111111-2222-3333-4444-555555555555",
                      "scenario": "decline_aml_hit",
                      "status": "Not Started"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error: an unknown scenario slug, or the session has already progressed past `Not Started`.",
            "content": {
              "application/json": {
                "examples": {
                  "Unknown Scenario": {
                    "summary": "scenario is not in the catalog",
                    "value": {
                      "scenario": [
                        "Unknown sandbox scenario: 'not-a-real-scenario'."
                      ]
                    }
                  },
                  "Already Started": {
                    "summary": "Session moved past Not Started",
                    "value": [
                      "Scenario can only be armed before the verification starts."
                    ]
                  }
                }
              }
            }
          },
          "404": {
            "description": "The Session-Token does not resolve to this `session_id`, or the session belongs to a live (non-sandbox) application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not Found": {
                    "summary": "Live session, or session_id / token mismatch",
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/ \\\n  -H 'Session-Token: YOUR_SESSION_TOKEN' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"scenario\": \"decline_aml_hit\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.post(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/\",\n    headers={\n        \"Session-Token\": \"YOUR_SESSION_TOKEN\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\"scenario\": \"decline_aml_hit\"},\n)\nresponse.raise_for_status()\nprint(response.json())  # {'session_id': '...', 'scenario': 'decline_aml_hit', 'status': 'Not Started'}"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/sandbox/arm/',\n  {\n    method: 'POST',\n    headers: {\n      'Session-Token': sessionToken,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({ scenario: 'decline_aml_hit' }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id, scenario, status } = await response.json();"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/update-status/": {
      "patch": {
        "summary": "Approve, decline, or request resubmission for a verification session",
        "description": "Manually override the final decision of a KYC or KYB verification session. Set `new_status` to `Approved` or `Declined` to record a manual decision, or to `Resubmitted` to clear specific verification steps and let the user redo them through the same verification URL. The endpoint accepts both user (KYC) and business (KYB) session IDs.\n\nCall it once a session has reached a reviewable state: the current status must be `Approved`, `Declined`, `In Review`, `Kyc Expired`, `Abandoned`, or `Resubmitted`. Sessions that are still `Not Started`, `In Progress`, or `Awaiting User`, or that ended as `Expired`, return `400`. The new status must differ from the current one, and your API key's organization needs the `write:sessions` permission. The eligible-state check runs before body validation, so an ineligible session returns the `Wrong Current Status` error even when the payload is also invalid.\n\nFor `Resubmitted`, pass `nodes_to_resubmit` to choose which workflow steps the user must redo, or omit it to auto-select existing feature attempts in a resubmittable state (`Declined`, `In Review`, `Not Finished`, `Expired`). Never-attempted features are not auto-selected — list them explicitly in `nodes_to_resubmit`, or the request returns `400` when no recorded attempt needs resubmission. Captured data for the selected steps is deleted, the steps are reordered to match the workflow graph, the session's expiration window restarts, and the original verification URL becomes usable again. Backend-only steps (`AML`, `DATABASE_VALIDATION`, `IP_ANALYSIS`) at the head of the list run immediately without user interaction; if every resubmitted step is backend-only, the session re-finalizes within the same request. `KYB_REGISTRY` and `KYB_KEY_PEOPLE` cannot be resubmitted directly — resubmit the relevant child KYC sessions instead and the parent business session recomputes automatically.\n\nEvery successful call fires the `status.updated` [webhook](/integration/webhooks) once the change commits and appends an activity entry — with actor attribution and your `comment` — to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and in the console activity timeline. The user-profile aggregates linked to the session's `vendor_data` update as well. Declining disables ongoing AML monitoring for the session; re-approving re-enables it (from `Resubmitted`, `Declined`, or `Kyc Expired`) when the workflow has monitoring turned on. Approving or declining a child KYC that belongs to a KYB key-people check recomputes the parent business session's status. Set `send_email: true` (with `email_address`) to notify the user: a status notice for `Approved`/`Declined`, or a resubmission email containing the verification link and per-step reasons for `Resubmitted`.\n\nThe operation is intentionally not repeatable with the same value: re-sending the current status returns `400` (`The new status is the same as the current status.`), so accidental double-submissions are harmless. Authentication and permission failures both return `403` — this API never responds `401`.",
        "operationId": "patch_v3_session_update_status",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the verification session to update. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "new_status"
                ],
                "properties": {
                  "new_status": {
                    "type": "string",
                    "description": "Target status. `Approved` and `Declined` record a final manual decision (each can also overturn the other). `Resubmitted` clears the selected steps and sends the session back to the user. Any other value returns `400`.",
                    "enum": [
                      "Approved",
                      "Declined",
                      "Resubmitted"
                    ],
                    "example": "Approved"
                  },
                  "comment": {
                    "type": "string",
                    "description": "Free-text reason for the change, stored on the session's review trail and returned in the `reviews` array of [Get Decision](#get-/v3/session/-sessionId-/decision/). For example `Duplicated user`.",
                    "example": "All checks passed manual review"
                  },
                  "nodes_to_resubmit": {
                    "type": "array",
                    "description": "Workflow steps the user must redo. Only acted on when `new_status` is `Resubmitted`. For `Approved`/`Declined` the entries are still schema-validated (an invalid `feature` value returns `400`) but schema-valid entries are semantically ignored. If omitted, the server auto-selects existing OCR, Liveness, Face Match, POA, Phone, Email, AML, Database Validation, and Questionnaire attempts whose status is `Declined`, `In Review`, `Not Finished`, or `Expired` (NFC, IP analysis, age estimation, face search, and KYB document attempts are never auto-selected; non-face-match face attempts are selected as `LIVENESS`) — features the user never attempted (no recorded attempt) are NOT selected, so a session with zero attempts returns `400` (\"No features found that need resubmission\") even though nothing was approved; pass `nodes_to_resubmit` explicitly in that case. Steps are executed in workflow-graph order regardless of the order you send them. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and the `KYB` alias are rejected with `400` — those parent checks recompute from their child KYC sessions.",
                    "items": {
                      "type": "object",
                      "required": [
                        "node_id",
                        "feature"
                      ],
                      "properties": {
                        "node_id": {
                          "type": "string",
                          "description": "Node identifier as it appears in the session's workflow definition (for example `feature_ocr`, `feature_liveness`).",
                          "example": "feature_ocr"
                        },
                        "feature": {
                          "type": "string",
                          "description": "Feature type of the node. The aliases `ID_VERIFICATION`, `POA`, `PHONE`, and `EMAIL` are normalized server-side to `OCR`, `PROOF_OF_ADDRESS`, `PHONE_VERIFICATION`, and `EMAIL_VERIFICATION`. `KYB_REGISTRY`, `KYB_KEY_PEOPLE`, and `KYB` pass schema validation but are always rejected with the business-rule `400` shown in the examples.",
                          "enum": [
                            "OCR",
                            "OCR_BACK",
                            "NFC",
                            "AML",
                            "FACE",
                            "LIVENESS",
                            "FACE_MATCH",
                            "IP_ANALYSIS",
                            "AGE_ESTIMATION",
                            "PROOF_OF_ADDRESS",
                            "PHONE_VERIFICATION",
                            "EMAIL_VERIFICATION",
                            "FACE_SEARCH",
                            "DATABASE_VALIDATION",
                            "QUESTIONNAIRE",
                            "DOCUMENT_AI",
                            "KYB_DOCUMENTS",
                            "ID_VERIFICATION",
                            "POA",
                            "PHONE",
                            "EMAIL",
                            "KYB_REGISTRY",
                            "KYB_KEY_PEOPLE",
                            "KYB"
                          ],
                          "example": "OCR"
                        }
                      }
                    }
                  },
                  "send_email": {
                    "type": "boolean",
                    "description": "Whether to email the user about the change. Requires `email_address`. For `Approved`/`Declined` the user receives a status notice; for `Resubmitted` the email includes the verification link and the per-step resubmission reasons.",
                    "default": false,
                    "example": false
                  },
                  "email_address": {
                    "type": "string",
                    "format": "email",
                    "description": "Recipient for the notification email. **Required when `send_email` is `true`** — omitting it returns `400`.",
                    "example": "user@example.com"
                  },
                  "email_language": {
                    "type": "string",
                    "description": "Language for the notification email. Accepts any string at schema level; unsupported codes silently fall back to English (`en`).",
                    "default": "en",
                    "example": "en"
                  }
                }
              },
              "examples": {
                "Approve Session": {
                  "summary": "Approve a session after manual review",
                  "value": {
                    "new_status": "Approved",
                    "comment": "All checks passed manual review"
                  }
                },
                "Decline Session": {
                  "summary": "Decline a session",
                  "value": {
                    "new_status": "Declined",
                    "comment": "Suspected fraud"
                  }
                },
                "Resubmit Specific Steps": {
                  "summary": "Request resubmission of chosen steps, with email",
                  "value": {
                    "new_status": "Resubmitted",
                    "nodes_to_resubmit": [
                      {
                        "node_id": "feature_ocr",
                        "feature": "OCR"
                      },
                      {
                        "node_id": "feature_liveness",
                        "feature": "LIVENESS"
                      }
                    ],
                    "send_email": true,
                    "email_address": "user@example.com",
                    "email_language": "en"
                  }
                },
                "Resubmit Auto-select": {
                  "summary": "Request resubmission (auto-select non-approved steps)",
                  "value": {
                    "new_status": "Resubmitted"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Status updated. The response contains only the `session_id` — fetch the updated session via [Get Decision](#get-/v3/session/-sessionId-/decision/). The `status.updated` webhook fires once the change commits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "UUID of the updated session."
                    }
                  }
                },
                "examples": {
                  "Updated": {
                    "summary": "Returned for Approved, Declined, and Resubmitted alike",
                    "value": {
                      "session_id": "3472fb7c-8f7c-4d1a-9cf4-cf3d74ce1a60"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field errors arrive as `{\"<field>\": [\"<message>\"]}` (some business-rule rejections use a string value instead: `{\"nodes_to_resubmit\": \"<message>\"}`), cross-state errors as `{\"detail\": \"<message>\"}`, and the same-status error as a bare JSON array.",
            "content": {
              "application/json": {
                "examples": {
                  "Wrong Current Status": {
                    "summary": "Session is not in an updatable state",
                    "value": {
                      "detail": "Only sessions with status Approved, Declined, In Review, Kyc Expired, Abandoned, Resubmitted can be updated. Current status: In Progress"
                    }
                  },
                  "Invalid Status Value": {
                    "summary": "new_status is a real session status but not one of Approved/Declined/Resubmitted",
                    "value": {
                      "new_status": [
                        "You can only update the status to 'Approved', 'Declined', or 'Resubmitted'."
                      ]
                    }
                  },
                  "Missing new_status": {
                    "summary": "Required field omitted",
                    "value": {
                      "new_status": [
                        "This field is required."
                      ]
                    }
                  },
                  "Same Status": {
                    "summary": "New status equals the current status",
                    "value": [
                      "The new status is the same as the current status."
                    ]
                  },
                  "Email Address Required": {
                    "summary": "send_email true without email_address",
                    "value": {
                      "email_address": [
                        "Email address is required when send_email is true."
                      ]
                    }
                  },
                  "Non-resubmittable Feature": {
                    "summary": "Resubmit targets a KYB parent check",
                    "value": {
                      "nodes_to_resubmit": "The following features cannot be resubmitted directly: KYB_REGISTRY. Resubmit the relevant child KYC sessions instead; the parent will recompute automatically."
                    }
                  },
                  "Nothing To Resubmit": {
                    "summary": "Auto-select found no non-approved features",
                    "value": {
                      "nodes_to_resubmit": "No features found that need resubmission. All features are already approved."
                    }
                  },
                  "Invalid Feature Choice": {
                    "summary": "Unknown feature in nodes_to_resubmit",
                    "value": {
                      "nodes_to_resubmit": [
                        {
                          "feature": [
                            "\"BOGUS\" is not a valid choice. Valid choices are: OCR, OCR_BACK, NFC, AML, FACE, LIVENESS, FACE_MATCH, IP_ANALYSIS, AGE_ESTIMATION, PROOF_OF_ADDRESS, PHONE_VERIFICATION, EMAIL_VERIFICATION, FACE_SEARCH, DATABASE_VALIDATION, QUESTIONNAIRE, KYB_REGISTRY, KYB_DOCUMENTS, KYB_KEY_PEOPLE, ID_VERIFICATION, POA, PHONE, EMAIL, KYB"
                          ]
                        }
                      ]
                    }
                  },
                  "Invalid Status Value (not a status at all)": {
                    "summary": "new_status is not a valid StatusChoices value",
                    "value": {
                      "new_status": [
                        "\"Bogus\" is not a valid choice."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "API key lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No KYC or KYB session with this `session_id` exists in your application (soft-deleted sessions also return `404`).",
            "content": {
              "application/json": {
                "examples": {
                  "Not Found": {
                    "summary": "Unknown session_id",
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"new_status\": \"Approved\",\n    \"comment\": \"All checks passed manual review\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/\",\n    headers={\n        \"x-api-key\": \"YOUR_API_KEY\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"new_status\": \"Resubmitted\",\n        \"nodes_to_resubmit\": [\n            {\"node_id\": \"feature_ocr\", \"feature\": \"OCR\"},\n            {\"node_id\": \"feature_liveness\", \"feature\": \"LIVENESS\"},\n        ],\n        \"send_email\": True,\n        \"email_address\": \"user@example.com\",\n        \"email_language\": \"en\",\n    },\n)\nresponse.raise_for_status()\nprint(response.json())  # {'session_id': '11111111-2222-3333-4444-555555555555'}"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-status/',\n  {\n    method: 'PATCH',\n    headers: {\n      'x-api-key': process.env.DIDIT_API_KEY,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      new_status: 'Declined',\n      comment: 'Suspected fraud',\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { session_id } = await response.json();"
          }
        ]
      }
    },
    "/v3/id-verification/": {
      "post": {
        "summary": "ID Verification (document OCR + fraud checks)",
        "description": "OCR an identity document and run document fraud checks in one call — returns the extracted holder fields, the parsed MRZ, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found. Supports 14,000+ document types from 220+ countries and territories.\n\n**How it works.** Send the document's `front_image` (required) and, for two-sided documents, the `back_image`. Upload uncropped photos with all four corners of the document visible — the service detects, aligns, and crops the document itself, so do not pre-crop. The document is classified automatically (you never declare the country or document type), the visual zone is read with OCR, and the MRZ and any barcodes are decoded. Cross-checks then validate dates, number formats, and MRZ check digits, and compare the visual zone against the MRZ. With `perform_document_liveness=true`, the images are additionally screened for screen replays, printed copies, and portrait manipulation.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Fraud and hard-failure risks always decline: `DOCUMENT_EXPIRED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, plus extraction failures (`NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`, `DOCUMENT_NUMBER_NOT_DETECTED`). (`PORTRAIT_IMAGE_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `INVALID_DATE`, and `MRZ_NOT_DETECTED` exist in workflow sessions but are never produced by this standalone endpoint.) Three risk groups are configurable per request via `invalid_mrz_action`, `inconsistent_data_action`, and `expiration_date_not_detected_action` (`DECLINE` or `NO_ACTION`). All other warnings (e.g. `POSSIBLE_DUPLICATED_USER`) are informational and never decline on their own. A document that cannot be processed at all returns `400` with `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `id_verification.status` and `id_verification.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one ID Verification API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the cropped document/portrait images are stored and returned as short-lived media URLs (`https://<media-host>/ocr/...`), and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored, `request_id` is a one-off correlation UUID, and `portrait_image`/`front_image`/`back_image` are returned inline as base64-encoded JPEG strings.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox User\" USA identity card), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3id-verification",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "front_image"
                ],
                "properties": {
                  "front_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Front side of the identity document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`, `pdf`. Maximum upload size: **10 MB** (larger files are rejected with `400`). PDFs are rendered to an image before OCR; encrypted PDFs require `front_image_password`. Images are automatically compressed to ~0.5 MB and EXIF orientation is applied. Upload the full, uncropped capture with all four corners of the document visible."
                  },
                  "front_image_password": {
                    "type": "string",
                    "description": "Password to decrypt `front_image` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.",
                    "writeOnly": true
                  },
                  "back_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Back side of the document — send it whenever the document has one (many ID cards carry the MRZ or a barcode on the back; omitting it simply leaves the MRZ and back-side fields empty in the response — this endpoint does not raise a missing-MRZ warning). Same format and size limits as `front_image`. When omitted, `back_image` and back-side fields are `null` in the response."
                  },
                  "back_image_password": {
                    "type": "string",
                    "description": "Password to decrypt `back_image` when it is an encrypted PDF.",
                    "writeOnly": true
                  },
                  "perform_document_liveness": {
                    "type": "boolean",
                    "default": false,
                    "description": "When `true`, also screens both images for presentation fraud: screen replays (`SCREEN_CAPTURE_DETECTED`), printed copies (`PRINTED_COPY_DETECTED`), and portrait manipulation (`PORTRAIT_MANIPULATION_DETECTED`). Any of these auto-declines. Adds latency, so enable it only when you need fraud screening.",
                    "example": true
                  },
                  "minimum_age": {
                    "type": "integer",
                    "nullable": true,
                    "default": null,
                    "minimum": 1,
                    "maximum": 120,
                    "description": "Accepted and validated (1–120) but **currently not applied** by this endpoint — it never produces a `MINIMUM_AGE_NOT_MET` warning from this field. Enforce age rules from the returned `date_of_birth`/`age`, or use a verification-session workflow with age restrictions."
                  },
                  "expiration_date_not_detected_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when no expiration date can be read (`EXPIRATION_DATE_NOT_DETECTED`). Default `NO_ACTION` because many identity documents print no expiry date. Note: a *detected and past* expiry date always declines (`DOCUMENT_EXPIRED`), regardless of this option."
                  },
                  "invalid_mrz_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when the extracted MRZ fails check-digit validation (`MRZ_VALIDATION_FAILED`). A missing MRZ raises no warning on this endpoint. Only relevant for documents that carry an MRZ. `REVIEW` is not supported on this endpoint and returns `400`."
                  },
                  "inconsistent_data_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when extracted data is internally inconsistent: `DATA_INCONSISTENT`, `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` (visual zone disagrees with the MRZ), or `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS`."
                  },
                  "preferred_characters": {
                    "type": "string",
                    "enum": [
                      "latin",
                      "non_latin"
                    ],
                    "default": "latin",
                    "description": "Preferred script for name/address fields on documents that print both Latin and non-Latin text (Arabic, Cyrillic, CJK, …). `latin` returns transliterated/Latin values; `non_latin` prefers the native script."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with document images returned as media URLs. When `false`, nothing is stored, `request_id` is a transient UUID, and images are returned inline as base64 JPEG strings.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.",
                    "example": {
                      "flow": "onboarding"
                    }
                  }
                }
              },
              "example": {
                "front_image": "(binary JPEG/PNG/PDF of the document front)",
                "back_image": "(binary JPEG/PNG/PDF of the document back)",
                "perform_document_liveness": true,
                "save_api_request": true,
                "vendor_data": "user-123",
                "metadata": {
                  "flow": "onboarding"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document processed. `id_verification.status` is `Approved` or `Declined`; every detected issue is itemized in `id_verification.warnings`. A problematic document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id and image fields are short-lived media URLs; with `save_api_request=false` they are inline base64 JPEG strings.",
            "content": {
              "application/json": {
                "examples": {
                  "Approved": {
                    "summary": "Two-sided ID card read cleanly (saved — images as media URLs)",
                    "value": {
                      "request_id": "11d219ed-d59c-4b1d-8d65-9b933f12d5b8",
                      "id_verification": {
                        "status": "Approved",
                        "document_type": "Identity Card",
                        "document_subtype": "ID_CARD_GENERIC",
                        "document_number": "CBX164224",
                        "personal_number": "20446581H",
                        "portrait_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-portrait_image-8c2f.jpg?signature=...",
                        "front_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-front_image-4a1e.jpg?signature=...",
                        "back_image": "https://<media-host>/ocr/11d219ed-d59c-4b1d-8d65-9b933f12d5b8-back_image-77b0.jpg?signature=...",
                        "front_image_camera_front": null,
                        "back_image_camera_front": null,
                        "front_image_camera_front_face_match_score": null,
                        "back_image_camera_front_face_match_score": null,
                        "front_image_quality_score": {
                          "focus_score": 78,
                          "brightness_score": 83.2,
                          "brightness_issue": "ok",
                          "is_document_fully_visible": true,
                          "resolution_score": 40.7,
                          "overall_score": 70.2
                        },
                        "back_image_quality_score": {
                          "focus_score": 100,
                          "brightness_score": 95.6,
                          "brightness_issue": "ok",
                          "is_document_fully_visible": true,
                          "resolution_score": 43,
                          "overall_score": 84.4
                        },
                        "date_of_birth": "1980-01-13",
                        "age": 46,
                        "expiration_date": "2032-03-31",
                        "date_of_issue": "2022-03-31",
                        "issuing_state": "ESP",
                        "issuing_state_name": "Spain",
                        "first_name": "Julio Francisco",
                        "last_name": "Fores Sena",
                        "full_name": "Julio Francisco Fores Sena",
                        "gender": "M",
                        "address": "Brda. Urb. El Cardonal 0052 52 Po3 B,Taco,San Cristobal De La Laguna,Santa Cruz De Tenerife",
                        "formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
                        "place_of_birth": "Valencia, Valencia",
                        "marital_status": "UNKNOWN",
                        "nationality": "ESP",
                        "extra_fields": {
                          "first_surname": "Fores",
                          "second_surname": "Sena"
                        },
                        "mrz": {
                          "surname": "FORES SENA",
                          "name": "JULIO FRANCISCO",
                          "country": "ESP",
                          "nationality": "ESP",
                          "birth_date": "800113",
                          "expiry_date": "320331",
                          "sex": "M",
                          "document_type": "ID",
                          "document_number": "CBX164224",
                          "optional_data": "20446581H",
                          "optional_data_2": "",
                          "birth_date_hash": "9",
                          "expiry_date_hash": "8",
                          "document_number_hash": "3",
                          "final_hash": "3",
                          "personal_number": "20446581H",
                          "warnings": [],
                          "errors": [],
                          "mrz_type": "TD1",
                          "mrz_string": "IDESPCBX164224320446581H<<<<<<\n8001139M3203318ESP<<<<<<<<<<<3\nFORES<SENA<<JULIO<FRANCISCO<<<",
                          "mrz_key": "CBX164224380011393203318"
                        },
                        "parsed_address": {
                          "street_1": "Avenida el Cardonal 52",
                          "street_2": "b",
                          "city": "La Laguna",
                          "region": "Canarias",
                          "country": "ES",
                          "postal_code": "38108",
                          "address_type": "Avenida",
                          "formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
                          "raw_results": {
                            "address_components": [
                              {
                                "long_name": "b",
                                "short_name": "b",
                                "types": [
                                  "subpremise"
                                ]
                              },
                              {
                                "long_name": "52",
                                "short_name": "52",
                                "types": [
                                  "street_number"
                                ]
                              },
                              {
                                "long_name": "Avenida el Cardonal",
                                "short_name": "Av. el Cardonal",
                                "types": [
                                  "route"
                                ]
                              },
                              {
                                "long_name": "La Laguna",
                                "short_name": "La Laguna",
                                "types": [
                                  "locality",
                                  "political"
                                ]
                              },
                              {
                                "long_name": "Spain",
                                "short_name": "ES",
                                "types": [
                                  "country",
                                  "political"
                                ]
                              },
                              {
                                "long_name": "38108",
                                "short_name": "38108",
                                "types": [
                                  "postal_code"
                                ]
                              }
                            ],
                            "formatted_address": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain",
                            "geometry": {
                              "location": {
                                "lat": 28.4501161,
                                "lng": -16.3004407
                              },
                              "location_type": "ROOFTOP"
                            },
                            "types": [
                              "street_address",
                              "subpremise"
                            ]
                          },
                          "document_location": {
                            "latitude": 28.4501161,
                            "longitude": -16.3004407
                          },
                          "label": "Spain Identity Card Address",
                          "is_verified": true,
                          "category": "Residential"
                        },
                        "warnings": [],
                        "barcodes": []
                      },
                      "vendor_data": "user-123",
                      "metadata": {
                        "flow": "onboarding"
                      },
                      "created_at": "2026-06-12T02:21:56.013573+00:00"
                    }
                  },
                  "Declined - expired document": {
                    "summary": "Front-only upload of an expired ID card",
                    "value": {
                      "request_id": "f61681b8-9422-4378-ac3e-04c6a247fb45",
                      "id_verification": {
                        "status": "Declined",
                        "document_type": "Identity Card",
                        "document_subtype": "ID_CARD_GENERIC",
                        "document_number": "BNK123072",
                        "personal_number": "21771255F",
                        "portrait_image": "https://<media-host>/ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-portrait_image-1d9a.jpg?signature=...",
                        "front_image": "https://<media-host>/ocr/f61681b8-9422-4378-ac3e-04c6a247fb45-front_image-5c2b.jpg?signature=...",
                        "back_image": null,
                        "front_image_camera_front": null,
                        "back_image_camera_front": null,
                        "front_image_camera_front_face_match_score": null,
                        "back_image_camera_front_face_match_score": null,
                        "front_image_quality_score": {
                          "focus_score": 100,
                          "brightness_score": 95.7,
                          "brightness_issue": "ok",
                          "is_document_fully_visible": true,
                          "resolution_score": 22.2,
                          "overall_score": 79.3
                        },
                        "back_image_quality_score": null,
                        "date_of_birth": "1997-01-30",
                        "age": 29,
                        "expiration_date": "2025-11-26",
                        "date_of_issue": null,
                        "issuing_state": "ESP",
                        "issuing_state_name": "Spain",
                        "first_name": "Lucia",
                        "last_name": "Marin Castro",
                        "full_name": "Lucia Marin Castro",
                        "gender": "F",
                        "address": null,
                        "formatted_address": null,
                        "place_of_birth": null,
                        "marital_status": "UNKNOWN",
                        "nationality": "ESP",
                        "extra_fields": {
                          "first_surname": "Marin",
                          "second_surname": "Castro"
                        },
                        "mrz": {},
                        "parsed_address": null,
                        "warnings": [
                          {
                            "risk": "DOCUMENT_EXPIRED",
                            "feature": "ID_VERIFICATION",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Document expired",
                            "long_description": "The document's expiration date has passed, rendering it no longer valid for use."
                          }
                        ],
                        "barcodes": []
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:25:48.117204+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "id_verification": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why."
                        },
                        "document_type": {
                          "type": "string",
                          "nullable": true,
                          "description": "Detected document type display name: `Identity Card`, `Passport`, `Driver's License`, `Residence Permit`, `Health Insurance Card`, or `Tax Card`. `null` when the document could not be classified.",
                          "example": "Identity Card"
                        },
                        "document_subtype": {
                          "type": "string",
                          "nullable": true,
                          "description": "Finer-grained subtype when available (e.g. `ID_CARD_GENERIC`, `EPASSPORT`).",
                          "example": "ID_CARD_GENERIC"
                        },
                        "document_number": {
                          "type": "string",
                          "nullable": true,
                          "example": "CBX164224"
                        },
                        "personal_number": {
                          "type": "string",
                          "nullable": true,
                          "description": "Secondary personal/national number printed on some documents.",
                          "example": "20446581H"
                        },
                        "portrait_image": {
                          "type": "string",
                          "nullable": true,
                          "description": "Cropped portrait photo from the document. A short-lived media URL (`https://<media-host>/ocr/...`) when `save_api_request=true` (default); an inline base64-encoded JPEG string when `save_api_request=false`. `null` when no portrait was detected."
                        },
                        "front_image": {
                          "type": "string",
                          "nullable": true,
                          "description": "Aligned crop of the document front. Media URL when saved, inline base64 JPEG otherwise."
                        },
                        "back_image": {
                          "type": "string",
                          "nullable": true,
                          "description": "Aligned crop of the document back. `null` when no `back_image` was uploaded."
                        },
                        "front_image_camera_front": {
                          "type": "string",
                          "nullable": true,
                          "description": "Always `null` on this endpoint (populated only in camera-capture verification sessions)."
                        },
                        "back_image_camera_front": {
                          "type": "string",
                          "nullable": true,
                          "description": "Always `null` on this endpoint."
                        },
                        "front_image_camera_front_face_match_score": {
                          "type": "number",
                          "nullable": true,
                          "description": "Always `null` on this endpoint."
                        },
                        "back_image_camera_front_face_match_score": {
                          "type": "number",
                          "nullable": true,
                          "description": "Always `null` on this endpoint."
                        },
                        "front_image_quality_score": {
                          "type": "object",
                          "nullable": true,
                          "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.",
                          "properties": {
                            "focus_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Sharpness of the document crop (higher is sharper)."
                            },
                            "brightness_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Exposure quality (higher is better)."
                            },
                            "brightness_issue": {
                              "type": "string",
                              "nullable": true,
                              "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)."
                            },
                            "is_document_fully_visible": {
                              "type": "boolean",
                              "nullable": true,
                              "description": "Whether all four corners of the document are inside the frame."
                            },
                            "resolution_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Effective resolution of the document area."
                            },
                            "overall_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Aggregate quality score."
                            }
                          }
                        },
                        "back_image_quality_score": {
                          "type": "object",
                          "nullable": true,
                          "description": "Capture-quality diagnostics for the uploaded image (`null` when the side was not provided or scoring failed). All scores are 0–100.",
                          "properties": {
                            "focus_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Sharpness of the document crop (higher is sharper)."
                            },
                            "brightness_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Exposure quality (higher is better)."
                            },
                            "brightness_issue": {
                              "type": "string",
                              "nullable": true,
                              "description": "`ok` when exposure is fine, otherwise the detected issue (e.g. too dark / too bright)."
                            },
                            "is_document_fully_visible": {
                              "type": "boolean",
                              "nullable": true,
                              "description": "Whether all four corners of the document are inside the frame."
                            },
                            "resolution_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Effective resolution of the document area."
                            },
                            "overall_score": {
                              "type": "number",
                              "format": "float",
                              "description": "Aggregate quality score."
                            }
                          }
                        },
                        "date_of_birth": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "example": "1980-01-13"
                        },
                        "age": {
                          "type": "integer",
                          "nullable": true,
                          "description": "Holder age in years, computed from `date_of_birth`.",
                          "example": 46
                        },
                        "expiration_date": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "example": "2032-03-31"
                        },
                        "date_of_issue": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "example": "2022-03-31"
                        },
                        "issuing_state": {
                          "type": "string",
                          "nullable": true,
                          "description": "Issuing country (ISO 3166-1 alpha-3).",
                          "example": "ESP"
                        },
                        "issuing_state_name": {
                          "type": "string",
                          "nullable": true,
                          "example": "Spain"
                        },
                        "first_name": {
                          "type": "string",
                          "nullable": true,
                          "example": "Julio Francisco"
                        },
                        "last_name": {
                          "type": "string",
                          "nullable": true,
                          "example": "Fores Sena"
                        },
                        "full_name": {
                          "type": "string",
                          "nullable": true,
                          "example": "Julio Francisco Fores Sena"
                        },
                        "gender": {
                          "type": "string",
                          "nullable": true,
                          "description": "`M`, `F`, or `null` when not printed/detected.",
                          "example": "M"
                        },
                        "address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Address exactly as printed on the document."
                        },
                        "formatted_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Geocoded, normalized version of `address`."
                        },
                        "place_of_birth": {
                          "type": "string",
                          "nullable": true,
                          "example": "Valencia, Valencia"
                        },
                        "marital_status": {
                          "type": "string",
                          "nullable": true,
                          "description": "`UNKNOWN` unless the document prints marital status.",
                          "example": "UNKNOWN"
                        },
                        "nationality": {
                          "type": "string",
                          "nullable": true,
                          "description": "Holder nationality (ISO 3166-1 alpha-3).",
                          "example": "ESP"
                        },
                        "extra_fields": {
                          "type": "object",
                          "nullable": true,
                          "additionalProperties": true,
                          "description": "Document-specific extras that have no dedicated column (e.g. `first_surname`/`second_surname` on Spanish IDs, license classes on driving licenses)."
                        },
                        "mrz": {
                          "type": "object",
                          "description": "Parsed machine-readable zone. An **empty object** (`{}`) when the document has no MRZ or it could not be read — also check the `warnings` list in that case. Dates inside the MRZ use the raw `YYMMDD` format.",
                          "properties": {
                            "surname": {
                              "type": "string",
                              "example": "FORES SENA"
                            },
                            "name": {
                              "type": "string",
                              "example": "JULIO FRANCISCO"
                            },
                            "country": {
                              "type": "string",
                              "description": "Issuing country (ISO 3166-1 alpha-3).",
                              "example": "ESP"
                            },
                            "nationality": {
                              "type": "string",
                              "description": "Holder nationality (ISO 3166-1 alpha-3).",
                              "example": "ESP"
                            },
                            "birth_date": {
                              "type": "string",
                              "description": "Raw MRZ date of birth (`YYMMDD`).",
                              "example": "800113"
                            },
                            "expiry_date": {
                              "type": "string",
                              "description": "Raw MRZ expiry date (`YYMMDD`).",
                              "example": "320331"
                            },
                            "sex": {
                              "type": "string",
                              "example": "M"
                            },
                            "document_type": {
                              "type": "string",
                              "description": "MRZ document-type code (e.g. `ID`, `P`).",
                              "example": "ID"
                            },
                            "document_number": {
                              "type": "string",
                              "example": "CBX164224"
                            },
                            "optional_data": {
                              "type": "string",
                              "nullable": true,
                              "example": "20446581H"
                            },
                            "optional_data_2": {
                              "type": "string",
                              "nullable": true,
                              "example": ""
                            },
                            "birth_date_hash": {
                              "type": "string",
                              "description": "MRZ check digit for the birth date.",
                              "example": "9"
                            },
                            "expiry_date_hash": {
                              "type": "string",
                              "description": "MRZ check digit for the expiry date.",
                              "example": "8"
                            },
                            "document_number_hash": {
                              "type": "string",
                              "description": "MRZ check digit for the document number.",
                              "example": "3"
                            },
                            "final_hash": {
                              "type": "string",
                              "description": "Composite MRZ check digit.",
                              "example": "3"
                            },
                            "personal_number": {
                              "type": "string",
                              "nullable": true,
                              "example": "20446581H"
                            },
                            "warnings": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "Non-fatal MRZ parsing warnings."
                            },
                            "errors": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "MRZ check-digit/parsing errors. Non-empty errors surface as an `MRZ_VALIDATION_FAILED` warning."
                            },
                            "mrz_type": {
                              "type": "string",
                              "description": "MRZ layout: `TD1` (ID cards, 3 lines), `TD2`, or `TD3` (passports, 2 lines).",
                              "example": "TD1"
                            },
                            "mrz_string": {
                              "type": "string",
                              "description": "Raw MRZ text as read, lines separated by `\\n`."
                            },
                            "mrz_key": {
                              "type": "string",
                              "description": "Composite key (document number + check digits) used for cross-checks."
                            }
                          }
                        },
                        "parsed_address": {
                          "type": "object",
                          "nullable": true,
                          "description": "Structured, geocoded breakdown of `address`. `null` when the document carries no address or it could not be parsed.",
                          "properties": {
                            "street_1": {
                              "type": "string",
                              "nullable": true,
                              "example": "Avenida el Cardonal 52"
                            },
                            "street_2": {
                              "type": "string",
                              "nullable": true,
                              "example": "b"
                            },
                            "city": {
                              "type": "string",
                              "nullable": true,
                              "example": "La Laguna"
                            },
                            "region": {
                              "type": "string",
                              "nullable": true,
                              "example": "Canarias"
                            },
                            "country": {
                              "type": "string",
                              "nullable": true,
                              "description": "ISO 3166-1 alpha-2 country code.",
                              "example": "ES"
                            },
                            "postal_code": {
                              "type": "string",
                              "nullable": true,
                              "example": "38108"
                            },
                            "address_type": {
                              "type": "string",
                              "nullable": true,
                              "example": "Avenida"
                            },
                            "formatted_address": {
                              "type": "string",
                              "nullable": true,
                              "description": "Canonical geocoded address string.",
                              "example": "Av. el Cardonal, 52, b, 38108 La Laguna, Santa Cruz de Tenerife, Spain"
                            },
                            "raw_results": {
                              "type": "object",
                              "additionalProperties": true,
                              "description": "Raw geocoding payload (`address_components`, `geometry.location`, `place_id`, …) for advanced consumers."
                            },
                            "document_location": {
                              "type": "object",
                              "nullable": true,
                              "properties": {
                                "latitude": {
                                  "type": "number"
                                },
                                "longitude": {
                                  "type": "number"
                                }
                              },
                              "description": "Geocoded coordinates of the document address."
                            },
                            "label": {
                              "type": "string",
                              "nullable": true,
                              "description": "Which document field the address was read from.",
                              "example": "Spain Identity Card Address"
                            },
                            "is_verified": {
                              "type": "boolean",
                              "description": "Whether geocoding confirmed the address exists."
                            },
                            "category": {
                              "type": "string",
                              "nullable": true,
                              "description": "Address category (e.g. `Residential`, `Commercial`).",
                              "example": "Residential"
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "description": "Machine-readable risk code. Always-decline risks: `DOCUMENT_EXPIRED`, `MINIMUM_AGE_NOT_MET`, `PORTRAIT_IMAGE_NOT_DETECTED`, `SCREEN_CAPTURE_DETECTED`, `PRINTED_COPY_DETECTED`, `PORTRAIT_MANIPULATION_DETECTED`, `INVALID_DATE`, `COULD_NOT_RECOGNIZE_DOCUMENT` (400-only, never a warning), `DOCUMENT_NUMBER_NOT_DETECTED`, `COULD_NOT_DETECT_DOCUMENT_TYPE`, `NAME_NOT_DETECTED`, `DATE_OF_BIRTH_NOT_DETECTED`. Governed by request options: `MRZ_NOT_DETECTED` / `MRZ_VALIDATION_FAILED` (`invalid_mrz_action`), `DATA_INCONSISTENT` / `MRZ_AND_DATA_EXTRACTED_FROM_OCR_NOT_SAME` / `DOCUMENT_NAME_DIFFERENT_FROM_OTHER_APPROVED_DOCUMENTS` (`inconsistent_data_action`), `EXPIRATION_DATE_NOT_DETECTED` (`expiration_date_not_detected_action`). Other risks (e.g. `POSSIBLE_DUPLICATED_USER`) are informational.",
                                "example": "DOCUMENT_EXPIRED"
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "ID_VERIFICATION"
                                ],
                                "description": "Feature that raised the warning. Always `ID_VERIFICATION` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "Extra context for some risks (e.g. `POSSIBLE_DUPLICATED_USER` includes `duplicated_session_id`); `null` for most."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "warning",
                                  "information"
                                ],
                                "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)"
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        },
                        "barcodes": {
                          "type": "array",
                          "description": "Barcodes decoded from the document (PDF417, QR, …). Empty when the document has none or none could be read.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "description": "Barcode type (e.g. `PDF417`, `QR_CODE`, `UNKNOWN`)."
                              },
                              "position": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "Location of the barcode in the image, when available."
                              },
                              "data": {
                                "type": "string",
                                "description": "Decoded payload."
                              },
                              "data_raw": {
                                "type": "string",
                                "description": "Raw (undecoded) payload, when different."
                              },
                              "side": {
                                "type": "string",
                                "nullable": true,
                                "description": "`front` or `back`."
                              }
                            }
                          }
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document image in which no document can be recognized at all returns `{\"error\": \"COULD_NOT_RECOGNIZE_DOCUMENT\"}`. PDF uploads can additionally fail with \"The submitted PDF file is corrupted or invalid.\" or a no-pages error.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing front image": {
                    "summary": "`front_image` not included in the form data",
                    "value": {
                      "front_image": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/webp/pdf",
                    "value": {
                      "front_image": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp, pdf."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 10 MB limit",
                    "value": {
                      "front_image": [
                        "File size should not exceed 10 MB"
                      ]
                    }
                  },
                  "Invalid option value": {
                    "summary": "Options only accept DECLINE or NO_ACTION",
                    "value": {
                      "invalid_mrz_action": [
                        "\"REVIEW\" is not a valid choice."
                      ]
                    }
                  },
                  "Encrypted PDF": {
                    "summary": "PDF uploaded without the required password",
                    "value": {
                      "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead."
                    }
                  },
                  "Wrong PDF password": {
                    "summary": "`front_image_password` / `back_image_password` does not decrypt the PDF",
                    "value": {
                      "detail": "The PDF password is incorrect. Please provide the correct password."
                    }
                  },
                  "Document not recognized": {
                    "summary": "No identity document could be detected in the image",
                    "value": {
                      "error": "COULD_NOT_RECOGNIZE_DOCUMENT"
                    }
                  },
                  "PDF conversion failure": {
                    "summary": "Uploaded PDF could not be converted to an image",
                    "value": {
                      "error": "Failed to convert front image PDF."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/id-verification/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'front_image=@./id_front.jpg' \\\n  -F 'back_image=@./id_back.jpg' \\\n  -F 'perform_document_liveness=true' \\\n  -F 'save_api_request=true' \\\n  -F 'vendor_data=user-123'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/id-verification/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('id_front.jpg', 'rb') as front_f, open('id_back.jpg', 'rb') as back_f:\n    files = {\n        'front_image': ('id_front.jpg', front_f, 'image/jpeg'),\n        'back_image': ('id_back.jpg', back_f, 'image/jpeg'),\n    }\n    data = {\n        'perform_document_liveness': 'true',\n        'save_api_request': 'true',\n        'vendor_data': 'user-123',\n    }\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=120)\n\nresp.raise_for_status()\nid_v = resp.json()['id_verification']\nprint('status:', id_v['status'])\nprint('name:', id_v.get('full_name'))\nprint('document:', id_v.get('document_type'), id_v.get('document_number'))\nfor w in id_v.get('warnings', []):\n    print('warning:', w['risk'], '-', w['log_type'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('front_image', new Blob([fs.readFileSync('./id_front.jpg')]), 'id_front.jpg');\nform.append('back_image', new Blob([fs.readFileSync('./id_back.jpg')]), 'id_back.jpg');\nform.append('perform_document_liveness', 'true');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/id-verification/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n});\n\nif (!response.ok) throw new Error(`ID verification failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.id_verification.status);\nconsole.log('warnings:', body.id_verification.warnings.map((w) => w.risk));"
          }
        ]
      }
    },
    "/v3/face-match/": {
      "post": {
        "summary": "Face Match",
        "description": "Compare two face images (1:1) and get back a similarity `score` (0–100) plus an `Approved`/`Declined` `status`.\n\n**How it works.** Both images are decoded and analyzed by a biometric model (set `rotate_image=true` if captures may be sideways or upside down), then a similarity score between the two faces is computed. A score **strictly above** `face_match_score_decline_threshold` (default `30`) returns `status: \"Approved\"`; a score at or below it returns `Declined` with a `LOW_FACE_MATCH_SIMILARITY` warning. If no face pair can be scored at all, the call still returns `200` with `status: \"Declined\"` and a `NO_REFERENCE_IMAGE` warning — `score` is `null` on the default save path, or `0` with `save_api_request=false`. Submit one clear, front-facing face per image for best results.\n\n**Billing.** Each `200` response consumes one Face Match API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, both images are stored as the session's face-match source/target images, and a `status.updated` webhook is emitted to your configured webhook endpoints. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Face search index.** This endpoint never enrolls faces into your face search index and never runs blocklist or duplicate screening — use `POST /v3/passive-liveness/` or `POST /v3/face-search/` for those.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3face-match",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "user_image",
                  "ref_image"
                ],
                "properties": {
                  "user_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Live or candidate face image to verify (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy."
                  },
                  "ref_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Reference face image to compare against (e.g. an ID document portrait or a previously enrolled photo). Same format and size limits as `user_image`."
                  },
                  "face_match_score_decline_threshold": {
                    "type": "number",
                    "format": "float",
                    "default": 30,
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Similarity threshold (0–100) that drives the response `status`. A computed score **at or below** this value sets `status` to `Declined` and adds a `LOW_FACE_MATCH_SIMILARITY` warning; a score strictly above it returns `Approved`. Default `30` is permissive — raise it (e.g. `50`–`60`) for stricter verification. Values outside 0–100 return `400`.",
                    "example": 50
                  },
                  "rotate_image": {
                    "type": "boolean",
                    "default": false,
                    "description": "When `true`, the service tries the input images in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.",
                    "example": false
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, and announced through a `status.updated` webhook. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"login\"}`) — it is parsed into an object.",
                    "example": {
                      "flow": "login",
                      "device_id": "abc123"
                    }
                  }
                }
              },
              "example": {
                "user_image": "(binary JPEG/PNG of the live selfie)",
                "ref_image": "(binary JPEG/PNG of the reference photo)",
                "face_match_score_decline_threshold": 50,
                "rotate_image": false,
                "save_api_request": true,
                "vendor_data": "user-123",
                "metadata": {
                  "flow": "login"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Face match computed. `face_match.score` is a 0–100 float (two decimals); `status` is `Approved` when the score is strictly above `face_match_score_decline_threshold`, otherwise `Declined` with an explanatory warning. A low similarity or unscorable pair still returns `200` — inspect `face_match.status` and `face_match.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.",
            "content": {
              "application/json": {
                "examples": {
                  "Approved": {
                    "summary": "Same person — score above the threshold",
                    "value": {
                      "request_id": "f9f5a777-8002-44c6-baaf-128bcfd6e226",
                      "face_match": {
                        "status": "Approved",
                        "score": 99.41,
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                661,
                                728,
                                1688,
                                2188
                              ],
                              "confidence": 0.732973,
                              "age": 26.91,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "ref_image": {
                          "entities": [
                            {
                              "bbox": [
                                653,
                                745,
                                1691,
                                2185
                              ],
                              "confidence": 0.722082,
                              "age": 27,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": []
                      },
                      "vendor_data": "user-123",
                      "metadata": {
                        "flow": "login"
                      },
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  },
                  "Declined - low similarity": {
                    "summary": "Score at or below the decline threshold",
                    "value": {
                      "request_id": "7cdda24e-311a-46d3-a3bb-e5d9b564ce73",
                      "face_match": {
                        "status": "Declined",
                        "score": 24.87,
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                661,
                                728,
                                1688,
                                2188
                              ],
                              "confidence": 0.732973,
                              "age": 26.91,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "ref_image": {
                          "entities": [
                            {
                              "bbox": [
                                156,
                                234,
                                679,
                                898
                              ],
                              "confidence": 0.717775,
                              "age": 41.2,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [
                          {
                            "risk": "LOW_FACE_MATCH_SIMILARITY",
                            "feature": "FACEMATCH",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Low face match similarity",
                            "long_description": "The facial features of the provided image don't closely match the reference image, suggesting a potential identity mismatch."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:08:19.859116+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "face_match": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Approved` when the similarity score is strictly above `face_match_score_decline_threshold` and a score could be computed; `Declined` otherwise (a warning explains why)."
                        },
                        "score": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Similarity score from 0 to 100 — the model's confidence that both images depict the same person. Rounded to two decimals only when `save_api_request` is true; with `save_api_request=false` the raw float is returned. `null` when no face pair could be scored (with the default `save_api_request=true`); with `save_api_request=false` the same condition can surface as `score: 0`. Either way a `NO_REFERENCE_IMAGE` warning is present and `status` is `Declined`.",
                          "example": 99.41
                        },
                        "user_image": {
                          "type": "object",
                          "description": "Face-detection results for the analyzed images.",
                          "properties": {
                            "entities": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "bbox": {
                                    "type": "array",
                                    "items": {
                                      "type": "integer"
                                    },
                                    "minItems": 4,
                                    "maxItems": 4,
                                    "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.",
                                    "example": [
                                      661,
                                      728,
                                      1688,
                                      2188
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "format": "float",
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Face-detection confidence (0–1).",
                                    "example": 0.732973
                                  },
                                  "age": {
                                    "type": "number",
                                    "format": "float",
                                    "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.",
                                    "example": 26.91
                                  },
                                  "gender": {
                                    "type": "string",
                                    "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.",
                                    "example": "male"
                                  },
                                  "race": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Reserved field — always `null` in responses from this endpoint.",
                                    "example": null
                                  }
                                }
                              },
                              "description": "One entry per detected face."
                            },
                            "best_angle": {
                              "type": "integer",
                              "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.",
                              "example": 0
                            }
                          }
                        },
                        "ref_image": {
                          "type": "object",
                          "description": "Face-detection results for the reference image, same shape as `user_image`.",
                          "properties": {
                            "entities": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "bbox": {
                                    "type": "array",
                                    "items": {
                                      "type": "integer"
                                    },
                                    "minItems": 4,
                                    "maxItems": 4,
                                    "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.",
                                    "example": [
                                      661,
                                      728,
                                      1688,
                                      2188
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "format": "float",
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Face-detection confidence (0–1).",
                                    "example": 0.732973
                                  },
                                  "age": {
                                    "type": "number",
                                    "format": "float",
                                    "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the face-match decision.",
                                    "example": 26.91
                                  },
                                  "gender": {
                                    "type": "string",
                                    "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.",
                                    "example": "male"
                                  },
                                  "race": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Reserved field — always `null` in responses from this endpoint.",
                                    "example": null
                                  }
                                }
                              },
                              "description": "One entry per detected face."
                            },
                            "best_angle": {
                              "type": "integer",
                              "example": 0
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Empty on a clean approval. `NO_REFERENCE_IMAGE` (`error`) — no face pair could be scored; `LOW_FACE_MATCH_SIMILARITY` (`error`) — score at or below the decline threshold. Any of these sets `status` to `Declined`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "enum": [
                                  "NO_REFERENCE_IMAGE",
                                  "LOW_FACE_MATCH_SIMILARITY"
                                ],
                                "description": "Machine-readable risk code."
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "FACEMATCH"
                                ],
                                "description": "Feature that raised the warning. Always `FACEMATCH` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "Always `null` for face-match warnings."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "warning",
                                  "information"
                                ],
                                "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Returned when a required image is missing, exceeds 5 MB, has an unsupported extension, or when an option is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing required image": {
                    "summary": "`ref_image` (or `user_image`) not included in the form data",
                    "value": {
                      "ref_image": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/webp",
                    "value": {
                      "user_image": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 5 MB limit",
                    "value": {
                      "user_image": [
                        "File size should not exceed 5 MB"
                      ]
                    }
                  },
                  "Threshold out of range": {
                    "summary": "`face_match_score_decline_threshold` outside 0–100",
                    "value": {
                      "face_match_score_decline_threshold": [
                        "Ensure this value is less than or equal to 100."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/face-match/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'user_image=@./selfie.jpg' \\\n  -F 'ref_image=@./id_portrait.jpg' \\\n  -F 'face_match_score_decline_threshold=50' \\\n  -F 'save_api_request=true' \\\n  -F 'vendor_data=user-123'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-match/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as user_f, open('id_portrait.jpg', 'rb') as ref_f:\n    files = {\n        'user_image': ('selfie.jpg', user_f, 'image/jpeg'),\n        'ref_image': ('id_portrait.jpg', ref_f, 'image/jpeg'),\n    }\n    data = {\n        'face_match_score_decline_threshold': 50,\n        'save_api_request': 'true',\n        'vendor_data': 'user-123',\n    }\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_match']['status'])\nprint('score:', body['face_match']['score'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('ref_image', new Blob([fs.readFileSync('./id_portrait.jpg')]), 'id_portrait.jpg');\nform.append('face_match_score_decline_threshold', '50');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-match/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n});\n\nif (!response.ok) throw new Error(`Face match failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_match.status, 'score:', body.face_match.score);"
          }
        ]
      }
    },
    "/v3/age-estimation/": {
      "post": {
        "summary": "Age Estimation (face age + passive liveness)",
        "description": "Estimate a person's age from a single face photo and run a passive liveness check in the same call — returns the model-predicted `age_estimation` (in years), a liveness `score` (0–100), and an `Approved`/`Declined` `status`. The age is **model-predicted from the face, not verified against a document** — use it for age gates and low-friction checks, not as legal proof of age.\n\n**How it works.** The image is analyzed for faces (set `rotate_image=true` if captures may be sideways or upside down); each detected face gets an estimated age, and the largest face in the frame drives `age_estimation`. In parallel, a passive liveness model scores whether the photo shows a live person (no user challenge required) and detects presentation attacks (photos of photos, screens, masks). Submit one clear, front-facing, well-lit face per image for best results.\n\n**Decision logic.** Any warning declines: `status` is `Approved` only when the `warnings` array is empty. A liveness `score` at or below `face_liveness_score_decline_threshold` (default `30`) adds `LOW_LIVENESS_SCORE`; a detected attack adds `LIVENESS_FACE_ATTACK`; no face found by the liveness model adds `NO_FACE_DETECTED`; no estimable face age adds `AGE_NOT_DETECTED` (`age_estimation` is `null`); an estimated age below `age_estimation_decline_threshold` (default `18`) adds `AGE_BELOW_MINIMUM`. Borderline ages near your threshold deserve a fallback to document-based verification — the model's error grows for faces close to the limit.\n\n**Billing.** Each `200` response consumes one Age Estimation API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any image processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the result is exposed there in `liveness_checks[]`, with `features: [\"AGE_ESTIMATION\"]`), the face crop and reference image are stored, the face embedding is added to your application's face index (used by face search and duplicate detection), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (`age_estimation: 30`, no warnings), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3age-estimation",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "user_image"
                ],
                "properties": {
                  "user_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Face photo to analyze (e.g. a selfie). Submit a single front-facing photo with the subject's face clearly visible and well lit. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp` (no PDF). Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.3 MB before processing, so very high resolutions do not improve accuracy."
                  },
                  "face_liveness_score_decline_threshold": {
                    "type": "number",
                    "format": "float",
                    "default": 30,
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Liveness threshold (0–100). A liveness `score` **at or below** this value adds a `LOW_LIVENESS_SCORE` warning and declines. Raise it for stricter anti-spoofing; values outside 0–100 return `400`.",
                    "example": 30
                  },
                  "age_estimation_decline_threshold": {
                    "type": "integer",
                    "default": 18,
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Minimum acceptable estimated age in years. An estimated age **below** this value adds an `AGE_BELOW_MINIMUM` warning and declines. Set `0` to disable the age check (the call then only runs liveness + age estimation without an age-based decision).",
                    "example": 18
                  },
                  "rotate_image": {
                    "type": "boolean",
                    "default": false,
                    "description": "When `true`, the service tries the input image in 90-degree increments (0, 90, 180, 270) and uses the orientation that yields the best face detection — useful for mobile captures with missing EXIF orientation. Adds latency, so only enable it if you cannot guarantee upright images.",
                    "example": false
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the detected face added to your application's face index. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"age-gate\"}`) — it is parsed into an object.",
                    "example": {
                      "flow": "age-gate"
                    }
                  }
                }
              },
              "example": {
                "user_image": "(binary JPEG/PNG of the selfie)",
                "face_liveness_score_decline_threshold": 30,
                "age_estimation_decline_threshold": 18,
                "rotate_image": false,
                "save_api_request": true,
                "vendor_data": "user-123",
                "metadata": {
                  "flow": "age-gate"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Age estimation and passive liveness completed. `age_estimation.age_estimation` is the model-predicted age in years of the largest detected face (`null` if none); `age_estimation.score` is the passive-liveness score (0–100); `status` is `Approved` only when `warnings` is empty. An underage or spoofed capture still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.",
            "content": {
              "application/json": {
                "examples": {
                  "Approved": {
                    "summary": "Live adult face — both checks passed",
                    "value": {
                      "request_id": "0c40ba43-64ab-4e2e-b4b8-7d1f12f81bc1",
                      "age_estimation": {
                        "status": "Approved",
                        "method": "PASSIVE",
                        "score": 97.5,
                        "user_image": {
                          "entities": [
                            {
                              "age": 27.33,
                              "bbox": [
                                40,
                                40,
                                100,
                                100
                              ],
                              "confidence": 0.7177750468254089,
                              "gender": "male"
                            }
                          ],
                          "best_angle": 0
                        },
                        "age_estimation": 27.33,
                        "warnings": []
                      },
                      "vendor_data": "user-123",
                      "metadata": {
                        "flow": "age-gate"
                      },
                      "created_at": "2026-06-12T02:24:11.512941+00:00"
                    }
                  },
                  "Declined - age below threshold": {
                    "summary": "Estimated age below `age_estimation_decline_threshold`",
                    "value": {
                      "request_id": "9be41a6f-2f4e-4f57-92f4-b3a4f6f0a1c2",
                      "age_estimation": {
                        "status": "Declined",
                        "method": "PASSIVE",
                        "score": 95,
                        "user_image": {
                          "entities": [
                            {
                              "age": 16.5,
                              "bbox": [
                                40,
                                40,
                                100,
                                100
                              ],
                              "confidence": 0.7177750468254089,
                              "gender": "female"
                            }
                          ],
                          "best_angle": 0
                        },
                        "age_estimation": 16.5,
                        "warnings": [
                          {
                            "risk": "AGE_BELOW_MINIMUM",
                            "feature": "LIVENESS",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Age below minimum",
                            "long_description": "The age of the face is below the minimum age threshold for the application."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:26:40.118332+00:00"
                    }
                  },
                  "Declined - liveness too low": {
                    "summary": "Liveness score at or below the decline threshold (possible spoof)",
                    "value": {
                      "request_id": "5f6f2f1f-7c4f-43b9-8d62-0a8f4c2d9e77",
                      "age_estimation": {
                        "status": "Declined",
                        "method": "PASSIVE",
                        "score": 5,
                        "user_image": {
                          "entities": [
                            {
                              "age": 25,
                              "bbox": [
                                40,
                                40,
                                100,
                                100
                              ],
                              "confidence": 0.7177750468254089,
                              "gender": "male"
                            }
                          ],
                          "best_angle": 0
                        },
                        "age_estimation": 25,
                        "warnings": [
                          {
                            "risk": "LOW_LIVENESS_SCORE",
                            "feature": "LIVENESS",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Low liveness score",
                            "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:27:02.901547+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "age_estimation": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Approved` when no warnings were raised; `Declined` otherwise (every warning this endpoint produces declines)."
                        },
                        "method": {
                          "type": "string",
                          "enum": [
                            "PASSIVE"
                          ],
                          "description": "Liveness method. Always `PASSIVE` on this endpoint (single-image, no user challenge)."
                        },
                        "score": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Passive-liveness score from 0 to 100 — the model's confidence that the photo shows a live person. Compared against `face_liveness_score_decline_threshold`; at or below it the call declines with `LOW_LIVENESS_SCORE`. `null` when the liveness model returns no confidence — the call still declines with `LOW_LIVENESS_SCORE` because null is treated as 0 for the threshold comparison.",
                          "example": 97.5
                        },
                        "user_image": {
                          "type": "object",
                          "description": "Face-detection results for the analyzed image.",
                          "properties": {
                            "entities": {
                              "type": "array",
                              "description": "One entry per detected face. Empty when no face was found (then `age_estimation` is `null` and an `AGE_NOT_DETECTED` warning is added).",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "age": {
                                    "type": "number",
                                    "format": "float",
                                    "description": "Model-estimated age of this face, in years.",
                                    "example": 27.33
                                  },
                                  "bbox": {
                                    "type": "array",
                                    "items": {
                                      "type": "integer"
                                    },
                                    "minItems": 4,
                                    "maxItems": 4,
                                    "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.",
                                    "example": [
                                      40,
                                      40,
                                      100,
                                      100
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "format": "float",
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Face-detection confidence (0–1).",
                                    "example": 0.7177750468254089
                                  },
                                  "gender": {
                                    "type": "string",
                                    "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.",
                                    "example": "male"
                                  }
                                }
                              }
                            },
                            "best_angle": {
                              "type": "integer",
                              "nullable": true,
                              "description": "Rotation (degrees: 0, 90, 180, or 270) that produced the best face detection. Only non-zero when `rotate_image=true` corrected the orientation.",
                              "example": 0
                            }
                          }
                        },
                        "age_estimation": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "description": "Model-predicted age in years of the **largest** detected face. `null` when no face age could be estimated (an `AGE_NOT_DETECTED` warning is added and the call declines).",
                          "example": 27.33
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Empty on a clean approval. Any entry sets `status` to `Declined`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "enum": [
                                  "NO_FACE_DETECTED",
                                  "LOW_LIVENESS_SCORE",
                                  "LIVENESS_FACE_ATTACK",
                                  "AGE_NOT_DETECTED",
                                  "AGE_BELOW_MINIMUM"
                                ],
                                "description": "Machine-readable risk code. `NO_FACE_DETECTED` — the liveness model found no face; `LOW_LIVENESS_SCORE` — liveness `score` at or below `face_liveness_score_decline_threshold`; `LIVENESS_FACE_ATTACK` — a presentation attack (photo/screen/mask) was detected; `AGE_NOT_DETECTED` — no face age could be estimated; `AGE_BELOW_MINIMUM` — estimated age below `age_estimation_decline_threshold`. `NO_FACE_DETECTED` and `LOW_LIVENESS_SCORE` are mutually exclusive — when no face is found only `NO_FACE_DETECTED` is emitted."
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "LIVENESS"
                                ],
                                "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "Always `null` for age-estimation warnings."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error"
                                ],
                                "description": "Severity. Every warning this endpoint produces is an `error` and sets `status` to `Declined`."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when a threshold is out of range. The body is DRF's standard field-error envelope: one array of messages per offending field.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing user image": {
                    "summary": "`user_image` not included in the form data",
                    "value": {
                      "user_image": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/webp",
                    "value": {
                      "user_image": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 5 MB limit",
                    "value": {
                      "user_image": [
                        "File size should not exceed 5 MB"
                      ]
                    }
                  },
                  "Threshold out of range": {
                    "summary": "`face_liveness_score_decline_threshold` outside 0–100",
                    "value": {
                      "face_liveness_score_decline_threshold": [
                        "Ensure this value is less than or equal to 100."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/age-estimation/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'user_image=@./selfie.jpg' \\\n  -F 'age_estimation_decline_threshold=18' \\\n  -F 'face_liveness_score_decline_threshold=30' \\\n  -F 'save_api_request=true' \\\n  -F 'vendor_data=user-123'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/age-estimation/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n    files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n    data = {\n        'age_estimation_decline_threshold': 18,\n        'face_liveness_score_decline_threshold': 30,\n        'save_api_request': 'true',\n        'vendor_data': 'user-123',\n    }\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nresult = resp.json()['age_estimation']\nprint('status:', result['status'])\nprint('estimated age:', result['age_estimation'])\nprint('liveness score:', result['score'])\nfor w in result['warnings']:\n    print('warning:', w['risk'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('age_estimation_decline_threshold', '18');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/age-estimation/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n});\n\nif (!response.ok) throw new Error(`Age estimation failed: ${response.status}`);\nconst { age_estimation } = await response.json();\nconsole.log('status:', age_estimation.status, 'age:', age_estimation.age_estimation, 'liveness:', age_estimation.score);"
          }
        ]
      }
    },
    "/v3/poa/": {
      "post": {
        "summary": "Proof of Address (document OCR + address checks)",
        "description": "Extract and validate a Proof of Address document — utility bills, bank statements, and government-issued documents — in one call. Returns the issuer, issue/expiry dates, the holder name, the raw and parsed/geocoded address, an `Approved`/`Declined` `status`, and a `warnings` list explaining every issue found.\n\n**Latency.** Extraction is LLM-based: typical calls take **5–15 seconds**, multi-page PDFs up to **~30 seconds**. Configure a client timeout of **at least 45 seconds** and do not retry before the call completes.\n\n**How it works.** The document (image or PDF, up to 15 MB; encrypted PDFs require `document_password`) is classified into a document type and subtype (e.g. `UTILITY_BILL` / `WATER_BILL`), and the issuer, dates, holder name, and address are extracted with LLM-based extraction. The address is parsed and geocoded into `poa_parsed_address`. PDF/EXIF forensics screen for tampering — modification after digital signing, known PDF editors, overlay-text manipulation, suspicious re-exports — surfacing `SUSPECTED_DOCUMENT_MANIPULATION` or `DOCUMENT_METADATA_MISMATCH`. Document age is validated against `poa_document_age_months`, and the language against `poa_languages_allowed`. If you pass `expected_address`, `expected_country`, or `expected_first_name`/`expected_last_name`, the extracted values are cross-checked and mismatches are flagged.\n\n**Decision logic.** `status` is `Approved` unless at least one warning resolves to a decline. Hard failures always decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (not configurable on this endpoint). Five risk groups are configurable per request via the `poa_*_action` options (`DECLINE` or `NO_ACTION`, all defaulting to `DECLINE`). `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION` and `UNPARSABLE_OR_INVALID_ADDRESS` are informational on this endpoint and never decline. A document that cannot be processed at all returns `400`; a readable but problematic document returns `200` with `status: \"Declined\"` — always inspect `poa.status` and `poa.warnings`, not just the HTTP code.\n\n**Billing.** Each `200` response consumes one Proof of Address API credit (standalone APIs have no free tier). When the organization's balance cannot cover the call, the endpoint returns `403` with the not-enough-credits error before any document processing.\n\n**Session persistence (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session: it appears in the Business Console, the uploaded document is stored, the returned `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/` (the decision additionally exposes `document_file` and the `document_metadata` forensics, which this response omits), and a `status.updated` webhook is emitted. When `false`, nothing is stored and `request_id` is a one-off correlation UUID that cannot be looked up later.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload (a fictional \"Sandbox Power Co.\" electricity bill), no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nNote: `FUTURE_ISSUE_DATE` and `POOR_DOCUMENT_QUALITY` exist in workflow sessions but are never produced by this standalone endpoint — a future-dated document is not auto-declined here.",
        "operationId": "post_v3poa",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "type": "string",
                    "format": "binary",
                    "description": "The Proof of Address document. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `pdf`, `webp`. Maximum upload size: **15 MB** (larger files are rejected with `400`). Multi-page PDFs are supported (expect higher latency); encrypted PDFs require `document_password`. Images are automatically compressed before processing."
                  },
                  "document_password": {
                    "type": "string",
                    "description": "Password to decrypt `document` when it is an encrypted PDF. Sending an encrypted PDF without (or with a wrong) password returns `400`.",
                    "writeOnly": true
                  },
                  "expected_address": {
                    "type": "string",
                    "description": "Address to verify against the document. It is geocoded and compared with the extracted address; a mismatch (or an expected address that cannot be geocoded/verified) adds `ADDRESS_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`. The geocoded form is echoed back in `expected_details_formatted_address` / `expected_details_parsed_address`.",
                    "example": "Av. Monseñor Tavella 1291, Salta, Argentina"
                  },
                  "expected_country": {
                    "type": "string",
                    "description": "Country to verify against the document (ISO 3166-1 code, e.g. `ARG` or `AR`). A mismatch with the document's country adds `POA_COUNTRY_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.",
                    "example": "ARG"
                  },
                  "expected_first_name": {
                    "type": "string",
                    "description": "First name to verify against `name_on_document` (fuzzy match, transliteration-aware). A score below the match threshold adds `NAME_MISMATCH_WITH_PROVIDED`, governed by `poa_address_mismatch_action`.",
                    "example": "Sophia"
                  },
                  "expected_last_name": {
                    "type": "string",
                    "description": "Last name to verify against `name_on_document`, combined with `expected_first_name` for the match score.",
                    "example": "Martinez"
                  },
                  "poa_languages_allowed": {
                    "type": "string",
                    "description": "Comma-separated list of accepted document languages as ISO 639-1 codes (e.g. `en,es,fr`). When omitted or blank, **all 51 supported languages** are accepted. A document in a language outside the list adds `UNSUPPORTED_DOCUMENT_LANGUAGE` (governed by `poa_unsupported_language_action`); an unknown code in the list returns `400` with the full set of supported codes.",
                    "example": "en,es"
                  },
                  "poa_document_age_months": {
                    "type": "string",
                    "description": "Comma-separated `key:value` pairs setting the maximum document age in months per document type — keys: `utility_bill`, `bank_statement`, `government_issued_document`, `other_poa_document`; values: `1`–`120`, or `-1` for unlimited (no age check). Defaults when omitted: `utility_bill:3,bank_statement:3,government_issued_document:12,other_poa_document:12`. A document older than its limit declines with `POA_DOCUMENT_EXPIRED`. **Caution:** when you provide this field, list *every* type you accept — omitted types are treated as not accepted (informational `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`) and their age check is skipped.",
                    "example": "utility_bill:3,bank_statement:6"
                  },
                  "poa_name_mismatch_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "Accepted but **currently not applied** — name-mismatch warnings (`NAME_MISMATCH_WITH_PROVIDED`) follow `poa_address_mismatch_action` instead. Set `poa_address_mismatch_action` to control both name and address mismatch behavior."
                  },
                  "poa_document_issues_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What to do on file-integrity problems — in practice only `DOCUMENT_METADATA_MISMATCH` is produced by this endpoint."
                  },
                  "poa_document_authenticity_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when document manipulation is suspected (`SUSPECTED_DOCUMENT_MANIPULATION`) — e.g. the PDF was modified after digital signing, processed by a known PDF editor, shows overlay-text manipulation, or has inconsistent EXIF dates."
                  },
                  "poa_unsupported_language_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when the document language is outside `poa_languages_allowed` (`UNSUPPORTED_DOCUMENT_LANGUAGE`)."
                  },
                  "poa_address_mismatch_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when the extracted details disagree with the `expected_*` fields: `ADDRESS_MISMATCH_WITH_PROVIDED`, `NAME_MISMATCH_WITH_PROVIDED`, and `POA_COUNTRY_MISMATCH_WITH_PROVIDED` all follow this action."
                  },
                  "poa_issuer_not_identified_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What to do when no issuing company/organization can be identified on the document (`ISSUER_NOT_IDENTIFIED`)."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session — visible in the Business Console, retrievable via `GET /v3/session/{sessionId}/decision/` using the returned `request_id`, announced through a `status.updated` webhook, and with the uploaded document stored. When `false`, nothing is stored and `request_id` is a transient UUID for response correlation only.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque string (your internal user id, email, UUID…) stored on the persisted session and echoed back in the response. Use it to correlate API calls with your own records and to filter sessions later.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back in the response. In multipart requests, send it as a JSON-encoded string field (e.g. `metadata={\"flow\":\"onboarding\"}`) — it is parsed into an object.",
                    "example": {
                      "flow": "onboarding"
                    }
                  }
                }
              },
              "example": {
                "document": "(binary JPEG/PNG/PDF of the utility bill or bank statement)",
                "expected_address": "Av. Monseñor Tavella 1291, Salta, Argentina",
                "expected_country": "ARG",
                "expected_first_name": "Sophia",
                "expected_last_name": "Martinez",
                "poa_languages_allowed": "en,es",
                "save_api_request": true,
                "vendor_data": "user-123"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document processed. `poa.status` is `Approved` or `Declined`; every detected issue is itemized in `poa.warnings`. An expired, mismatched, or suspicious document still returns `200` with `status: \"Declined\"` — inspect the body, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.",
            "content": {
              "application/json": {
                "examples": {
                  "Approved": {
                    "summary": "Utility bill read cleanly, no expected-details checks",
                    "value": {
                      "request_id": "4b523a8d-ed32-45e4-8aa8-a89eda459d88",
                      "poa": {
                        "issuing_state": "ARG",
                        "document_type": "UTILITY_BILL",
                        "document_subtype": "WATER_BILL",
                        "document_language": "es",
                        "issuer": "Aguas del Norte",
                        "issue_date": "2026-04-10",
                        "expiration_date": null,
                        "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA",
                        "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina",
                        "poa_parsed_address": {
                          "street_1": "Avenida Monseñor Tavella 1291",
                          "street_2": "b",
                          "city": "Salta",
                          "region": "Salta",
                          "country": "AR",
                          "postal_code": "A4400",
                          "document_location": {
                            "latitude": -24.8208682,
                            "longitude": -65.4130954
                          }
                        },
                        "expected_details_address": null,
                        "expected_details_formatted_address": null,
                        "expected_details_parsed_address": null,
                        "name_on_document": "OESTE EMBOTELLADORA S.A.",
                        "extra_fields": {
                          "bank_account_number": null,
                          "bank_iban": null,
                          "bank_sort_code": null,
                          "bank_routing_number": null,
                          "bank_swift_bic": null,
                          "bank_branch_name": null,
                          "bank_branch_address": null,
                          "document_phone_number": null,
                          "additional_names": [
                            "LA EMBOTELLADORA DEL NORTE SA"
                          ]
                        },
                        "status": "Approved",
                        "warnings": []
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:29:02.439655+00:00"
                    }
                  },
                  "Declined - document too old": {
                    "summary": "Issue date older than the allowed `poa_document_age_months`",
                    "value": {
                      "request_id": "f61681b8-9422-4378-ac3e-04c6a247fb45",
                      "poa": {
                        "issuing_state": "ARG",
                        "document_type": "UTILITY_BILL",
                        "document_subtype": "WATER_BILL",
                        "document_language": "es",
                        "issuer": "Aguas del Norte",
                        "issue_date": "2021-02-10",
                        "expiration_date": null,
                        "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA",
                        "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina",
                        "poa_parsed_address": {
                          "street_1": "Avenida Monseñor Tavella 1291",
                          "street_2": "b",
                          "city": "Salta",
                          "region": "Salta",
                          "country": "AR",
                          "postal_code": "A4400",
                          "document_location": {
                            "latitude": -24.8208682,
                            "longitude": -65.4130954
                          }
                        },
                        "expected_details_address": null,
                        "expected_details_formatted_address": null,
                        "expected_details_parsed_address": null,
                        "name_on_document": "OESTE EMBOTELLADORA S.A.",
                        "extra_fields": {
                          "bank_account_number": null,
                          "bank_iban": null,
                          "bank_sort_code": null,
                          "bank_routing_number": null,
                          "bank_swift_bic": null,
                          "bank_branch_name": null,
                          "bank_branch_address": null,
                          "document_phone_number": null,
                          "additional_names": [
                            "LA EMBOTELLADORA DEL NORTE SA"
                          ]
                        },
                        "status": "Declined",
                        "warnings": [
                          {
                            "risk": "POA_DOCUMENT_EXPIRED",
                            "feature": "PROOF_OF_ADDRESS",
                            "additional_data": {
                              "max_age_months": 3,
                              "document_subtype": "WATER_BILL",
                              "document_type": "UTILITY_BILL",
                              "issue_date": "2021-02-10"
                            },
                            "log_type": "error",
                            "short_description": "Document expired",
                            "long_description": "The submitted document is older than 90 days from its issue date, which exceeds the acceptable time period for validity."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:23:02.165493+00:00"
                    }
                  },
                  "Declined - name mismatch": {
                    "summary": "`expected_first_name`/`expected_last_name` do not match `name_on_document`",
                    "value": {
                      "request_id": "0a7f2f9c-58f1-4b8e-b1e3-2f6f9d4f7a21",
                      "poa": {
                        "issuing_state": "ARG",
                        "document_type": "UTILITY_BILL",
                        "document_subtype": "WATER_BILL",
                        "document_language": "es",
                        "issuer": "Aguas del Norte",
                        "issue_date": "2021-02-10",
                        "expiration_date": null,
                        "poa_address": "AVDA. MONSEÑOR TAVELLA N° 1291, B° VILLA LAVALLE CP: 4400, CAPITAL - SALTA",
                        "poa_formatted_address": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina",
                        "poa_parsed_address": {
                          "street_1": "Avenida Monseñor Tavella 1291",
                          "street_2": "b",
                          "city": "Salta",
                          "region": "Salta",
                          "country": "AR",
                          "postal_code": "A4400",
                          "document_location": {
                            "latitude": -24.8208682,
                            "longitude": -65.4130954
                          }
                        },
                        "expected_details_address": null,
                        "expected_details_formatted_address": null,
                        "expected_details_parsed_address": null,
                        "name_on_document": "OESTE EMBOTELLADORA S.A.",
                        "extra_fields": {
                          "bank_account_number": null,
                          "bank_iban": null,
                          "bank_sort_code": null,
                          "bank_routing_number": null,
                          "bank_swift_bic": null,
                          "bank_branch_name": null,
                          "bank_branch_address": null,
                          "document_phone_number": null,
                          "additional_names": [
                            "LA EMBOTELLADORA DEL NORTE SA"
                          ]
                        },
                        "status": "Declined",
                        "warnings": [
                          {
                            "risk": "NAME_MISMATCH_WITH_PROVIDED",
                            "feature": "PROOF_OF_ADDRESS",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Name mismatch with provided information",
                            "long_description": "The full name on the document does not match the name from the user's verified identity documents, or the full name sent by API."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T02:27:21.330172+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "poa": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Approved` when no warning resolves to a decline; `Declined` otherwise — the `warnings` list explains why."
                        },
                        "issuing_state": {
                          "type": "string",
                          "nullable": true,
                          "description": "Country the document was issued in, as extracted from the document — usually ISO 3166-1 alpha-3 (e.g. `ARG`).",
                          "example": "ARG"
                        },
                        "document_type": {
                          "type": "string",
                          "enum": [
                            "UTILITY_BILL",
                            "BANK_STATEMENT",
                            "GOVERNMENT_ISSUED_DOCUMENT",
                            "OTHER_POA_DOCUMENT",
                            "UNKNOWN"
                          ],
                          "description": "Detected document category. `UNKNOWN` means the document could not be classified as an acceptable Proof of Address and auto-declines with `INVALID_DOCUMENT_TYPE`. Never null — classification failures fall back to `UNKNOWN`."
                        },
                        "document_subtype": {
                          "type": "string",
                          "description": "Finer-grained classification within `document_type` — e.g. `ELECTRICITY_BILL`, `WATER_BILL`, `GAS_BILL`, `INTERNET_BILL`, `PHONE_BILL` (utility bills); `ACCOUNT_STATEMENT`, `CREDIT_CARD_STATEMENT` (bank statements); `TAX_ASSESSMENT`, `RESIDENCY_CERTIFICATE` (government documents). `UNKNOWN` when no subtype could be determined. Never null — classification failures fall back to `UNKNOWN`.",
                          "example": "WATER_BILL"
                        },
                        "document_language": {
                          "type": "string",
                          "nullable": true,
                          "description": "Detected document language (ISO 639-1).",
                          "example": "es"
                        },
                        "issuer": {
                          "type": "string",
                          "nullable": true,
                          "description": "Company or organization that issued the document. `null` adds `ISSUER_NOT_IDENTIFIED`.",
                          "example": "Aguas del Norte"
                        },
                        "issue_date": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "description": "Document issue date (`YYYY-MM-DD`). `null` when it could not be extracted — that declines with `UNABLE_TO_EXTRACT_ISSUE_DATE` on this endpoint.",
                          "example": "2021-02-10"
                        },
                        "expiration_date": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "description": "Explicit expiry/validity date printed on the document, when present. Independent of the `poa_document_age_months` age check."
                        },
                        "poa_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Address exactly as printed on the document. `null` auto-declines with `MISSING_ADDRESS_INFORMATION`."
                        },
                        "poa_formatted_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Geocoded, normalized version of `poa_address`.",
                          "example": "Av. Monseñor Tavella 1291 b, A4400 Salta, Argentina"
                        },
                        "poa_parsed_address": {
                          "type": "object",
                          "nullable": true,
                          "description": "Structured, geocoded breakdown of the document address. `null` when the address could not be parsed.",
                          "properties": {
                            "street_1": {
                              "type": "string",
                              "nullable": true,
                              "example": "Avenida Monseñor Tavella 1291"
                            },
                            "street_2": {
                              "type": "string",
                              "nullable": true
                            },
                            "city": {
                              "type": "string",
                              "nullable": true,
                              "example": "Salta"
                            },
                            "region": {
                              "type": "string",
                              "nullable": true,
                              "example": "Salta"
                            },
                            "country": {
                              "type": "string",
                              "nullable": true,
                              "description": "ISO 3166-1 alpha-2 country code.",
                              "example": "AR"
                            },
                            "postal_code": {
                              "type": "string",
                              "nullable": true,
                              "example": "A4400"
                            },
                            "document_location": {
                              "type": "object",
                              "nullable": true,
                              "properties": {
                                "latitude": {
                                  "type": "number"
                                },
                                "longitude": {
                                  "type": "number"
                                }
                              },
                              "description": "Geocoded coordinates of the document address."
                            }
                          }
                        },
                        "expected_details_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Echo of the `expected_address` you sent, or `null`."
                        },
                        "expected_details_formatted_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Geocoded, normalized version of `expected_address`; `null` when not provided or not geocodable."
                        },
                        "expected_details_parsed_address": {
                          "type": "object",
                          "nullable": true,
                          "additionalProperties": true,
                          "description": "Full geocoding result for `expected_address` (street/city/region/country/postal code plus raw geocoder output and an `is_verified` flag). `null` when `expected_address` was not provided."
                        },
                        "name_on_document": {
                          "type": "string",
                          "nullable": true,
                          "description": "Primary holder name extracted from the document; compared against `expected_first_name`/`expected_last_name` when provided. `null` declines with `POA_NAME_NOT_DETECTED` on this endpoint.",
                          "example": "OESTE EMBOTELLADORA S.A."
                        },
                        "extra_fields": {
                          "type": "object",
                          "description": "Document-specific extras. Bank fields are populated for bank statements when printed on the document; `null` otherwise.",
                          "properties": {
                            "bank_account_number": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_iban": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_sort_code": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_routing_number": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_swift_bic": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_branch_name": {
                              "type": "string",
                              "nullable": true
                            },
                            "bank_branch_address": {
                              "type": "string",
                              "nullable": true
                            },
                            "document_phone_number": {
                              "type": "string",
                              "nullable": true,
                              "description": "Contact phone number printed on the document."
                            },
                            "additional_names": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "description": "Other holder names found on the document (e.g. co-account holders, previous account names)."
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Empty on a clean approval. Every entry explains one detected risk; entries with `log_type: \"error\"` set `status` to `Declined`.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "description": "Machine-readable risk code. Always-decline: `MISSING_ADDRESS_INFORMATION`, `POA_DOCUMENT_EXPIRED`, `INVALID_DOCUMENT_TYPE`, `UNABLE_TO_VALIDATE_DOCUMENT_AGE`, plus `UNABLE_TO_EXTRACT_ISSUE_DATE` and `POA_NAME_NOT_DETECTED` (fixed to decline on this endpoint). Governed by request options: `NAME_MISMATCH_WITH_PROVIDED` / `ADDRESS_MISMATCH_WITH_PROVIDED` / `POA_COUNTRY_MISMATCH_WITH_PROVIDED` (`poa_address_mismatch_action`), `POOR_DOCUMENT_QUALITY` (never produced by this endpoint) / `DOCUMENT_METADATA_MISMATCH` (`poa_document_issues_action`), `SUSPECTED_DOCUMENT_MANIPULATION` (`poa_document_authenticity_action`), `UNSUPPORTED_DOCUMENT_LANGUAGE` (`poa_unsupported_language_action`), `ISSUER_NOT_IDENTIFIED` (`poa_issuer_not_identified_action`). Informational on this endpoint: `POA_DOCUMENT_NOT_SUPPORTED_FOR_APPLICATION`, `UNPARSABLE_OR_INVALID_ADDRESS`.",
                                "example": "POA_DOCUMENT_EXPIRED"
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "PROOF_OF_ADDRESS"
                                ],
                                "description": "Feature that raised the warning. Always `PROOF_OF_ADDRESS` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "Extra context for some risks — e.g. `POA_DOCUMENT_EXPIRED` includes `max_age_months`, `document_type`, `document_subtype`, and `issue_date`; `SUSPECTED_DOCUMENT_MANIPULATION` includes `reason` and `detection_method`. `null` for most risks."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "information"
                                ],
                                "description": "Severity. `error` warnings drive `status` to `Declined`; `information` entries are advisory and never decline on their own. (`warning` is not produced by this endpoint — its options only accept `DECLINE` or `NO_ACTION`.)"
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation or processing error. Field-level problems return DRF's standard envelope (one array of messages per offending field). Encrypted-PDF problems return `{\"detail\": ...}`. A document the extraction pipeline cannot process returns `{\"error\": [\"Error extracting POA information\"]}`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing document": {
                    "summary": "`document` not included in the form data",
                    "value": {
                      "document": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/pdf/webp",
                    "value": {
                      "document": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, pdf, webp."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 15 MB limit",
                    "value": {
                      "document": [
                        "File size should not exceed 15 MB"
                      ]
                    }
                  },
                  "Encrypted PDF": {
                    "summary": "PDF uploaded without the required password",
                    "value": {
                      "detail": "The PDF is encrypted. Please upload a decrypted PDF or a photo instead."
                    }
                  },
                  "Wrong PDF password": {
                    "summary": "`document_password` does not decrypt the PDF",
                    "value": {
                      "detail": "The PDF password is incorrect. Please provide the correct password."
                    }
                  },
                  "Invalid language code": {
                    "summary": "`poa_languages_allowed` contains an unsupported code",
                    "value": {
                      "poa_languages_allowed": [
                        "Invalid language code: 'xx'. Must be one of the supported languages: ['ar', 'bn', 'hy', 'bg', 'bs', 'ca', 'cnr', 'sq', 'zh', 'hr', 'cs', 'da', 'nl', 'en', 'et', 'fi', 'fr', 'ka', 'kk', 'de', 'el', 'he', 'hi', 'hu', 'id', 'it', 'ja', 'ko', 'ky', 'lv', 'lt', 'mk', 'mn', 'ms', 'no', 'fa', 'pl', 'pt', 'ro', 'ru', 'sr', 'sk', 'sl', 'so', 'es', 'sv', 'th', 'tr', 'uk', 'uz', 'vi']."
                      ]
                    }
                  },
                  "Invalid document age value": {
                    "summary": "`poa_document_age_months` value outside 1–120 / -1",
                    "value": {
                      "poa_document_age_months": [
                        "Invalid integer value for 'utility_bill': '999'. Must be -1 (unlimited) or a positive integer between 1 and 120."
                      ]
                    }
                  },
                  "Extraction failure": {
                    "summary": "The document could not be processed by the extraction pipeline",
                    "value": {
                      "error": [
                        "Error extracting POA information"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any document processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/poa/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'document=@./utility-bill.pdf' \\\n  -F 'expected_address=Av. Monseñor Tavella 1291, Salta, Argentina' \\\n  -F 'expected_country=ARG' \\\n  -F 'expected_first_name=Sophia' \\\n  -F 'expected_last_name=Martinez' \\\n  -F 'poa_languages_allowed=en,es' \\\n  -F 'vendor_data=user-123' \\\n  --max-time 45"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/poa/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('utility-bill.pdf', 'rb') as f:\n    files = {'document': ('utility-bill.pdf', f, 'application/pdf')}\n    data = {\n        'expected_address': 'Av. Monseñor Tavella 1291, Salta, Argentina',\n        'expected_country': 'ARG',\n        'expected_first_name': 'Sophia',\n        'expected_last_name': 'Martinez',\n        'poa_languages_allowed': 'en,es',\n        'vendor_data': 'user-123',\n    }\n    # LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow headroom\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=45)\n\nresp.raise_for_status()\npoa = resp.json()['poa']\nprint('status:', poa['status'])\nprint('issuer:', poa['issuer'], '| issued:', poa['issue_date'])\nprint('address:', poa['poa_formatted_address'])\nfor w in poa['warnings']:\n    print('warning:', w['risk'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('document', new Blob([fs.readFileSync('./utility-bill.pdf')]), 'utility-bill.pdf');\nform.append('expected_address', 'Av. Monseñor Tavella 1291, Salta, Argentina');\nform.append('expected_country', 'ARG');\nform.append('expected_first_name', 'Sophia');\nform.append('expected_last_name', 'Martinez');\nform.append('poa_languages_allowed', 'en,es');\nform.append('vendor_data', 'user-123');\n\n// LLM-based extraction takes 5-15s (multi-page PDFs up to ~30s) - allow >=45s\nconst response = await fetch('https://verification.didit.me/v3/poa/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n  signal: AbortSignal.timeout(45_000),\n});\n\nif (!response.ok) throw new Error(`POA verification failed: ${response.status}`);\nconst { poa } = await response.json();\nconsole.log('status:', poa.status);\nconsole.log('warnings:', poa.warnings.map((w) => w.risk));"
          }
        ]
      }
    },
    "/v3/aml/": {
      "post": {
        "summary": "AML screening (standalone)",
        "operationId": "post_v3aml",
        "tags": [
          "Standalone APIs"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [],
        "description": "Screen a person or company against PEP, sanctions, warning/watchlist, and adverse-media datasets in one call.\n\n**Two-score model.** Each hit carries a `match_score` (0–100, identity confidence) and a `risk_score` (0–100, entity risk). Hits whose `match_score` is below `aml_match_score_threshold` (default **93**) are tagged `review_status: \"False Positive\"` and ignored for risk purposes; the highest `risk_score` among the remaining hits becomes `aml.score`. The final `aml.status` follows your thresholds: `score` > `aml_score_review_threshold` (default **100**) → `Declined`; `score` > `aml_score_approve_threshold` (default **80**) → `In Review`; otherwise `Approved`. Whenever `score` is non-zero and ≥ the approve threshold a `POSSIBLE_MATCH_FOUND` warning is added (a score of exactly 0 never warns).\n\n**Match-score weights.** `aml_name_weight` + `aml_dob_weight` + `aml_country_weight` must sum to exactly 100 (defaults 60/25/15). Components missing from the request are re-normalized away; a matching `document_number` acts as a golden key that overrides `match_score` to 100.\n\n**Latency.** Plain screenings typically complete in a few seconds. With `include_adverse_media=true` the API waits for adverse-media enrichment (an initial ~5 s pause plus up to ~25 s of polling) — configure a client timeout of **at least 60 seconds**.\n\n**Persistence.** `save_api_request` defaults to **true**: the screening is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), it appears in the console, a `status.updated` webhook fires, and hit review statuses can be updated later. With `save_api_request=false` nothing is stored and `request_id` is a transient correlation id. `include_ongoing_monitoring=true` keeps re-screening the profile and requires `save_api_request=true` (enforced with a `400`).\n\n**Billing.** One AML API credit per call, charged after a successful screening; an insufficient balance returns `403` before any screening happens.\n\n**Sandbox.** Keys from sandbox applications skip screening and billing entirely and return a static `Approved` response with zero hits.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "full_name"
                ],
                "properties": {
                  "full_name": {
                    "type": "string",
                    "description": "Full name of the person or company to screen.",
                    "example": "Antonio Ejemplo Modelo"
                  },
                  "entity_type": {
                    "type": "string",
                    "enum": [
                      "person",
                      "company"
                    ],
                    "default": "person",
                    "description": "Type of entity to screen. Defaults to `person`."
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "description": "Date of birth (persons) or incorporation date (companies), `YYYY-MM-DD`. Improves match precision via the DOB weight.",
                    "example": "1972-02-29"
                  },
                  "nationality": {
                    "type": "string",
                    "description": "ISO 3166-1 **alpha-2** country code (e.g. `ES`). Invalid codes return `400`. Improves match precision via the country weight.",
                    "example": "ES"
                  },
                  "document_number": {
                    "type": "string",
                    "description": "Identity document number. Not weighted — acts as a golden key: an exact match overrides the hit's `match_score` to 100, a hard mismatch penalizes it."
                  },
                  "aml_score_approve_threshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 80,
                    "description": "Risk-score threshold at or below which the result is `Approved`. Must be ≤ `aml_score_review_threshold` (else `400`)."
                  },
                  "aml_score_review_threshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 100,
                    "description": "Risk-score threshold above which the result is `Declined`. Scores between the two thresholds produce `In Review`."
                  },
                  "aml_name_weight": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 60,
                    "description": "Weight of name similarity in the per-hit match score. The three weights must sum to exactly 100 (else `400`)."
                  },
                  "aml_dob_weight": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 25,
                    "description": "Weight of date of birth in the per-hit match score."
                  },
                  "aml_country_weight": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 15,
                    "description": "Weight of country/nationality in the per-hit match score."
                  },
                  "aml_match_score_threshold": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 100,
                    "default": 93,
                    "description": "Per-hit cutoff: hits with `match_score` below this are `False Positive` (excluded from risk assessment); at or above are `Unreviewed` possible matches."
                  },
                  "include_adverse_media": {
                    "type": "boolean",
                    "default": false,
                    "description": "Also search news media for negative coverage. Adds up to ~30 s of latency while results are gathered — use a ≥60 s client timeout."
                  },
                  "include_ongoing_monitoring": {
                    "type": "boolean",
                    "default": false,
                    "description": "Keep re-screening this profile and push changes via webhook. Requires `save_api_request=true` (else `400`)."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "Persist the screening as a session (console visibility, decision endpoint, webhooks, hit review). Set to `false` for a stateless check."
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Your identifier for the screened user; echoed back and stored with the session."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Free-form JSON stored with the request and echoed back."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/aml/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"full_name\": \"Antonio Ejemplo Modelo\",\n    \"entity_type\": \"person\",\n    \"date_of_birth\": \"1972-02-29\",\n    \"nationality\": \"ES\",\n    \"include_adverse_media\": false,\n    \"vendor_data\": \"user-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/aml/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"full_name\": \"Antonio Ejemplo Modelo\",\n        \"entity_type\": \"person\",\n        \"date_of_birth\": \"1972-02-29\",\n        \"nationality\": \"ES\",\n        \"include_adverse_media\": False,\n        \"vendor_data\": \"user-1234\",\n    },\n    timeout=60,  # adverse media enrichment can add ~30s\n)\nresp.raise_for_status()\nprint(resp.json()[\"aml\"][\"status\"])  # Approved / In Review / Declined"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/aml/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    full_name: 'Antonio Ejemplo Modelo',\n    entity_type: 'person',\n    date_of_birth: '1972-02-29',\n    nationality: 'ES',\n    include_adverse_media: false,\n    vendor_data: 'user-1234',\n  }),\n});\nconst data = await res.json();\nconsole.log(data.aml.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Screening completed. `aml.status` reflects your thresholds; every hit at or above the internal inclusion floor is listed in `aml.hits` with its identity `match_score`, entity `risk_score`, and a `score_breakdown` explaining the match.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "aml": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "In Review",
                            "Declined"
                          ],
                          "description": "`Declined` when `score` > `aml_score_review_threshold`; `In Review` when `score` > `aml_score_approve_threshold`; `Approved` otherwise (including when no score is available)."
                        },
                        "total_hits": {
                          "type": "integer",
                          "description": "Number of entries in `hits`."
                        },
                        "entity_type": {
                          "type": "string",
                          "enum": [
                            "person",
                            "company"
                          ],
                          "description": "Echo of the screened entity type."
                        },
                        "hits": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "description": "One screening hit. `match_score` (0–100) measures identity confidence — hits below `aml_match_score_threshold` are tagged `review_status: \"False Positive\"` and excluded from the risk assessment; hits at or above it are `\"Unreviewed\"` possible matches. `risk_score` (0–100) measures how risky the matched entity is and drives the top-level `aml.score`.",
                            "properties": {
                              "id": {
                                "type": "string",
                                "nullable": true,
                                "description": "Stable identifier of the matched entity in the screening datasets."
                              },
                              "url": {
                                "type": "string",
                                "nullable": true,
                                "description": "Source URL for the matched entity, when available."
                              },
                              "match": {
                                "type": "boolean",
                                "nullable": true,
                                "description": "Legacy exact-match flag. Prefer `match_score` + `review_status`."
                              },
                              "score": {
                                "type": "number",
                                "nullable": true,
                                "description": "DEPRECATED weighted match score in the 0–1 range. Use `match_score` instead."
                              },
                              "match_score": {
                                "type": "number",
                                "nullable": true,
                                "description": "Identity confidence (0–100). Compared against `aml_match_score_threshold` to split False Positives from Possible Matches."
                              },
                              "risk_score": {
                                "type": "number",
                                "nullable": true,
                                "description": "Entity risk score (0–100). The highest `risk_score` among non-false-positive hits becomes the top-level `aml.score`."
                              },
                              "target": {
                                "type": "boolean",
                                "nullable": true
                              },
                              "caption": {
                                "type": "string",
                                "nullable": true,
                                "description": "Display name of the matched entity."
                              },
                              "datasets": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "string"
                                },
                                "description": "Datasets the entity appears in (e.g. `PEP`, `PEP Level 1`, sanctions list names)."
                              },
                              "features": {
                                "type": "object",
                                "nullable": true
                              },
                              "properties": {
                                "type": "object",
                                "nullable": true,
                                "description": "Entity profile: `name`, `alias`, `notes`, `gender`, `birthDate`, `birthPlace`, `nationality`, `position`, and other biographical arrays (each nullable)."
                              },
                              "rca_name": {
                                "type": "string",
                                "nullable": true,
                                "description": "Name of the related close associate through which the entity was matched, when the hit is an RCA."
                              },
                              "pep_matches": {
                                "type": "array",
                                "description": "PEP list entries backing this hit.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "list_name": {
                                      "type": "string"
                                    },
                                    "publisher": {
                                      "type": "string"
                                    },
                                    "source_url": {
                                      "type": "string"
                                    },
                                    "description": {
                                      "type": "string"
                                    },
                                    "matched_name": {
                                      "type": "string"
                                    },
                                    "pep_position": {
                                      "type": "string"
                                    },
                                    "date_of_birth": {
                                      "type": "string"
                                    },
                                    "place_of_birth": {
                                      "type": "string"
                                    },
                                    "aliases": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "education": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "other_sources": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    }
                                  }
                                }
                              },
                              "sanction_matches": {
                                "type": "array",
                                "items": {
                                  "type": "object"
                                },
                                "description": "Sanctions list entries backing this hit (same backing-list shape as `pep_matches`, plus sanction-specific fields)."
                              },
                              "warning_matches": {
                                "type": "array",
                                "items": {
                                  "type": "object"
                                },
                                "description": "Warning/watchlist entries backing this hit."
                              },
                              "adverse_media_matches": {
                                "type": "array",
                                "description": "Adverse-media articles. Populated only when `include_adverse_media=true`.",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "headline": {
                                      "type": "string"
                                    },
                                    "summary": {
                                      "type": "string"
                                    },
                                    "sentiment": {
                                      "type": "string"
                                    },
                                    "sentiment_score": {
                                      "type": "number"
                                    },
                                    "source_url": {
                                      "type": "string"
                                    },
                                    "thumbnail": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "country": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "author_name": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "publication_date": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "adverse_keywords": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "other_sources": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    }
                                  }
                                }
                              },
                              "adverse_media_details": {
                                "type": "object",
                                "nullable": true,
                                "description": "Aggregated adverse-media sentiment for the entity (`sentiment`, `sentiment_score`, `adverse_keywords` frequency map). Populated only when `include_adverse_media=true`."
                              },
                              "first_seen": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "When the entity first appeared in the datasets."
                              },
                              "last_seen": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "Most recent dataset refresh that included the entity."
                              },
                              "linked_entities": {
                                "type": "array",
                                "description": "Known relationships of the matched entity (family, associates, companies).",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "name": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "relation": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "status": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "details": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "active": {
                                      "type": "array",
                                      "items": {
                                        "type": "string"
                                      }
                                    }
                                  }
                                }
                              },
                              "risk_view": {
                                "type": "object",
                                "nullable": true,
                                "description": "Per-dimension risk breakdown (`crimes`, `countries`, `categories`, `custom_list`), each with `score`, `weightage`, `risk_level`, and a `risk_scores` map."
                              },
                              "additional_information": {
                                "type": "object",
                                "nullable": true,
                                "description": "Free-form extra context, e.g. a `flag_summary` array explaining why the entity is flagged."
                              },
                              "review_status": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "False Positive",
                                  "Unreviewed",
                                  "Confirmed Match",
                                  "Inconclusive"
                                ],
                                "description": "`False Positive` — `match_score` below `aml_match_score_threshold`, excluded from risk assessment. `Unreviewed` — possible match awaiting review. `Confirmed Match` / `Inconclusive` — set manually by a reviewer (via the Business Console) on saved sessions; that review capability is not part of this spec."
                              },
                              "score_breakdown": {
                                "type": "object",
                                "nullable": true,
                                "description": "How `match_score` was computed. Name, date of birth, and country each contribute `<component>_score` × `<component>_weight_normalized` points; weights are re-normalized when a component is missing from the screened data. The document number is not weighted — it acts as a 'golden key': `MATCH` overrides the score to 100, `NEUTRAL` leaves it unchanged, `HARD_MISMATCH` applies a penalty.",
                                "properties": {
                                  "name_score": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Raw name similarity (0–100)."
                                  },
                                  "name_weight": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Configured name weight (`aml_name_weight`)."
                                  },
                                  "name_weight_normalized": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Name weight after re-normalization (0–100)."
                                  },
                                  "name_contribution": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Points the name contributed to `total_score`."
                                  },
                                  "dob_score": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Date-of-birth score as a percentage of its weight (−100 to 100)."
                                  },
                                  "dob_weight": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Configured DOB weight (`aml_dob_weight`)."
                                  },
                                  "dob_weight_normalized": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "DOB weight after re-normalization (0–100)."
                                  },
                                  "dob_contribution": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Points the DOB contributed to `total_score`."
                                  },
                                  "country_score": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Country score as a percentage of its weight (−100 to 100)."
                                  },
                                  "country_weight": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Configured country weight (`aml_country_weight`)."
                                  },
                                  "country_weight_normalized": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Country weight after re-normalization (0–100)."
                                  },
                                  "country_contribution": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Points the country contributed to `total_score`."
                                  },
                                  "document_number_match_type": {
                                    "type": "string",
                                    "nullable": true,
                                    "enum": [
                                      "MATCH",
                                      "NEUTRAL",
                                      "HARD_MISMATCH"
                                    ],
                                    "description": "Golden-key result for the document number comparison."
                                  },
                                  "document_number_effect": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Human-readable description of the document-number effect."
                                  },
                                  "total_score": {
                                    "type": "integer",
                                    "nullable": true,
                                    "description": "Final weighted match score (0–100) — mirrors `match_score`."
                                  }
                                }
                              }
                            }
                          }
                        },
                        "score": {
                          "type": "number",
                          "nullable": true,
                          "description": "Overall AML risk score (0–100): the highest `risk_score` among hits that are not False Positives. Drives `status` via the two thresholds."
                        },
                        "screened_data": {
                          "type": "object",
                          "description": "Echo of what was screened: `full_name`, `date_of_birth`, `nationality`, `document_number` (absent fields are `null`).",
                          "properties": {
                            "full_name": {
                              "type": "string"
                            },
                            "date_of_birth": {
                              "type": "string",
                              "nullable": true
                            },
                            "nationality": {
                              "type": "string",
                              "nullable": true
                            },
                            "document_number": {
                              "type": "string",
                              "nullable": true
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "feature": {
                                "type": "string",
                                "description": "Feature that raised the warning."
                              },
                              "risk": {
                                "type": "string",
                                "description": "Machine-readable risk identifier."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "description": "Extra context for the warning, when available."
                              },
                              "log_type": {
                                "type": "string",
                                "description": "`warning`, `information`, or `error` — how the risk affected the outcome."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "One-line human-readable summary."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Full human-readable explanation."
                              }
                            }
                          },
                          "description": "Contains a `POSSIBLE_MATCH_FOUND` warning whenever `score` is non-zero and ≥ `aml_score_approve_threshold` (a score of exactly 0 never warns); empty otherwise."
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "description": "Echo of the `metadata` you sent."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "examples": {
                  "No hits (Approved)": {
                    "summary": "Clean screening — zero hits",
                    "value": {
                      "request_id": "d27c5509-7a66-45a9-b05e-d69d9f4c336a",
                      "aml": {
                        "status": "Approved",
                        "total_hits": 0,
                        "entity_type": "person",
                        "hits": [],
                        "score": 0,
                        "screened_data": {
                          "full_name": "Maximilian Mustermann-Doe",
                          "date_of_birth": "1992-03-14",
                          "nationality": "DE",
                          "document_number": null
                        },
                        "warnings": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": {
                        "internal_ref": "abc-123"
                      },
                      "created_at": "2026-06-11T10:21:07.013938+00:00"
                    }
                  },
                  "PEP hit (Approved — risk below approve threshold)": {
                    "summary": "One PEP possible match; risk score 73 stays under the default approve threshold of 80",
                    "value": {
                      "request_id": "0b7e7a3e-65a1-4f3d-9c7a-1de2f9a40111",
                      "aml": {
                        "status": "Approved",
                        "total_hits": 1,
                        "entity_type": "person",
                        "hits": [
                          {
                            "id": "kDpfszrbkDdaxpMVZw3NLg",
                            "url": "https://news.example.org/articles/sample",
                            "match": false,
                            "score": 0.93,
                            "match_score": 93,
                            "risk_score": 73,
                            "target": null,
                            "caption": "Antonio Ejemplo Modelo",
                            "datasets": [
                              "PEP Level 1",
                              "PEP"
                            ],
                            "features": null,
                            "rca_name": "",
                            "first_seen": "2026-01-07T00:00:00",
                            "last_seen": "2026-01-07T00:00:00",
                            "review_status": "Unreviewed",
                            "properties": {
                              "name": [
                                "Antonio Ejemplo Modelo",
                                "Antonio Ejemplo"
                              ],
                              "alias": [
                                "Antonio Ejemplo Modelo",
                                "Ejemplo Modelo, Pedro"
                              ],
                              "notes": [
                                "Prime Minister of Spain"
                              ],
                              "gender": [
                                "male"
                              ],
                              "birthDate": [
                                "1972-02-29"
                              ],
                              "birthPlace": [
                                "Madrid, Spain"
                              ],
                              "nationality": null,
                              "position": null
                            },
                            "risk_view": {
                              "crimes": {
                                "score": 0,
                                "weightage": 20,
                                "risk_level": "Low",
                                "risk_scores": {}
                              },
                              "countries": {
                                "score": 9.64,
                                "weightage": 30,
                                "risk_level": "Low",
                                "risk_scores": {
                                  "Spain": 25.71
                                }
                              },
                              "categories": {
                                "score": 62.5,
                                "weightage": 50,
                                "risk_level": "High",
                                "risk_scores": {
                                  "PEP": 100,
                                  "PEP Level 1": 100
                                }
                              },
                              "custom_list": {}
                            },
                            "score_breakdown": {
                              "name_score": 89,
                              "name_weight": 60,
                              "name_weight_normalized": 60,
                              "name_contribution": 53.4,
                              "dob_score": 100,
                              "dob_weight": 25,
                              "dob_weight_normalized": 25,
                              "dob_contribution": 25,
                              "country_score": 100,
                              "country_weight": 15,
                              "country_weight_normalized": 15,
                              "country_contribution": 15,
                              "document_number_match_type": "NEUTRAL",
                              "document_number_effect": "No document number provided for screening",
                              "total_score": 93
                            },
                            "pep_matches": [
                              {
                                "aliases": [],
                                "education": [],
                                "list_name": "Presidency of the Government of Spain",
                                "publisher": "Presidency of the Government of Spain",
                                "source_url": "https://news.example.org/articles/sample",
                                "description": "The Presidency of the Government is the central institutional body in Spain that supports the Prime Minister and coordinates the general action of the central government.",
                                "matched_name": "Antonio Ejemplo Modelo",
                                "pep_position": "",
                                "date_of_birth": "1972-02-29",
                                "other_sources": [],
                                "place_of_birth": "Madrid, Spain"
                              }
                            ],
                            "sanction_matches": [],
                            "warning_matches": [],
                            "adverse_media_matches": [],
                            "adverse_media_details": null,
                            "linked_entities": [
                              {
                                "name": [
                                  "Maria Begona Gomez Fernandez"
                                ],
                                "active": [],
                                "status": [],
                                "details": [],
                                "relation": [
                                  "spouse"
                                ]
                              }
                            ],
                            "additional_information": {
                              "flag_summary": [
                                "Antonio Ejemplo Modelo has been flagged as a Level 1 Politically Exposed Person (PEP) from Spain, serving as the President of the Government of Spain."
                              ]
                            }
                          }
                        ],
                        "score": 73,
                        "screened_data": {
                          "full_name": "Pedro Sanchez Perez-Castejon",
                          "date_of_birth": "1972-02-29",
                          "nationality": "ES",
                          "document_number": null
                        },
                        "warnings": []
                      },
                      "vendor_data": null,
                      "metadata": null,
                      "created_at": "2026-06-11T10:23:44.812000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field-level problems return DRF's standard envelope (one array of messages per offending field); cross-field rules (weight sum, threshold ordering, monitoring-without-save) return `{\"error\": [...]}`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing full_name": {
                    "summary": "`full_name` is the only required field",
                    "value": {
                      "full_name": [
                        "This field is required."
                      ]
                    }
                  },
                  "Invalid nationality": {
                    "summary": "`nationality` must be ISO 3166-1 alpha-2",
                    "value": {
                      "nationality": [
                        "Invalid ISO 3166-1 alpha-2 country code."
                      ]
                    }
                  },
                  "Weights do not sum to 100": {
                    "summary": "`aml_name_weight` + `aml_dob_weight` + `aml_country_weight` ≠ 100",
                    "value": {
                      "error": [
                        "AML weights must sum to 100. Current sum: 90 (name: 50, dob: 25, country: 15)"
                      ]
                    }
                  },
                  "Thresholds out of order": {
                    "summary": "Approve threshold above review threshold",
                    "value": {
                      "error": [
                        "Approve threshold (90.0) cannot be greater than review threshold (80.0)"
                      ]
                    }
                  },
                  "Monitoring without saving": {
                    "summary": "`include_ongoing_monitoring=true` with `save_api_request=false`",
                    "value": {
                      "error": [
                        "Ongoing monitoring can only be enabled when the API request is saved."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/face-search/": {
      "post": {
        "summary": "Face Search",
        "description": "Search a face image (1:N) against your application's face search index — faces enrolled from verification sessions, saved standalone API calls, direct user-profile face uploads, and your block/allow lists — and get back ranked similarity matches plus blocklist and duplicate warnings.\n\n**How it works.** The largest detected face in `user_image` is embedded and searched against the index, scoped to your application. Up to **5 matches** above the similarity floor are returned in `matches`, each with `similarity_percentage`, the originating session (`session_id`, `session_number`, `status`, `vendor_data`), `user_details` extracted during that session's document verification, `is_blocklisted` / `is_allowlisted` flags, and a `source` discriminator (`session`, `imported`, or `list_entry`).\n\n- `search_type=most_similar` (default) ranks every enrolled face by similarity — use it for deduplication and fraud-ring investigation.\n- `search_type=blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces, ranking blocklisted entries first — use it when the primary goal is blocklist screening.\n\n`status` is `Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fires; `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) and `MULTIPLE_FACES_DETECTED` (`warning`) never decline. Unlike Passive Liveness, this endpoint does **not** exclude prior sessions with the same `vendor_data` from matching. A passive-liveness analysis also runs internally and is stored with the persisted session, but its result is not part of this response.\n\n**Billing.** Each `200` response consumes one Face Search API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook) and the searched face is enrolled into your face search index. Faces enrolled by Face Search calls are excluded from future Face Search results, so repeated searches never match each other. When `false`, the call is one-shot: nothing is stored, the face is not enrolled, and `match_image_url` values are internal storage paths rather than downloadable URLs.\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3face-search",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "user_image"
                ],
                "properties": {
                  "user_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Front-facing face image to search with. Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image must contain at least one detectable face — otherwise the endpoint returns `400`. When several faces are present, the largest one is searched and a `MULTIPLE_FACES_DETECTED` warning is added."
                  },
                  "search_type": {
                    "type": "string",
                    "default": "most_similar",
                    "enum": [
                      "most_similar",
                      "blocklisted_or_approved"
                    ],
                    "description": "Search policy. `most_similar` (default) ranks every enrolled face in your application by similarity. `blocklisted_or_approved` restricts candidates to blocklisted faces, allowlisted faces, faces from approved sessions, and imported user-profile faces — with blocklisted entries ranked first.",
                    "example": "most_similar"
                  },
                  "rotate_image": {
                    "type": "boolean",
                    "default": false,
                    "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.",
                    "example": false
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session, emits a `status.updated` webhook, presigns `match_image_url` values, and enrolls the searched face into your face search index (Face Search enrollments are excluded from future searches). When `false`, the search is one-shot — no session is stored and the face is not enrolled.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. It does not exclude same-user faces from the search results.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.",
                    "example": {
                      "flow": "dedup_check"
                    }
                  }
                }
              },
              "example": {
                "user_image": "(binary JPEG/PNG selfie)",
                "search_type": "most_similar",
                "rotate_image": false,
                "save_api_request": true,
                "vendor_data": "user-123",
                "metadata": {
                  "flow": "dedup_check"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Face search completed. `face_search.matches` holds up to 5 similar faces ordered by similarity; an empty array means no enrolled face exceeded the similarity floor. `status` is `Declined` only on blocklist hits — duplicate matches alone return `Approved`, so inspect `matches` and `warnings`, not just `status`. When `save_api_request=true`, `request_id` is the persisted session id.",
            "content": {
              "application/json": {
                "examples": {
                  "Blocklist hit (Declined)": {
                    "summary": "The face matches a blocklisted face",
                    "value": {
                      "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
                      "face_search": {
                        "status": "Declined",
                        "total_matches": 1,
                        "matches": [
                          {
                            "session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86",
                            "session_number": 323442,
                            "similarity_percentage": 99.99,
                            "source": "session",
                            "vendor_data": "user-1",
                            "verification_date": "2025-01-01T00:00:00Z",
                            "user_details": {
                              "full_name": "Jane Marie Doe",
                              "document_type": "ID",
                              "document_number": "X1234567"
                            },
                            "match_image_url": "https://<media-host>/face/3f6a1c2e/reference.jpg",
                            "status": "Approved",
                            "is_blocklisted": true,
                            "is_allowlisted": false,
                            "api_service": null
                          }
                        ],
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                40,
                                40,
                                120,
                                120
                              ],
                              "confidence": 0.732973
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [
                          {
                            "risk": "FACE_IN_BLOCKLIST",
                            "feature": "LIVENESS",
                            "additional_data": {
                              "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86",
                              "blocklisted_session_number": 323442,
                              "api_service": null
                            },
                            "log_type": "error",
                            "short_description": "Face in blocklist",
                            "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  },
                  "Duplicate found (Approved)": {
                    "summary": "The face already appeared in another approved session — informational, no decline",
                    "value": {
                      "request_id": "5e0c3a1f-7b2d-4c8e-9f10-2a3b4c5d6e7f",
                      "face_search": {
                        "status": "Approved",
                        "total_matches": 1,
                        "matches": [
                          {
                            "session_id": "1f2e3d4c-5b6a-7980-1122-334455667788",
                            "session_number": 1024,
                            "similarity_percentage": 87.42,
                            "source": "session",
                            "vendor_data": "user-9",
                            "verification_date": "2025-11-20T09:15:00Z",
                            "user_details": null,
                            "match_image_url": "https://<media-host>/face/9c8b7a6d/reference.jpg",
                            "status": "Approved",
                            "is_blocklisted": false,
                            "is_allowlisted": false,
                            "api_service": "PASSIVE_LIVENESS"
                          }
                        ],
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                40,
                                40,
                                120,
                                120
                              ],
                              "confidence": 0.732973
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [
                          {
                            "risk": "DUPLICATED_FACE",
                            "feature": "LIVENESS",
                            "additional_data": {
                              "duplicated_session_id": "1f2e3d4c-5b6a-7980-1122-334455667788",
                              "duplicated_session_number": 1024,
                              "api_service": "PASSIVE_LIVENESS"
                            },
                            "log_type": "information",
                            "short_description": "Duplicated face from other approved session",
                            "long_description": "The system identified a duplicated face from another approved session, requiring further investigation."
                          }
                        ]
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  },
                  "No matches (Approved)": {
                    "summary": "No enrolled face exceeded the similarity floor",
                    "value": {
                      "request_id": "9d8c7b6a-5f4e-3d2c-1b0a-998877665544",
                      "face_search": {
                        "status": "Approved",
                        "total_matches": 0,
                        "matches": [],
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                40,
                                40,
                                120,
                                120
                              ],
                              "confidence": 0.732973
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": []
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "face_search": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Declined` only when a `FACE_IN_BLOCKLIST` or `POSSIBLE_FACE_IN_BLOCKLIST` warning fired. Duplicate matches and multiple-face warnings never decline."
                        },
                        "total_matches": {
                          "type": "integer",
                          "description": "Number of entries in `matches` (0–5).",
                          "example": 1
                        },
                        "matches": {
                          "type": "array",
                          "maxItems": 5,
                          "description": "Up to 5 faces from your face search index that exceed the similarity floor, ordered by similarity (with blocklisted entries ranked first when `search_type=blocklisted_or_approved`). Empty when no enrolled face is similar enough.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "session_id": {
                                "type": "string",
                                "format": "uuid",
                                "nullable": true,
                                "description": "Id of the verification session the matched face belongs to. `null` when `source` is `imported` or `list_entry`.",
                                "example": "882c42d5-8a4d-4d20-8080-a22f57822c86"
                              },
                              "session_number": {
                                "type": "integer",
                                "nullable": true,
                                "description": "Human-friendly session number shown in the Business Console. `null` for non-session matches.",
                                "example": 323442
                              },
                              "similarity_percentage": {
                                "type": "number",
                                "format": "float",
                                "minimum": 0,
                                "maximum": 100,
                                "description": "Similarity between the submitted face and the matched face (0–100).",
                                "example": 99.99
                              },
                              "source": {
                                "type": "string",
                                "enum": [
                                  "session",
                                  "imported",
                                  "list_entry"
                                ],
                                "description": "Where the matched face was enrolled from: `session` — a verification session or a saved standalone API call; `imported` — a face uploaded directly to a user profile via the vendor-user faces upload endpoint; `list_entry` — a face attached to one of your lists."
                              },
                              "vendor_data": {
                                "type": "string",
                                "nullable": true,
                                "description": "`vendor_data` of the matched session (or of the user profile for imported faces).",
                                "example": "user-1"
                              },
                              "verification_date": {
                                "type": "string",
                                "format": "date-time",
                                "nullable": true,
                                "description": "When the matched face was originally captured (`YYYY-MM-DDThh:mm:ssZ`). `null` for `list_entry` matches.",
                                "example": "2025-01-01T00:00:00Z"
                              },
                              "user_details": {
                                "type": "object",
                                "nullable": true,
                                "description": "Identity data extracted during the matched session's document verification. `null` when the matched session has no document data (e.g. liveness-only sessions). For `source: imported` matches it is populated from the vendor-user profile instead: `full_name` set, `document_type`/`document_number` null.",
                                "properties": {
                                  "full_name": {
                                    "type": "string",
                                    "nullable": true,
                                    "example": "Jane Marie Doe"
                                  },
                                  "document_type": {
                                    "type": "string",
                                    "nullable": true,
                                    "example": "ID"
                                  },
                                  "document_number": {
                                    "type": "string",
                                    "nullable": true,
                                    "example": "X1234567"
                                  }
                                }
                              },
                              "match_image_url": {
                                "type": "string",
                                "nullable": true,
                                "description": "Time-limited URL (`https://<media-host>/...`) of the matched face's reference image when `save_api_request=true`. With `save_api_request=false` this is an internal storage path that is not directly downloadable.",
                                "example": "https://<media-host>/face/3f6a1c2e/reference.jpg"
                              },
                              "status": {
                                "type": "string",
                                "nullable": true,
                                "description": "Status of the matched session (`Approved`, `Declined`, `In Review`, …). `null` for `imported`/`list_entry` matches.",
                                "example": "Approved"
                              },
                              "is_blocklisted": {
                                "type": "boolean",
                                "description": "`true` when the matched face is on your face blocklist. Any sufficiently similar blocklisted match declines the search."
                              },
                              "is_allowlisted": {
                                "type": "boolean",
                                "description": "`true` when the matched face is on one of your allowlists. Allowlisted matches suppress duplicate warnings."
                              },
                              "api_service": {
                                "type": "string",
                                "nullable": true,
                                "description": "Set when the matched face was enrolled by a standalone API call (e.g. `PASSIVE_LIVENESS`); `null` for regular verification sessions and imported faces.",
                                "example": null
                              }
                            }
                          }
                        },
                        "user_image": {
                          "type": "object",
                          "description": "Face-detection results for the submitted image. Entries here carry only `bbox` and `confidence`.",
                          "properties": {
                            "entities": {
                              "type": "array",
                              "description": "One entry per detected face. The largest face is the one searched.",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "bbox": {
                                    "type": "array",
                                    "items": {
                                      "type": "integer"
                                    },
                                    "minItems": 4,
                                    "maxItems": 4,
                                    "description": "Bounding box `[x_min, y_min, x_max, y_max]` in pixels.",
                                    "example": [
                                      40,
                                      40,
                                      120,
                                      120
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "format": "float",
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Face-detection confidence (0–1).",
                                    "example": 0.732973
                                  }
                                }
                              }
                            },
                            "best_angle": {
                              "type": "integer",
                              "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation. Never null on this endpoint (defaults to 0 when the model omits it).",
                              "example": 0
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Risk signals. `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` (`error`) decline the search; `MULTIPLE_FACES_DETECTED` (`warning`) and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` (`information`) are advisory.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "enum": [
                                  "MULTIPLE_FACES_DETECTED",
                                  "FACE_IN_BLOCKLIST",
                                  "POSSIBLE_FACE_IN_BLOCKLIST",
                                  "DUPLICATED_FACE",
                                  "POSSIBLE_DUPLICATED_FACE"
                                ],
                                "description": "Machine-readable risk code."
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "LIVENESS"
                                ],
                                "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "`null` for `MULTIPLE_FACES_DETECTED`. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the first matching session."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "warning",
                                  "information"
                                ],
                                "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, or when **no face is detected** in the submitted image. Field errors use DRF's `{field: [messages]}` envelope; face-detection failures use `{\"error\": ...}`.",
            "content": {
              "application/json": {
                "examples": {
                  "No face detected": {
                    "summary": "The image decodes fine but contains no detectable face",
                    "value": {
                      "error": "No face detected in the image"
                    }
                  },
                  "Missing image": {
                    "summary": "`user_image` not included in the form data",
                    "value": {
                      "user_image": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/webp",
                    "value": {
                      "user_image": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 5 MB limit",
                    "value": {
                      "user_image": [
                        "File size should not exceed 5 MB"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/face-search/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'user_image=@./selfie.jpg' \\\n  -F 'search_type=most_similar' \\\n  -F 'save_api_request=true' \\\n  -F 'vendor_data=user-123'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/face-search/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n    files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n    data = {\n        'search_type': 'most_similar',\n        'save_api_request': 'true',\n        'vendor_data': 'user-123',\n    }\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['face_search']['status'])\nprint('matches:', body['face_search']['total_matches'])\nfor m in body['face_search']['matches']:\n    print(f\"  session={m['session_id']} similarity={m['similarity_percentage']} blocklisted={m['is_blocklisted']}\")"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('search_type', 'most_similar');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/face-search/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n});\n\nif (!response.ok) throw new Error(`Face search failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.face_search.status, 'matches:', body.face_search.total_matches);\nbody.face_search.matches.forEach(m =>\n  console.log(`  session=${m.session_id} similarity=${m.similarity_percentage} blocklisted=${m.is_blocklisted}`)\n);"
          }
        ]
      }
    },
    "/v3/sessions/": {
      "get": {
        "summary": "List verification sessions (KYC and/or KYB) with filters and pagination",
        "description": "Returns a paginated list of verification sessions for the application identified by your API key, newest first (descending `session_number`).\n\n**Canonical URL:** the route is `/v3/sessions/` (trailing slash). Requests to `/v3/sessions` without the slash receive a `301` redirect to the slashed URL with the query string preserved; most HTTP clients follow it transparently on GET.\n\n**When to use this endpoint vs webhooks.** Use this endpoint for dashboards, back-office search, reconciliation, and batch exports. Do not poll it to track the progress of individual verifications - configure [webhooks](/integration/webhooks) instead, which push `status.updated` events the moment a session changes. For one session's full result, call `GET /v3/session/{sessionId}/decision/`.\n\n**KYC and KYB rows.** `session_kind` selects which tables are listed:\n- `user` (default) - User Verification (KYC) sessions only. Backward-compatible behavior.\n- `business` - Business Verification (KYB) sessions only. Rows have a different, smaller shape (see the response schema).\n- `all` - both kinds in one response: the KYC page and the KYB page for the same `limit`/`offset` are concatenated in `results` (KYC rows first), so a page can contain up to `2 x limit` rows. `count` is the sum of both kinds and `next`/`previous` reflect the KYC-side pagination. For strict pagination of mixed data, call `user` and `business` separately.\n\nEvery row carries a `session_kind` discriminator (`\"user\"` or `\"business\"`).\n\n**Pagination.** Standard limit/offset: `limit` (default 50) and `offset` (default 0), with `next`/`previous` ready-made page URLs. For KYC rows `count` is exact. For KYB rows `count` is computed with a bounded scan and is capped at 100 - when there are more than 100 matches it stays at 100, so iterate using `next` (or by checking whether a full page came back) rather than computing total pages from `count`.\n\n**Filter semantics.** All filters combine with AND and apply to KYC rows. When `session_kind` is `business` or `all`, the KYB side honors the shared parameters `vendor_data`, `status`, `workflow_id`, `country` (registry-company country code), `date_from`, `date_to`, `search` (company name, registration number — type registration numbers without separators, e.g. `DPC001` not `DPC-001` — vendor_data, session number, or session UUID), `warning`, and `organization_name`; the remaining KYC-only filters are ignored for KYB rows. Comma-separated parameters (`status`, `country`, `document_type`, `region`, `workflow_id`, `document_name`, `warning`, `organization_name`, `session_type`) match any of the listed values (OR within the parameter). `vendor_data` is an exact match - use `search` for partial matching. Unknown values in list filters simply return no rows (no error); malformed typed values (`date_from`, `date_to`, `didit_internal_id`, `api_service`, `session_kind`) return `400` with a per-field error object.\n\n**Search.** `search` adapts to the input: a full UUID looks up `session_id`; digits (with optional `+ - . space`) match session-number prefix, document/personal numbers, phone-number prefix, or `vendor_data`; values containing `@` match email prefixes; any other text matches names (accent-insensitive) and `vendor_data`.\n\nNo fixed per-endpoint rate limit is enforced, but keep list polling infrequent and rely on webhooks for real-time updates.",
        "operationId": "get_v3_sessions",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "session_kind",
            "in": "query",
            "required": false,
            "description": "Which kind of sessions to list: `user` (KYC only, default), `business` (KYB only), or `all` (both, concatenated - see the description for merged-pagination semantics). Matching is case-insensitive (`BUSINESS` works); any value other than a case variant of `user`/`business`/`all` returns `400`.",
            "schema": {
              "type": "string",
              "enum": [
                "user",
                "business",
                "all"
              ],
              "default": "user"
            },
            "example": "all"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum number of rows per page (per kind when `session_kind=all`). Defaults to 50.",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "example": 20
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of rows to skip before the first returned row. Defaults to 0. Prefer following the `next`/`previous` URLs from the response.",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "example": 20
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated list of session statuses to include, e.g. `Approved,In Review`. Values are case-sensitive; see [Verification Statuses](/integration/verification-statuses). Unknown values match nothing.",
            "schema": {
              "type": "string"
            },
            "example": "Approved,In Review"
          },
          {
            "name": "vendor_data",
            "in": "query",
            "required": false,
            "description": "Exact-match filter on the `vendor_data` you sent at session creation (your own user/business identifier). For partial matches use `search` instead.",
            "schema": {
              "type": "string"
            },
            "example": "your-user-id-123"
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Smart free-text search. A full UUID matches `session_id`; numeric input matches session-number prefix, document/personal numbers, phone prefix, or `vendor_data`; input containing `@` matches email prefixes; other text matches names (accent-insensitive) and `vendor_data`. Free text containing digits additionally matches document/personal-number search fields and session-number prefixes.",
            "schema": {
              "type": "string"
            },
            "example": "jane doe"
          },
          {
            "name": "workflow_id",
            "in": "query",
            "required": false,
            "description": "Comma-separated workflow UUIDs (find them on the Workflows page in the Console). Malformed UUIDs in the list are ignored; if none are valid the result is empty.",
            "schema": {
              "type": "string"
            },
            "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab"
          },
          {
            "name": "date_from",
            "in": "query",
            "required": false,
            "description": "Only sessions created on or after this date (UTC), format `YYYY-MM-DD`. Malformed dates return `400`.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-06-01"
          },
          {
            "name": "date_to",
            "in": "query",
            "required": false,
            "description": "Only sessions created up to this date (UTC), format `YYYY-MM-DD`. For KYC rows the entire day is included; for KYB rows the comparison is `created_at <= date_to` (midnight), so use the next day's date to include a full day of KYB sessions.",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "example": "2026-06-30"
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "description": "Comma-separated country filter. For KYC rows: ISO 3166-1 alpha-3 codes of the ID document's issuing state (e.g. `ESP,USA`), which may differ from nationality. For KYB rows: the registry company's country code.",
            "schema": {
              "type": "string"
            },
            "example": "ESP,USA"
          },
          {
            "name": "document_type",
            "in": "query",
            "required": false,
            "description": "Comma-separated ID-document type codes verified in the session (KYC rows only): `P` (Passport), `ID` (Identity Card), `DL` (Driver's License), `RP` (Residence Permit), `SSC` (Social Security Card), `HIC` (Health Insurance Card), `WP` (Work Permit), `TC` (Tax Card), `VISA`, `PSC` (Public Service Card), `BC` (Birth Certificate), `OTHER`. Note the response field `document_type` contains the display name, not the code.",
            "schema": {
              "type": "string"
            },
            "example": "P,ID"
          },
          {
            "name": "document_name",
            "in": "query",
            "required": false,
            "description": "Exact document names; multiple names can be passed comma-separated, but the value is only split on commas when it contains more than one ` - ` separator (a lone name with an embedded comma is matched whole). Names are as recognized by document verification (front or back side), e.g. `Spain - Id Card (2021)`. KYC rows only.",
            "schema": {
              "type": "string"
            },
            "example": "Spain - Id Card (2021)"
          },
          {
            "name": "region",
            "in": "query",
            "required": false,
            "description": "Comma-separated document region/state values (for documents issued per region, such as US driver's licenses). KYC rows only.",
            "schema": {
              "type": "string"
            },
            "example": "CA,TX"
          },
          {
            "name": "didit_internal_id",
            "in": "query",
            "required": false,
            "description": "Filter by Didit's internal user identifier (the `didit_internal_id` response field) to list all sessions of one verified user. Must be a valid UUID; malformed values return `400`. KYC rows only.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147"
          },
          {
            "name": "session_type",
            "in": "query",
            "required": false,
            "description": "Comma-separated session creation channels: `API`, `HOSTED`, `MIGRATED`. The special value `ALL` (or omitting the parameter) disables the filter. KYC rows only.",
            "schema": {
              "type": "string"
            },
            "example": "HOSTED"
          },
          {
            "name": "api_service",
            "in": "query",
            "required": false,
            "description": "Filter standalone API sessions by service. Invalid values return `400`. KYC rows only.",
            "schema": {
              "type": "string",
              "enum": [
                "ID_VERIFICATION",
                "FACE_MATCH",
                "AGE_ESTIMATION",
                "FACE_SEARCH",
                "POA",
                "AML",
                "KYB_REGISTRY",
                "PASSIVE_LIVENESS",
                "DATABASE_VALIDATION",
                "PHONE_VERIFICATION",
                "EMAIL_VERIFICATION"
              ]
            },
            "example": "AML"
          },
          {
            "name": "screened_full_name",
            "in": "query",
            "required": false,
            "description": "Accent-insensitive name search inside AML and database-validation screened data. When combined with `api_service=AML` or `api_service=DATABASE_VALIDATION`, the search is restricted to that feature's screened data. KYC rows only.",
            "schema": {
              "type": "string"
            },
            "example": "Jane Doe"
          },
          {
            "name": "warning",
            "in": "query",
            "required": false,
            "description": "Comma-separated warning risk codes; returns sessions that raised any of them (e.g. `POSSIBLE_FRAUD`). Risk codes are listed per feature in the [warnings documentation](/integration/webhooks). Applies to KYC rows and, with business warning codes, to KYB rows.",
            "schema": {
              "type": "string"
            },
            "example": "POSSIBLE_FRAUD"
          },
          {
            "name": "status_initialized_as_in_review",
            "in": "query",
            "required": false,
            "description": "When `true`, only sessions whose decision initially landed in `In Review` (useful to audit manual-review volume). KYC rows only.",
            "schema": {
              "type": "boolean"
            },
            "example": true
          },
          {
            "name": "is_kyc_to_be_reviewed",
            "in": "query",
            "required": false,
            "description": "`true` returns sessions whose document verification is flagged for manual review; `false` returns sessions with document verification explicitly not flagged. Omit to disable. KYC rows only.",
            "schema": {
              "type": "boolean"
            },
            "example": true
          },
          {
            "name": "euuid",
            "in": "query",
            "required": false,
            "description": "Filter by the end user's euuid (UUID assigned to the verifying user, used by Console deep links). Must be a valid UUID; malformed values return a 400 field error. KYC rows only.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "0f8fad5b-d9cb-469f-a165-70867728950e"
          },
          {
            "name": "organization_name",
            "in": "query",
            "required": false,
            "description": "Comma-separated organization names. Console-oriented; with an API key (already scoped to one application) it only matches your own organization's name.",
            "schema": {
              "type": "string"
            },
            "example": "Acme Inc"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated session list. `results` items are KYC rows, KYB rows, or both depending on `session_kind`; discriminate with each row's `session_kind` field.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "count",
                    "next",
                    "previous",
                    "results"
                  ],
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of sessions matching the filters. Exact for KYC rows; for KYB rows it is capped at 100 (use `next` to know whether more pages exist). For `session_kind=all` it is the sum of both kinds.",
                      "example": 2
                    },
                    "next": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or `null` on the last page.",
                      "example": "https://verification.didit.me/v3/sessions/?limit=20&offset=20"
                    },
                    "previous": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or `null` on the first page.",
                      "example": null
                    },
                    "results": {
                      "type": "array",
                      "description": "Session rows, newest first. With `session_kind=all`, KYC rows come first, then KYB rows.",
                      "items": {
                        "oneOf": [
                          {
                            "title": "KYC session row (session_kind: user)",
                            "type": "object",
                            "description": "Summary row for a User Verification (KYC) session. Identity fields (`full_name`, `country`, `document_type`, `portrait_image`, contact fields) are populated progressively as the user completes verification steps, and are `null` until the relevant step finishes. Fields restricted by the workflow's response-attributes configuration are also returned as `null`.",
                            "required": [
                              "session_id",
                              "session_kind",
                              "session_number",
                              "session_url",
                              "portrait_image",
                              "document_type",
                              "full_name",
                              "country",
                              "status",
                              "vendor_data",
                              "didit_internal_id",
                              "created_at",
                              "features",
                              "phone_number",
                              "email_address",
                              "additional_details"
                            ],
                            "properties": {
                              "session_id": {
                                "type": "string",
                                "format": "uuid",
                                "description": "Unique identifier of the verification session. Use it with `GET /v3/session/{sessionId}/decision/` to fetch the full decision payload.",
                                "example": "5b3720ed-d429-42ef-b67f-37ea805f48ee"
                              },
                              "session_kind": {
                                "type": "string",
                                "enum": [
                                  "user"
                                ],
                                "description": "Row discriminator. Always `\"user\"` for KYC rows; Business Verification (KYB) rows have `\"business\"` and a different shape.",
                                "example": "user"
                              },
                              "session_number": {
                                "type": "integer",
                                "description": "Human-friendly sequential number of the session within your application. Also matched by the `search` parameter.",
                                "example": 720
                              },
                              "session_url": {
                                "type": "string",
                                "nullable": true,
                                "description": "Hosted verification URL the end user opens to complete the flow. `null` when the session has no hosted URL.",
                                "example": "https://verify.didit.me/session/CuXoBYCYVO5Y"
                              },
                              "portrait_image": {
                                "type": "string",
                                "nullable": true,
                                "description": "Expiring (presigned) URL of the best available portrait: the NFC chip photo when available, otherwise the ID document portrait, otherwise the liveness reference image. `null` until one of those exists. Download promptly; the link expires.",
                                "example": "https://<media-host>/portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=..."
                              },
                              "document_type": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "Passport",
                                  "Identity Card",
                                  "Driver's License",
                                  "Residence Permit",
                                  "Health Insurance Card",
                                  "Tax Card"
                                ],
                                "description": "Human-readable type of the verified ID document. `null` before document verification completes, and also `null` after completion when the verified code has no display mapping (`SSC`, `WP`, `VISA`, `PSC`, `BC`, `OTHER`). Note: the `document_type` query parameter filters by short codes (`P`, `ID`, `DL`, `RP`, ...), not by these display values.",
                                "example": "Passport"
                              },
                              "full_name": {
                                "type": "string",
                                "nullable": true,
                                "description": "Best available full name for the session, resolved in order: ID document OCR, AML screened data, database-validation screened data, proof-of-address document, then the expected details provided at session creation. `null` when none is available yet.",
                                "example": "Jane Marie Doe"
                              },
                              "country": {
                                "type": "string",
                                "nullable": true,
                                "description": "ISO 3166-1 alpha-3 country associated with the session, resolved in order: ID document issuing state, database-validation issuing state, proof-of-address issuing state, expected details, then the verified phone's country. May differ from nationality.",
                                "example": "ESP"
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "Not Started",
                                  "In Progress",
                                  "Approved",
                                  "Declined",
                                  "In Review",
                                  "Expired",
                                  "Abandoned",
                                  "Kyc Expired",
                                  "Resubmitted",
                                  "Awaiting User"
                                ],
                                "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page for the lifecycle.",
                                "example": "Approved"
                              },
                              "vendor_data": {
                                "type": "string",
                                "nullable": true,
                                "description": "Your own identifier for the end user, echoed back exactly as sent at session creation.",
                                "example": "your-user-id-123"
                              },
                              "didit_internal_id": {
                                "type": "string",
                                "format": "uuid",
                                "nullable": true,
                                "description": "Didit's internal identifier for the verified user profile behind this session. Stable across multiple sessions of the same user; filterable via the `didit_internal_id` query parameter.",
                                "example": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147"
                              },
                              "created_at": {
                                "type": "string",
                                "format": "date-time",
                                "description": "UTC timestamp when the session was created.",
                                "example": "2026-06-09T12:26:54.328364Z"
                              },
                              "features": {
                                "type": "array",
                                "description": "Per-feature status for every verification step configured in the session's workflow, in workflow order.",
                                "items": {
                                  "type": "object",
                                  "required": [
                                    "feature",
                                    "status"
                                  ],
                                  "properties": {
                                    "feature": {
                                      "type": "string",
                                      "enum": [
                                        "ID_VERIFICATION",
                                        "NFC",
                                        "LIVENESS",
                                        "FACE_MATCH",
                                        "POA",
                                        "QUESTIONNAIRE",
                                        "EMAIL_VERIFICATION",
                                        "PHONE",
                                        "AML",
                                        "IP_ANALYSIS",
                                        "AGE_ESTIMATION",
                                        "DATABASE_VALIDATION"
                                      ],
                                      "description": "Verification feature identifier.",
                                      "example": "ID_VERIFICATION"
                                    },
                                    "status": {
                                      "type": "string",
                                      "enum": [
                                        "Not Finished",
                                        "Approved",
                                        "Declined",
                                        "In Review",
                                        "Resub Requested"
                                      ],
                                      "description": "Status of this feature. `Not Finished` until the step completes.",
                                      "example": "Approved"
                                    }
                                  }
                                }
                              },
                              "phone_number": {
                                "type": "object",
                                "nullable": true,
                                "description": "Phone associated with the session: the verified phone when phone verification ran, otherwise the contact phone provided at session creation. `null` when neither exists.",
                                "required": [
                                  "number",
                                  "is_verified"
                                ],
                                "properties": {
                                  "number": {
                                    "type": "string",
                                    "description": "Phone number in E.164 format.",
                                    "example": "+14155552671"
                                  },
                                  "is_verified": {
                                    "type": "boolean",
                                    "description": "`true` only when the phone-verification feature approved this number.",
                                    "example": true
                                  }
                                }
                              },
                              "email_address": {
                                "type": "object",
                                "nullable": true,
                                "description": "Email associated with the session: the verified email when email verification ran, otherwise the contact email provided at session creation. `null` when neither exists.",
                                "required": [
                                  "email",
                                  "is_verified"
                                ],
                                "properties": {
                                  "email": {
                                    "type": "string",
                                    "format": "email",
                                    "example": "jane@example.com"
                                  },
                                  "is_verified": {
                                    "type": "boolean",
                                    "description": "`true` only when the email-verification feature approved this address.",
                                    "example": true
                                  }
                                }
                              },
                              "additional_details": {
                                "type": "object",
                                "nullable": true,
                                "description": "Compact per-feature extras. Only features that produced data appear as keys; `null` when no feature produced extras. Fields excluded by the workflow's response-attributes configuration are omitted.",
                                "properties": {
                                  "aml": {
                                    "type": "object",
                                    "description": "Present when AML screening ran.",
                                    "properties": {
                                      "total_hits": {
                                        "type": "integer",
                                        "description": "Number of AML screening hits.",
                                        "example": 0
                                      },
                                      "entity_type": {
                                        "type": "string",
                                        "description": "Screened entity type.",
                                        "example": "person"
                                      },
                                      "is_adverse_media_fetched": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "screened_data": {
                                        "type": "object",
                                        "description": "Subset of the data that was screened. Only non-empty keys are included.",
                                        "properties": {
                                          "nationality": {
                                            "type": "string",
                                            "example": "ESP"
                                          },
                                          "document_number": {
                                            "type": "string",
                                            "example": "XX1234567"
                                          },
                                          "date_of_birth": {
                                            "type": "string",
                                            "example": "1990-05-21"
                                          }
                                        }
                                      }
                                    }
                                  },
                                  "database_validation": {
                                    "type": "object",
                                    "description": "Present when database validation ran.",
                                    "properties": {
                                      "validation_type": {
                                        "type": "string",
                                        "example": "DOCUMENT_VALIDATION"
                                      },
                                      "match_type": {
                                        "type": "string",
                                        "example": "FULL_MATCH"
                                      },
                                      "identification_number": {
                                        "type": "string",
                                        "description": "Tax, personal, or document number that was validated (first available).",
                                        "example": "12345678Z"
                                      }
                                    }
                                  },
                                  "poa": {
                                    "type": "object",
                                    "description": "Present when proof of address ran.",
                                    "properties": {
                                      "document_type": {
                                        "type": "string",
                                        "example": "UTILITY_BILL"
                                      },
                                      "issuer": {
                                        "type": "string",
                                        "example": "Energy Co"
                                      },
                                      "address": {
                                        "type": "string",
                                        "example": "Calle Mayor 1, 28013 Madrid"
                                      },
                                      "name_on_document": {
                                        "type": "string",
                                        "example": "Jane Marie Doe"
                                      }
                                    }
                                  },
                                  "liveness": {
                                    "type": "object",
                                    "description": "Present when a liveness check ran.",
                                    "properties": {
                                      "method": {
                                        "type": "string",
                                        "example": "PASSIVE"
                                      },
                                      "score": {
                                        "type": "number",
                                        "description": "Liveness confidence score, rounded to 2 decimals.",
                                        "example": 0.98
                                      }
                                    }
                                  },
                                  "face_match": {
                                    "type": "object",
                                    "description": "Present when face match ran.",
                                    "properties": {
                                      "score": {
                                        "type": "number",
                                        "description": "Face-match similarity score, rounded to 2 decimals.",
                                        "example": 0.95
                                      },
                                      "source_image": {
                                        "type": "string",
                                        "description": "Expiring (presigned) URL of the source face image.",
                                        "example": "https://<media-host>/faces/source.jpg?X-Amz-Expires=3600&..."
                                      },
                                      "target_image": {
                                        "type": "string",
                                        "description": "Expiring (presigned) URL of the target face image.",
                                        "example": "https://<media-host>/faces/target.jpg?X-Amz-Expires=3600&..."
                                      }
                                    }
                                  },
                                  "phone_verification": {
                                    "type": "object",
                                    "description": "Present when phone verification ran.",
                                    "properties": {
                                      "country_code": {
                                        "type": "string",
                                        "description": "ISO 3166-1 alpha-2 country of the phone number.",
                                        "example": "US"
                                      },
                                      "is_temporary": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "is_ported": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "is_blocked": {
                                        "type": "boolean",
                                        "example": false
                                      }
                                    }
                                  },
                                  "email_verification": {
                                    "type": "object",
                                    "description": "Present when email verification ran.",
                                    "properties": {
                                      "is_breached": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "is_disposable": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "is_undeliverable": {
                                        "type": "boolean",
                                        "example": false
                                      },
                                      "is_blocklisted": {
                                        "type": "boolean",
                                        "example": false
                                      }
                                    }
                                  }
                                }
                              }
                            }
                          },
                          {
                            "title": "KYB session row (session_kind: business)",
                            "type": "object",
                            "description": "Summary row for a Business Verification (KYB) session. Returned only when `session_kind` is `business` or `all`.",
                            "required": [
                              "session_id",
                              "session_kind",
                              "session_number",
                              "status",
                              "vendor_data",
                              "created_at",
                              "workflow_id",
                              "workflow_type",
                              "workflow_label",
                              "company_name",
                              "registration_number",
                              "country"
                            ],
                            "properties": {
                              "session_id": {
                                "type": "string",
                                "format": "uuid",
                                "description": "Unique identifier of the business verification session.",
                                "example": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52"
                              },
                              "session_kind": {
                                "type": "string",
                                "enum": [
                                  "business"
                                ],
                                "description": "Row discriminator. Always `\"business\"` for KYB rows.",
                                "example": "business"
                              },
                              "session_number": {
                                "type": "integer",
                                "description": "Human-friendly sequential number of the business session within your application. Numbered independently from KYC sessions.",
                                "example": 42
                              },
                              "status": {
                                "type": "string",
                                "enum": [
                                  "Not Started",
                                  "In Progress",
                                  "Approved",
                                  "Declined",
                                  "In Review",
                                  "Expired",
                                  "Abandoned",
                                  "Kyc Expired",
                                  "Resubmitted",
                                  "Awaiting User"
                                ],
                                "description": "Overall session status. See the [Verification Statuses](/integration/verification-statuses) page.",
                                "example": "Approved"
                              },
                              "vendor_data": {
                                "type": "string",
                                "nullable": true,
                                "description": "Your own identifier for the business, echoed back exactly as sent at session creation.",
                                "example": "your-company-id-77"
                              },
                              "created_at": {
                                "type": "string",
                                "format": "date-time",
                                "description": "UTC timestamp when the session was created.",
                                "example": "2026-06-08T09:14:02.118450Z"
                              },
                              "workflow_id": {
                                "type": "string",
                                "format": "uuid",
                                "nullable": true,
                                "description": "Identifier of the workflow this session was created with.",
                                "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab"
                              },
                              "workflow_type": {
                                "type": "string",
                                "nullable": true,
                                "description": "Workflow type. `kyb` for business workflows.",
                                "example": "kyb"
                              },
                              "workflow_label": {
                                "type": "string",
                                "nullable": true,
                                "description": "Display name of the workflow.",
                                "example": "Standard KYB"
                              },
                              "company_name": {
                                "type": "string",
                                "nullable": true,
                                "description": "Name of the company being verified, from the registry company linked to the session. `null` until a company is selected.",
                                "example": "Acme Holdings S.L."
                              },
                              "registration_number": {
                                "type": "string",
                                "nullable": true,
                                "description": "Company registration number from the registry company linked to the session.",
                                "example": "B12345678"
                              },
                              "country": {
                                "type": "string",
                                "nullable": true,
                                "description": "Country code of the registry company linked to the session.",
                                "example": "ES"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                },
                "examples": {
                  "KYC sessions (default)": {
                    "summary": "session_kind=user - one completed and one not-started session",
                    "value": {
                      "count": 2,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee",
                          "session_kind": "user",
                          "session_number": 720,
                          "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y",
                          "portrait_image": "https://<media-host>/portraits/5b3720ed.jpg?X-Amz-Expires=3600&X-Amz-Signature=...",
                          "document_type": "Passport",
                          "full_name": "Jane Marie Doe",
                          "country": "ESP",
                          "status": "Approved",
                          "vendor_data": "your-user-id-123",
                          "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147",
                          "created_at": "2026-06-09T12:26:54.328364Z",
                          "features": [
                            {
                              "feature": "ID_VERIFICATION",
                              "status": "Approved"
                            },
                            {
                              "feature": "LIVENESS",
                              "status": "Approved"
                            },
                            {
                              "feature": "FACE_MATCH",
                              "status": "Approved"
                            },
                            {
                              "feature": "AML",
                              "status": "Approved"
                            },
                            {
                              "feature": "IP_ANALYSIS",
                              "status": "Approved"
                            }
                          ],
                          "phone_number": {
                            "number": "+14155552671",
                            "is_verified": true
                          },
                          "email_address": {
                            "email": "jane@example.com",
                            "is_verified": true
                          },
                          "additional_details": {
                            "aml": {
                              "total_hits": 0,
                              "entity_type": "person",
                              "is_adverse_media_fetched": false
                            },
                            "liveness": {
                              "method": "PASSIVE",
                              "score": 0.98
                            },
                            "face_match": {
                              "score": 0.95
                            }
                          }
                        },
                        {
                          "session_id": "038ad62f-5ebd-46c2-afe9-4b2956bf687f",
                          "session_kind": "user",
                          "session_number": 719,
                          "session_url": "https://verify.didit.me/session/7RGKiO6ioGH6",
                          "portrait_image": null,
                          "document_type": null,
                          "full_name": null,
                          "country": null,
                          "status": "Not Started",
                          "vendor_data": "your-user-id-124",
                          "didit_internal_id": "eb4e679d-ca4b-461f-9685-687bac29ebdb",
                          "created_at": "2026-06-09T08:02:23.343053Z",
                          "features": [
                            {
                              "feature": "ID_VERIFICATION",
                              "status": "Not Finished"
                            },
                            {
                              "feature": "LIVENESS",
                              "status": "Not Finished"
                            },
                            {
                              "feature": "FACE_MATCH",
                              "status": "Not Finished"
                            },
                            {
                              "feature": "AML",
                              "status": "Not Finished"
                            },
                            {
                              "feature": "IP_ANALYSIS",
                              "status": "Not Finished"
                            }
                          ],
                          "phone_number": null,
                          "email_address": null,
                          "additional_details": null
                        }
                      ]
                    }
                  },
                  "Mixed KYC + KYB (session_kind=all)": {
                    "summary": "session_kind=all - KYC row followed by a KYB row",
                    "value": {
                      "count": 2,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "session_id": "5b3720ed-d429-42ef-b67f-37ea805f48ee",
                          "session_kind": "user",
                          "session_number": 720,
                          "session_url": "https://verify.didit.me/session/CuXoBYCYVO5Y",
                          "portrait_image": null,
                          "document_type": "Identity Card",
                          "full_name": "Jane Marie Doe",
                          "country": "ESP",
                          "status": "In Review",
                          "vendor_data": "your-user-id-123",
                          "didit_internal_id": "d9e78474-6c8d-4f2a-bb90-6f5c2f3a1147",
                          "created_at": "2026-06-09T12:26:54.328364Z",
                          "features": [
                            {
                              "feature": "ID_VERIFICATION",
                              "status": "In Review"
                            },
                            {
                              "feature": "LIVENESS",
                              "status": "Approved"
                            }
                          ],
                          "phone_number": null,
                          "email_address": null,
                          "additional_details": {
                            "liveness": {
                              "method": "PASSIVE",
                              "score": 0.98
                            }
                          }
                        },
                        {
                          "session_id": "8a1f63c2-1b9e-4f3a-9c41-2f6a1f0b7d52",
                          "session_kind": "business",
                          "session_number": 42,
                          "status": "Approved",
                          "vendor_data": "your-company-id-77",
                          "created_at": "2026-06-08T09:14:02.118450Z",
                          "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab",
                          "workflow_type": "kyb",
                          "workflow_label": "Standard KYB",
                          "company_name": "Acme Holdings S.L.",
                          "registration_number": "B12345678",
                          "country": "ES"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "301": {
            "description": "Redirect to the canonical trailing-slash URL. Returned when the request path is `/v3/sessions` (no trailing slash); the `Location` header points to `/v3/sessions/` with the query string preserved. GET clients typically follow it automatically."
          },
          "400": {
            "description": "Invalid query parameter. The body is an object keyed by the offending parameter.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid session_kind": {
                    "summary": "session_kind not one of user/business/all",
                    "value": {
                      "session_kind": "Must be \"user\", \"business\", or \"all\"."
                    }
                  },
                  "Invalid date": {
                    "summary": "Malformed date_from/date_to",
                    "value": {
                      "date_from": [
                        "Enter a valid date."
                      ]
                    }
                  },
                  "Invalid didit_internal_id": {
                    "summary": "didit_internal_id is not a UUID",
                    "value": {
                      "didit_internal_id": [
                        "Enter a valid UUID."
                      ]
                    }
                  },
                  "Invalid api_service": {
                    "summary": "api_service not in the allowed choices",
                    "value": {
                      "api_service": [
                        "Select a valid choice. FOO is not one of the available choices."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, malformed, or revoked API key. This API never returns `401`; all authentication failures are `403`.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -s 'https://verification.didit.me/v3/sessions/?status=Approved,In%20Review&date_from=2026-06-01&limit=20' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = \"https://verification.didit.me/v3/sessions/\"\nheaders = {\"x-api-key\": \"YOUR_API_KEY\"}\nparams = {\n    \"session_kind\": \"user\",          # \"user\" (default) | \"business\" | \"all\"\n    \"status\": \"Approved,In Review\",  # comma-separated, OR within the list\n    \"date_from\": \"2026-06-01\",\n    \"limit\": 20,\n}\n\nwhile url:\n    response = requests.get(url, headers=headers, params=params)\n    response.raise_for_status()\n    page = response.json()\n    for session in page[\"results\"]:\n        print(session[\"session_kind\"], session[\"session_id\"], session[\"status\"])\n    url, params = page[\"next\"], None  # \"next\" already carries the query string"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const params = new URLSearchParams({\n  session_kind: 'all',\n  status: 'Approved,In Review',\n  date_from: '2026-06-01',\n  limit: '20',\n});\n\nconst response = await fetch(\n  `https://verification.didit.me/v3/sessions/?${params}`,\n  { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\n\nconst { count, next, results } = await response.json();\nfor (const session of results) {\n  // session.session_kind is \"user\" (KYC) or \"business\" (KYB)\n  console.log(session.session_kind, session.session_id, session.status);\n}"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/share/": {
      "post": {
        "summary": "Mint a share token for a finished verification session",
        "description": "Mint a short-lived JWT that lets a specific Didit application import this finished session. Pair with `POST /v3/session/import-shared/`.",
        "operationId": "post_v3_session_share",
        "tags": [
          "Sessions"
        ],
        "responses": {
          "200": {
            "description": "Share token minted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "share_token": {
                      "type": "string",
                      "description": "HS256-signed JWT. Pass this verbatim as `share_token` to [`POST /v3/session/import-shared/`](/sessions-api/import-shared-session) on the target application.",
                      "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                    },
                    "for_application_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Echoes the target application UUID that the token is bound to.",
                      "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee"
                    },
                    "session_kind": {
                      "type": "string",
                      "enum": [
                        "user",
                        "business"
                      ],
                      "description": "Whether the source session is a User Verification (KYC) or Business Verification (KYB) session."
                    }
                  }
                },
                "examples": {
                  "User session": {
                    "summary": "Share a KYC session",
                    "value": {
                      "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzZXNzaW9uX2lkIjoiMjBiYjNjM2ItNjE0Ni00MzlmLTg0YTQtYmQzMGQwMGFjNmEyIiwiZnJvbV9hcHBsaWNhdGlvbl9pZCI6ImRiZDIwZTM0LTQyZTktNGYyYy1iYTkxLWNmMDc2MjAxNmY2NCIsImZvcl9hcHBsaWNhdGlvbl9pZCI6ImE1ZjNiY2EyLTQ2ZTItNDExZS05MGVmLWE1ODA5MDBhNTdlZSIsImlhdCI6MTc1MzYzMDY2NiwiZXhwIjoxNzUzNjM0MjY2fQ.JJ9pNE_hqZsOtbR0XYZIWw4JzidjdEl279iUrsIkhGE",
                      "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee",
                      "session_kind": "user"
                    }
                  },
                  "Business session": {
                    "summary": "Share a KYB session",
                    "value": {
                      "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                      "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee",
                      "session_kind": "business"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field errors are keyed by field name; the not-finished error arrives under a `detail` key (as an array).",
            "content": {
              "application/json": {
                "examples": {
                  "Wrong status": {
                    "summary": "Source session is not finished",
                    "value": {
                      "detail": [
                        "Only finished sessions (\"Approved\", \"Declined\", \"In Review\") can be shared."
                      ]
                    }
                  },
                  "Target missing": {
                    "summary": "Target application does not exist",
                    "value": {
                      "for_application_id": [
                        "Target application does not exist."
                      ]
                    }
                  },
                  "Self-share": {
                    "summary": "Target is the calling application",
                    "value": {
                      "for_application_id": [
                        "Cannot share a session with the same application."
                      ]
                    }
                  },
                  "TTL bounds": {
                    "summary": "ttl_in_seconds outside bounds",
                    "value": {
                      "ttl_in_seconds": [
                        "Ensure this value is greater than or equal to 60."
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid credentials. Unlike most v3 endpoints, this endpoint authenticates through the permission decorator and returns `401` when no valid token/key is presented.",
            "content": {
              "application/json": {
                "examples": {
                  "Unauthorized": {
                    "summary": "Unauthorized",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The credentials are valid but lack the `write:sessions` permission for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "No Permission": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    },
                    "summary": "No Permission"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No session with the given `session_id` exists in your application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not Found": {
                    "summary": "Session not found",
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the source verification session to mint a token for.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          }
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "for_application_id"
                ],
                "properties": {
                  "for_application_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID of the Didit application that will redeem the token. Must exist, must not be soft-deleted, and must differ from the calling application. Find it in the Business Console under **Settings → Application**.",
                    "example": "a5f3bca2-46e2-411e-90ef-a580900a57ee"
                  },
                  "ttl_in_seconds": {
                    "type": "integer",
                    "description": "Token lifetime, in seconds. Minimum `60`, maximum `86400` (24 h). Defaults to `3600` (1 h).",
                    "minimum": 60,
                    "maximum": 86400,
                    "default": 3600,
                    "example": 3600
                  }
                }
              },
              "example": {
                "for_application_id": "a5f3bca2-46e2-411e-90ef-a580900a57ee",
                "ttl_in_seconds": 7200
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n    \"ttl_in_seconds\": 7200\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.post(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/\",\n    headers={\n        'x-api-key': 'YOUR_API_KEY',\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"for_application_id\": \"a5f3bca2-46e2-411e-90ef-a580900a57ee\",\n        \"ttl_in_seconds\": 7200,\n    },\n)\nresponse.raise_for_status()\nshare_token = response.json()[\"share_token\"]"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/share/',\n  {\n    method: 'POST',\n    headers: {\n      'x-api-key': 'YOUR_API_KEY',\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      for_application_id: 'a5f3bca2-46e2-411e-90ef-a580900a57ee',\n      ttl_in_seconds: 7200,\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst { share_token } = await response.json();"
          }
        ]
      }
    },
    "/v3/session/import-shared/": {
      "post": {
        "summary": "Redeem a share token to clone a verification session into your application",
        "description": "Redeem a share token to clone a KYC or KYB session into the calling application with a fresh `session_id`. Not idempotent — a token redeems once per receiver.",
        "operationId": "post_v3_session_import_shared",
        "tags": [
          "Sessions"
        ],
        "responses": {
          "201": {
            "description": "Session cloned. For user (KYC) sessions the body is the V2 decision payload (`session_id`, `session_number`, `status`, `workflow_id`, per-feature blocks such as `id_verification`, `liveness`, `aml`, plus `created_at`/`expires_at` — no `session_kind` field). For business (KYB) sessions it is the V3 KYB decision payload, which includes `session_kind: \"business\"` and blocks like `registry_checks` and `aml_screenings`. With `trust_review: false` the cloned session's `status` is forced to `In Review`.",
            "content": {
              "application/json": {
                "examples": {
                  "User session imported": {
                    "summary": "Cloned KYC session (truncated — full V2 decision payload in reality)",
                    "value": {
                      "session_id": "11111111-2222-3333-4444-555555555555",
                      "session_number": 43762,
                      "session_url": null,
                      "status": "In Review",
                      "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab",
                      "vendor_data": "user-1",
                      "created_at": "2026-05-17T08:42:11Z",
                      "expires_at": "2026-05-24T08:42:11Z"
                    }
                  },
                  "Business session imported": {
                    "summary": "Cloned KYB session (truncated — full V3 KYB decision payload in reality)",
                    "value": {
                      "session_id": "22222222-3333-4444-5555-666666666666",
                      "session_kind": "business",
                      "session_number": 982,
                      "session_url": null,
                      "status": "Approved",
                      "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab",
                      "vendor_data": "company-1"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error (token invalid, expired, wrong target, or references a session that no longer exists).",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Share Token": {
                    "summary": "Invalid JWT",
                    "value": {
                      "share_token": [
                        "Invalid share token."
                      ]
                    }
                  },
                  "Expired Share Token": {
                    "summary": "Token past `exp`",
                    "value": {
                      "share_token": [
                        "Share token has expired."
                      ]
                    }
                  },
                  "Wrong audience": {
                    "summary": "Token not bound to this application",
                    "value": {
                      "share_token": [
                        "This token is not valid for this application."
                      ]
                    }
                  },
                  "Original missing": {
                    "summary": "Source session was deleted",
                    "value": {
                      "share_token": [
                        "Original session does not exist."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid API key (this endpoint returns `403`, never `401`, for authentication failures), or this source session has already been imported into the calling application (one redeem per receiver).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "summary": "Forbidden",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Already imported": {
                    "summary": "Source already imported once",
                    "value": {
                      "detail": "This session has already been shared with your application."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`workflow_id` does not belong to the calling application.",
            "content": {
              "application/json": {
                "examples": {
                  "Workflow not found": {
                    "summary": "Workflow unknown",
                    "value": {
                      "detail": "Workflow does not exist for this application."
                    }
                  }
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "share_token",
                  "trust_review",
                  "workflow_id"
                ],
                "properties": {
                  "share_token": {
                    "type": "string",
                    "description": "JWT share token issued by [`POST /v3/session/{sessionId}/share/`](/sessions-api/share-session).",
                    "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                  },
                  "trust_review": {
                    "type": "boolean",
                    "description": "If `true`, the cloned session keeps the source's final `status`. If `false`, it is forced into `In Review`.",
                    "example": false
                  },
                  "workflow_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "UUID of a workflow in the calling application. Cross-application IDs are rejected with `404`.",
                    "example": "9f9b1234-aaaa-bbbb-cccc-1234567890ab"
                  },
                  "vendor_data": {
                    "type": "string",
                    "nullable": true,
                    "description": "Optional override for the cloned session's `vendor_data`.",
                    "example": "user-1"
                  }
                }
              },
              "example": {
                "share_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
                "trust_review": false,
                "workflow_id": "9f9b1234-aaaa-bbbb-cccc-1234567890ab",
                "vendor_data": "user-1"
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST \\\n  https://verification.didit.me/v3/session/import-shared/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n    \"trust_review\": false,\n    \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n    \"vendor_data\": \"user-1\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.post(\n    \"https://verification.didit.me/v3/session/import-shared/\",\n    headers={\n        'x-api-key': 'YOUR_API_KEY',\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"share_token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\",\n        \"trust_review\": False,\n        \"workflow_id\": \"9f9b1234-aaaa-bbbb-cccc-1234567890ab\",\n        \"vendor_data\": \"user-1\",\n    },\n)\nresponse.raise_for_status()\nimported = response.json()"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/import-shared/',\n  {\n    method: 'POST',\n    headers: {\n      'x-api-key': 'YOUR_API_KEY',\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      share_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',\n      trust_review: false,\n      workflow_id: '9f9b1234-aaaa-bbbb-cccc-1234567890ab',\n      vendor_data: 'user-1',\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst imported = await response.json();"
          }
        ]
      }
    },
    "/v3/kyb/search/": {
      "post": {
        "summary": "KYB registry company search (standalone)",
        "operationId": "post_v3_kyb_search",
        "tags": [
          "Standalone APIs"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [],
        "description": "Search official company registries for candidate companies by name and/or registration number. This is step 1 of the two-step registry flow: **search** returns lightweight candidates, then `POST /v3/kyb/select/` retrieves the full profile of one candidate.\n\n**The search is free** — no balance check, no billing. Candidates always come back with `fetch_status: \"pending\"`: the full company data is only fetched (and billed) on select.\n\n**`kyb_response_id` is ephemeral.** It is a per-search handle, not a stable company id. Select promptly after searching and re-search to get a fresh handle instead of storing it.\n\n**Two modes.**\n- *Synchronous (default):* the API polls the registry until the search resolves and returns the final candidate list (`search_resolved: true`).\n- *Asynchronous:* pass `webhook_url` and the API returns immediately — usually with `search_status: \"pending\"` and an empty candidate list — then POSTs a `kyb.registry_search.resolved` callback to your URL when the registry finishes. The callback body is `{\"event_id\", \"event_type\": \"kyb.registry_search.resolved\", \"request_id\", \"vendor_data\", \"metadata\", \"search_status\", \"search_resolved\", \"kyb_registry\", \"timestamp\", \"created_at\"}` — note `timestamp` and `created_at` in the callback are epoch integers, unlike this endpoint's ISO `created_at` — and is sent **unsigned** (header `X-Didit-Unsigned-Callback: true`, no `X-Signature`).\n\n**Country codes.** ISO 3166-1 alpha-2 (`GB`, `DE`), plus `XX-YY` subdivision codes where registries are regional — e.g. `US-CA` for California.\n\n**Sandbox.** Keys from sandbox applications return one static candidate without contacting any registry.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "country_code"
                ],
                "properties": {
                  "country_code": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "ISO 3166-1 alpha-2 code of the country whose company registry to search. For example, `GB`. Where company registries operate at state or province level, append the ISO 3166-2 subdivision code as `XX-YY`: `US-CA` searches the California registry. United States searches always require the subdivision — a bare `US` is rejected. Case-insensitive; normalized to upper case.",
                    "example": "GB"
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Company name to search for. Either `name` or `registration_number` is required.",
                    "example": "Tesco"
                  },
                  "registration_number": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Registry registration number to search for. Either `name` or `registration_number` is required."
                  },
                  "search_type": {
                    "type": "string",
                    "enum": [
                      "contains",
                      "start_with",
                      "fuzzy"
                    ],
                    "default": "contains",
                    "description": "Name-matching strategy applied by the registry search."
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Your identifier for this lookup; echoed back and included in the webhook callback."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Free-form JSON; echoed back and included in the webhook callback."
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 500,
                    "description": "Switches the search to asynchronous mode: the API returns immediately and POSTs an unsigned `kyb.registry_search.resolved` callback to this URL when the candidate list is ready."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/kyb/search/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"country_code\": \"GB\",\n    \"name\": \"Tesco\",\n    \"search_type\": \"contains\",\n    \"vendor_data\": \"company-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/kyb/search/\",\n    headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n    json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n    timeout=60,  # synchronous mode waits for the registry to resolve\n)\nresp.raise_for_status()\ncandidates = resp.json()[\"kyb_registry\"][\"companies\"]\nkyb_response_id = candidates[0][\"kyb_response_id\"]  # pass to /v3/kyb/select/"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/kyb/search/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({ country_code: 'GB', name: 'Tesco' }),\n});\nconst data = await res.json();\nconst kybResponseId = data.kyb_registry.companies[0].kyb_response_id;"
          }
        ],
        "responses": {
          "200": {
            "description": "Search accepted. Synchronous mode returns the resolved candidate list; webhook mode returns immediately (often `search_status: \"pending\"`) and delivers the final list via the `kyb.registry_search.resolved` callback.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "With `webhook_url`: the persisted search-request id, echoed as `request_id` in the `kyb.registry_search.resolved` callback. Without `webhook_url`: a transient correlation UUID (the search is not stored)."
                    },
                    "kyb_registry": {
                      "type": "object",
                      "properties": {
                        "companies": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "kyb_response_id": {
                                "type": "string",
                                "nullable": true,
                                "description": "Handle for this candidate — pass it to `POST /v3/kyb/select/` to retrieve the full profile. Ephemeral and scoped to this search: do not store it long-term; re-search to get a fresh one.",
                                "example": "69aeeb95febb0f1704042259"
                              },
                              "name": {
                                "type": "string",
                                "nullable": true,
                                "description": "Registered company name.",
                                "example": "Tesco PLC"
                              },
                              "registration_number": {
                                "type": "string",
                                "nullable": true,
                                "description": "Registry registration number.",
                                "example": "00445790"
                              },
                              "status": {
                                "type": "string",
                                "nullable": true,
                                "description": "Normalized registry status of the company (e.g. `active`, `dissolved`, `inactive`, `struck off`).",
                                "example": "active"
                              },
                              "type": {
                                "type": "string",
                                "nullable": true,
                                "description": "Legal form as reported by the registry.",
                                "example": "Public Limited Company"
                              },
                              "risk_level": {
                                "type": "string",
                                "nullable": true,
                                "description": "Registry-reported risk level, when available. Usually `null` at search time."
                              },
                              "fetch_status": {
                                "type": "string",
                                "nullable": true,
                                "description": "Always `pending` for search candidates — the full company profile is only fetched (and billed) when you select the candidate.",
                                "example": "pending"
                              }
                            }
                          },
                          "description": "Deduplicated candidate list. Empty while `search_resolved=false` (webhook mode) or when nothing matched."
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "total": {
                              "type": "integer",
                              "description": "Number of candidates returned."
                            },
                            "page": {
                              "type": "integer"
                            },
                            "per_page": {
                              "type": "integer",
                              "example": 25
                            }
                          }
                        },
                        "search_status": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "pending",
                            "resolved"
                          ],
                          "description": "Registry search lifecycle status."
                        },
                        "search_resolved": {
                          "type": "boolean",
                          "description": "`true` once the candidate list is final."
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "description": "Echo of the `metadata` you sent."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "examples": {
                  "Resolved search": {
                    "summary": "Synchronous mode — final candidate list",
                    "value": {
                      "request_id": "0c7e9a40-3a13-4f7e-8a44-9d2e2f1c5b6a",
                      "kyb_registry": {
                        "companies": [
                          {
                            "kyb_response_id": "69aeeb95febb0f1704042259",
                            "name": "Tesco PLC",
                            "registration_number": "00445790",
                            "status": "active",
                            "type": "Public Limited Company",
                            "risk_level": null,
                            "fetch_status": "pending"
                          },
                          {
                            "kyb_response_id": "69aeeb95febb0f170404225a",
                            "name": "E-TESCO LTD",
                            "registration_number": "15168476",
                            "status": "active",
                            "type": "Private Limited Company",
                            "risk_level": null,
                            "fetch_status": "pending"
                          }
                        ],
                        "pagination": {
                          "total": 2,
                          "page": 1,
                          "per_page": 25
                        },
                        "search_status": "resolved",
                        "search_resolved": true
                      },
                      "vendor_data": "company-1234",
                      "metadata": null,
                      "created_at": "2026-06-11T10:40:00.000000+00:00"
                    }
                  },
                  "Pending search (webhook mode)": {
                    "summary": "`webhook_url` provided — candidates arrive via the kyb.registry_search.resolved callback",
                    "value": {
                      "request_id": "5f2d8c1b-7e4a-4b9c-9f0d-3a6e8c2b1d4f",
                      "kyb_registry": {
                        "companies": [],
                        "pagination": {
                          "total": 0,
                          "page": 1,
                          "per_page": 25
                        },
                        "search_status": "pending",
                        "search_resolved": false
                      },
                      "vendor_data": "company-1234",
                      "metadata": {
                        "source": "onboarding"
                      },
                      "created_at": "2026-06-11T10:41:00.000000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field-level problems use DRF's standard envelope; the name/registration-number rule reports under `non_field_errors`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing search criteria": {
                    "summary": "Neither `name` nor `registration_number` provided",
                    "value": {
                      "non_field_errors": [
                        "Either 'name' or 'registration_number' is required."
                      ]
                    }
                  },
                  "Invalid country_code": {
                    "summary": "Must be `XX` (alpha-2) or `XX-YY` subdivision",
                    "value": {
                      "country_code": [
                        "country_code must be a 2-letter ISO code."
                      ]
                    }
                  },
                  "Invalid search_type": {
                    "summary": "Only contains / start_with / fuzzy",
                    "value": {
                      "search_type": [
                        "\"exact\" is not a valid choice."
                      ]
                    }
                  },
                  "Invalid webhook_url": {
                    "summary": "`webhook_url` must be a valid URL",
                    "value": {
                      "webhook_url": [
                        "Enter a valid URL."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. This endpoint is free, so there is no credit-shortfall variant.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/kyb/select/": {
      "post": {
        "summary": "KYB registry company select (standalone)",
        "operationId": "post_v3_kyb_select",
        "tags": [
          "Standalone APIs"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [],
        "description": "Retrieve the full registry profile of one candidate returned by `POST /v3/kyb/search/` — company details, officers, beneficial owners, addresses, industries, filings, and the complete raw registry payload.\n\n**Pairing with search.** Pass the candidate's `kyb_response_id` exactly as returned by the search. The handle is ephemeral and scoped to that search — select promptly, and re-search for a fresh handle rather than storing it.\n\n**Billing.** Each select is billed at the KYB registry price (search is free). The balance is checked before any registry call (`403` when short) and the charge is recorded against the created business session.\n\n**Persistence is mandatory.** Every select creates a business session (its id is the `request_id`) so billing and retrieval can be tracked; it appears in the console. `save_api_request` is accepted for symmetry but must be `true` (the default) — sending `false` returns `400`.\n\n**Resolution.** The API polls the registry for the full profile before responding. If the registry is still processing after polling, the response comes back with `fetch_status: \"pending\"` / `data_resolved: false` and sparse fields; the stored record completes automatically once the registry resolves (the business session status updates with it) — re-check it in the Business Console or by polling `GET /v3/session/{request_id}/decision/` (business sessions resolve through the same decision endpoint).\n\n**Status.** `kyb_registry.status` is Didit's assessment of the registry check: companies that are not active in the registry, or whose officers/ownership could not be derived, are routed to `In Review`; clean active companies are `Approved`. `registry_status` is the company's own status at the registry (e.g. `active`, `dissolved`).\n\n**Sandbox.** Keys from sandbox applications return a static resolved company without contacting any registry and without billing.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "kyb_response_id"
                ],
                "properties": {
                  "kyb_response_id": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Candidate handle from `POST /v3/kyb/search/` (`kyb_registry.companies[].kyb_response_id`). Ephemeral — use it shortly after the search that produced it.",
                    "example": "69aeeb95febb0f1704042259"
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Your identifier for this company; echoed back and stored with the business session."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Free-form JSON stored with the business session and echoed back."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "Must be `true` (the default). KYB registry selects are always persisted so billing and retrieval can be tracked; `false` returns `400`."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/kyb/select/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"kyb_response_id\": \"69aeeb95febb0f1704042259\",\n    \"vendor_data\": \"company-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nsearch = requests.post(\n    \"https://verification.didit.me/v3/kyb/search/\",\n    headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n    json={\"country_code\": \"GB\", \"name\": \"Tesco\"},\n    timeout=60,\n).json()\nkyb_response_id = search[\"kyb_registry\"][\"companies\"][0][\"kyb_response_id\"]\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/kyb/select/\",\n    headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n    json={\"kyb_response_id\": kyb_response_id, \"vendor_data\": \"company-1234\"},\n    timeout=90,  # waits for the registry to resolve the full profile\n)\nresp.raise_for_status()\ncompany = resp.json()[\"kyb_registry\"]\nprint(company[\"company_name\"], company[\"registry_status\"], company[\"status\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/kyb/select/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    kyb_response_id: '69aeeb95febb0f1704042259',\n    vendor_data: 'company-1234',\n  }),\n});\nconst data = await res.json();\nconsole.log(data.kyb_registry.company_name, data.kyb_registry.registry_status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Company retrieved and billed. `kyb_registry` carries the full profile; check `data_resolved` — when `false` the registry was still processing and the stored record completes automatically once it resolves.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Id of the business session created for this retrieval. The stored record (visible in the console) keeps updating if the registry resolves after the response."
                    },
                    "kyb_registry": {
                      "type": "object",
                      "description": "Full registry company profile.",
                      "properties": {
                        "uuid": {
                          "type": "string",
                          "format": "uuid",
                          "description": "Didit's id for this company record."
                        },
                        "node_id": {
                          "type": "string",
                          "example": "feature_kyb_registry"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "Not Finished",
                            "Approved",
                            "Declined",
                            "In Review"
                          ],
                          "description": "Didit's assessment of the registry check (e.g. `In Review` when the company is not active in the registry or ownership data could not be derived)."
                        },
                        "registry_status": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "active",
                            "dissolved",
                            "deregistered",
                            "see full details",
                            "authorised",
                            "appointed representative",
                            "unauthorised",
                            "inactive",
                            "no longer authorised",
                            "closed",
                            "struck off"
                          ],
                          "description": "Normalized company status as reported by the registry."
                        },
                        "data_resolved": {
                          "type": "boolean",
                          "description": "`true` once the registry returned the full profile. `false` when the registry was still processing after polling — the stored record completes automatically once the registry resolves."
                        },
                        "company_name": {
                          "type": "string",
                          "example": "Tesco PLC"
                        },
                        "registration_number": {
                          "type": "string",
                          "nullable": true,
                          "example": "00445790"
                        },
                        "country_code": {
                          "type": "string",
                          "description": "ISO 3166-1 alpha-2.",
                          "example": "GB"
                        },
                        "region": {
                          "type": "string",
                          "nullable": true,
                          "description": "Subdivision code when the registry is regional (e.g. `CA` for `US-CA`); empty otherwise."
                        },
                        "company_type": {
                          "type": "string",
                          "nullable": true,
                          "example": "Public Limited Company"
                        },
                        "incorporation_date": {
                          "type": "string",
                          "format": "date",
                          "nullable": true,
                          "example": "1947-11-27"
                        },
                        "registered_address": {
                          "type": "string",
                          "nullable": true,
                          "example": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA"
                        },
                        "tax_number": {
                          "type": "string",
                          "nullable": true,
                          "example": "GB412 8925 93"
                        },
                        "risk_level": {
                          "type": "string",
                          "nullable": true,
                          "description": "Registry-reported risk level, when available."
                        },
                        "verification_status": {
                          "type": "string",
                          "nullable": true,
                          "description": "Registry verification status, when available."
                        },
                        "is_from_registry": {
                          "type": "boolean",
                          "description": "Always `true` for this endpoint — the profile was retrieved from an official registry."
                        },
                        "fetch_status": {
                          "type": "string",
                          "enum": [
                            "pending",
                            "resolved"
                          ],
                          "description": "Registry fetch lifecycle. Mirrored by `data_resolved`."
                        },
                        "alternative_names": {
                          "type": "string",
                          "nullable": true,
                          "description": "Previous/alternative registered names, when reported."
                        },
                        "nature_of_business": {
                          "type": "string",
                          "nullable": true,
                          "description": "Activity description or classification code, when reported."
                        },
                        "registered_capital": {
                          "type": "string",
                          "nullable": true
                        },
                        "registered_capital_amount": {
                          "type": "string",
                          "nullable": true,
                          "description": "Decimal amount as a string, when reported."
                        },
                        "registered_capital_currency": {
                          "type": "string",
                          "nullable": true,
                          "description": "ISO 4217 code, when reported."
                        },
                        "website": {
                          "type": "string",
                          "nullable": true,
                          "example": "https://www.tesco.com/"
                        },
                        "email": {
                          "type": "string",
                          "nullable": true,
                          "example": "customer.service@tesco.co.uk"
                        },
                        "phone": {
                          "type": "string",
                          "nullable": true,
                          "example": "03301231688"
                        },
                        "legal_entity_identifier": {
                          "type": "string",
                          "nullable": true,
                          "description": "LEI, when reported."
                        },
                        "location_of_registration": {
                          "type": "string",
                          "nullable": true,
                          "description": "Registry/jurisdiction the company is registered with, when reported."
                        },
                        "vat_number": {
                          "type": "string",
                          "nullable": true,
                          "description": "EU VAT number, when collected during the hosted registry step or edited afterwards. At select time this is typically not yet set — VIES validation runs when the end user submits the registry step in the hosted flow."
                        },
                        "vat_validation_status": {
                          "type": "string",
                          "enum": [
                            "valid",
                            "invalid",
                            "could_not_validate",
                            "not_applicable"
                          ],
                          "description": "Result of the EU VIES check run at registry submit. `not_applicable` when no VAT number was provided or the company is outside the EU VAT area (EU-27 plus Northern Ireland)."
                        },
                        "vat_validated_name": {
                          "type": "string",
                          "nullable": true,
                          "description": "Trader name registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                        },
                        "vat_validated_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Trader address registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                        },
                        "vat_checked_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Timestamp of the VIES check. `null` when no check ran."
                        },
                        "financial_summary": {
                          "type": "object",
                          "nullable": true,
                          "description": "Registry-reported share/capital details (currency, total shares, share classes), when available."
                        },
                        "officers": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "uuid": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "name": {
                                "type": "string",
                                "example": "MELISSA BETHELL"
                              },
                              "designation": {
                                "type": "string",
                                "nullable": true,
                                "description": "Raw registry designation (e.g. `Director`, `Secretary`)."
                              },
                              "role": {
                                "type": "string",
                                "description": "Normalized role derived from the designation. Full 17-value model set; registry parsing on this endpoint typically emits director, non_executive_director, secretary, chairman, or company_officer.",
                                "enum": [
                                  "director",
                                  "non_executive_director",
                                  "secretary",
                                  "chairman",
                                  "ubo",
                                  "shareholder",
                                  "representative",
                                  "founder",
                                  "legal_advisor",
                                  "authorized_signatory",
                                  "trustee",
                                  "beneficiary",
                                  "company_officer",
                                  "settlor",
                                  "protector",
                                  "investor",
                                  "other"
                                ]
                              },
                              "nationality": {
                                "type": "string",
                                "nullable": true
                              },
                              "is_active": {
                                "type": "boolean",
                                "description": "`false` when the registry marks the appointment as resigned."
                              },
                              "kyc_status": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "Approved",
                                  "Declined",
                                  "Pending"
                                ],
                                "description": "Status of an associated KYC session, when one was started for this officer. `null` otherwise."
                              },
                              "kyc_session_url": {
                                "type": "string",
                                "nullable": true,
                                "description": "Verification URL of the associated KYC session, when one exists."
                              }
                            }
                          },
                          "description": "Directors, secretaries, and other officers extracted from the registry."
                        },
                        "beneficial_owners": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "uuid": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "name": {
                                "type": "string"
                              },
                              "first_name": {
                                "type": "string",
                                "nullable": true
                              },
                              "last_name": {
                                "type": "string",
                                "nullable": true
                              },
                              "entity_type": {
                                "type": "string",
                                "enum": [
                                  "person",
                                  "company"
                                ],
                                "description": "Whether the owner is a natural person or a corporate entity."
                              },
                              "roles": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "Normalized roles (e.g. `ubo`, `shareholder`)."
                              },
                              "ownership_min_shares": {
                                "type": "string",
                                "nullable": true,
                                "description": "Lower bound of the ownership band reported by the registry (e.g. `25%`)."
                              },
                              "ownership_max_shares": {
                                "type": "string",
                                "nullable": true,
                                "description": "Upper bound of the ownership band reported by the registry."
                              },
                              "is_active": {
                                "type": "boolean"
                              },
                              "kyc_status": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "Approved",
                                  "Declined",
                                  "Pending"
                                ]
                              },
                              "kyc_session_url": {
                                "type": "string",
                                "nullable": true
                              },
                              "effective_ownership_percent": {
                                "type": "number",
                                "nullable": true,
                                "description": "Parsed ownership percentage (max bound, falling back to min bound)."
                              }
                            }
                          },
                          "description": "Beneficial owners / persons with significant control extracted from the registry. Can be empty when the registry does not expose ownership."
                        },
                        "addresses": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "object",
                            "properties": {
                              "address": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string"
                              },
                              "description": {
                                "type": "string"
                              }
                            }
                          },
                          "description": "All addresses reported by the registry."
                        },
                        "industries": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "object"
                          },
                          "description": "Industry classifications, when reported."
                        },
                        "accounts": {
                          "type": "object",
                          "nullable": true,
                          "description": "Filing/accounts dates reported by the registry (e.g. `last_account`, `next_account`, `due_by_date`)."
                        },
                        "registry_data": {
                          "type": "object",
                          "nullable": true,
                          "description": "The complete raw registry payload (people, filings, announcements, identifiers, …) for audit and custom parsing."
                        },
                        "user_provided_data": {
                          "type": "object",
                          "nullable": true,
                          "description": "Always `null` on this endpoint (used by the hosted KYB flow when end users edit registry data)."
                        },
                        "confirmed_by_user_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Always `null` on this endpoint."
                        },
                        "last_console_edit_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true,
                          "description": "Set when a console reviewer edits the record later."
                        },
                        "is_editable": {
                          "type": "boolean",
                          "description": "Whether the registry-sourced data can still be edited in the hosted flow/console."
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "description": "Echo of the `metadata` you sent."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "examples": {
                  "Resolved company": {
                    "summary": "Full registry profile (officer list truncated for brevity)",
                    "value": {
                      "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e",
                      "kyb_registry": {
                        "uuid": "f1e2d3c4-b5a6-4789-9c0d-1e2f3a4b5c6d",
                        "node_id": "feature_kyb_registry",
                        "status": "In Review",
                        "registry_status": "active",
                        "data_resolved": true,
                        "company_name": "Tesco PLC",
                        "registration_number": "00445790",
                        "country_code": "GB",
                        "region": "",
                        "company_type": "Public Limited Company",
                        "incorporation_date": "1947-11-27",
                        "registered_address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA",
                        "tax_number": "GB412 8925 93",
                        "risk_level": "low risk",
                        "verification_status": "verified",
                        "is_from_registry": true,
                        "fetch_status": "resolved",
                        "alternative_names": "TESCO STORES (HOLDINGS) PUBLIC LIMITED COMPANY",
                        "nature_of_business": null,
                        "registered_capital": null,
                        "registered_capital_amount": null,
                        "registered_capital_currency": null,
                        "website": "https://www.tesco.com/",
                        "email": "customer.service@tesco.co.uk",
                        "phone": "03301231688",
                        "legal_entity_identifier": null,
                        "location_of_registration": null,
                        "financial_summary": {
                          "currency": "Pound (£)",
                          "total_number_of_shares": "8174378721",
                          "meta_detail": {
                            "Class_Of_Share": "Ordinary"
                          }
                        },
                        "officers": [
                          {
                            "uuid": "a1b2c3d4-e5f6-4789-9a0b-1c2d3e4f5a6b",
                            "name": "CHRISTOPHER JON TAYLOR",
                            "designation": "Secretary",
                            "role": "secretary",
                            "nationality": null,
                            "is_active": true,
                            "kyc_status": null,
                            "kyc_session_url": null
                          },
                          {
                            "uuid": "b2c3d4e5-f6a7-4890-8b1c-2d3e4f5a6b7c",
                            "name": "MELISSA BETHELL",
                            "designation": "Director",
                            "role": "director",
                            "nationality": "British",
                            "is_active": true,
                            "kyc_status": null,
                            "kyc_session_url": null
                          }
                        ],
                        "beneficial_owners": [],
                        "addresses": [
                          {
                            "address": "Tesco House, Shire Park, Kestrel Way, Welwyn Garden City, United Kingdom, AL7 1GA",
                            "type": "legal_entity_registry_address",
                            "description": "registered office address"
                          }
                        ],
                        "industries": null,
                        "accounts": {
                          "last_account": "26 February 2025",
                          "next_account": "26 February 2026",
                          "due_by_date": "26 August 2026"
                        },
                        "registry_data": {
                          "name": "Tesco PLC",
                          "registration_number": "00445790",
                          "status": "active",
                          "type": "Public Limited Company",
                          "incorporation_date": "1947-11-27",
                          "tax_number": "GB412 8925 93",
                          "country_code": "GB",
                          "fetch_status": "resolved",
                          "industries_detail": [
                            {
                              "code": "47110",
                              "name": "Retail Sale In Non-specialised Stores With Food, Beverages Or Tobacco Predominating",
                              "description": null,
                              "meta_detail": []
                            }
                          ]
                        },
                        "user_provided_data": null,
                        "confirmed_by_user_at": null,
                        "last_console_edit_at": null,
                        "is_editable": true
                      },
                      "vendor_data": "company-1234",
                      "metadata": null,
                      "created_at": "2026-06-11T10:45:00.000000+00:00"
                    }
                  },
                  "Pending (registry still resolving)": {
                    "summary": "Select stayed pending after polling — sparse fields, auto-completes server-side",
                    "value": {
                      "request_id": "9d4f2c7a-1b3e-4a5c-8d6f-0e9a8b7c6d5e",
                      "kyb_registry": {
                        "uuid": "7c0a4f7e-2b1d-4f6a-9c3e-8d5b2a1f0e9d",
                        "node_id": "feature_kyb_registry",
                        "status": "Not Finished",
                        "registry_status": null,
                        "data_resolved": false,
                        "company_name": "Tesco PLC",
                        "registration_number": "00445790",
                        "country_code": "GB",
                        "region": null,
                        "company_type": null,
                        "incorporation_date": null,
                        "registered_address": null,
                        "tax_number": null,
                        "risk_level": null,
                        "verification_status": null,
                        "is_from_registry": true,
                        "fetch_status": "pending",
                        "alternative_names": null,
                        "nature_of_business": null,
                        "registered_capital": null,
                        "registered_capital_amount": null,
                        "registered_capital_currency": null,
                        "website": null,
                        "email": null,
                        "phone": null,
                        "legal_entity_identifier": null,
                        "location_of_registration": null,
                        "financial_summary": {},
                        "officers": [],
                        "beneficial_owners": [],
                        "addresses": null,
                        "industries": null,
                        "accounts": null,
                        "registry_data": {},
                        "user_provided_data": null,
                        "confirmed_by_user_at": null,
                        "last_console_edit_at": null,
                        "is_editable": true
                      },
                      "vendor_data": "company-1234",
                      "metadata": null,
                      "created_at": "2026-06-11T10:45:00.000000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field-level problems use DRF's standard envelope.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing kyb_response_id": {
                    "summary": "`kyb_response_id` is required",
                    "value": {
                      "kyb_response_id": [
                        "This field is required."
                      ]
                    }
                  },
                  "save_api_request=false rejected": {
                    "summary": "Selects are always persisted",
                    "value": {
                      "save_api_request": [
                        "KYB registry select must be saved so billing and retrieval can be tracked."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/passive-liveness/": {
      "post": {
        "summary": "Passive Liveness",
        "description": "Detect presentation attacks — printed photos, screen replays, masks, deepfakes — from a single face image. No video, motion, or user interaction required.\n\n**How it works.** The image is analyzed by a biometric model; the largest detected face is evaluated and a liveness `score` (0–100) is computed. `status` is `Declined` when any `error`-severity warning fires:\n- `NO_FACE_DETECTED` — the liveness model found no face,\n- `LOW_LIVENESS_SCORE` — score at or below `face_liveness_score_decline_threshold` (default `30`),\n- `LIVENESS_FACE_ATTACK` — the model detected a presentation attack,\n- `FACE_IN_BLOCKLIST` / `POSSIBLE_FACE_IN_BLOCKLIST` — the face matches an entry on your face blocklist.\n\n`MULTIPLE_FACES_DETECTED` is a `warning`-severity signal (does not decline), and `DUPLICATED_FACE` / `POSSIBLE_DUPLICATED_FACE` are `information`-severity signals that the same face already appeared in another approved session (they never decline). `face_quality` and `face_luminance` (0–100, nullable) describe capture quality.\n\n**Blocklist and duplicate screening.** Runs on **every** call — with or without `save_api_request` — by matching the detected face against your application's face search index. Prior faces with the same `vendor_data` are excluded from the entire index search — so re-running liveness for the same user does not trigger `DUPLICATED_FACE`, and a blocklisted face originating from a session with the caller's own `vendor_data` is also NOT flagged (`FACE_IN_BLOCKLIST` screening is bypassed for same-`vendor_data` faces).\n\n**Billing.** Each `200` response consumes one Passive Liveness API credit (standalone APIs have no free tier). Insufficient balance returns `403` before any image processing.\n\n**Session persistence and face enrollment (`save_api_request`, default `true`).** When `true`, the call is persisted as an API-type session (Business Console, `GET /v3/session/{sessionId}/decision/` via the returned `request_id`, `status.updated` webhook), the reference image and face crop are stored, and the detected face is **enrolled into your face search index** so later Face Search calls and duplicate checks can match it. When `false`, the call is one-shot: nothing is stored and the face is not enrolled (blocklist/duplicate screening still runs).\n\n**Sandbox.** Sandbox API keys skip all processing and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Approved` mock payload, no session is persisted, and no credits are consumed.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3passive-liveness",
        "tags": [
          "Standalone APIs"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "user_image"
                ],
                "properties": {
                  "user_image": {
                    "type": "string",
                    "format": "binary",
                    "description": "Front-facing face image (e.g. a selfie). Allowed extensions: `tiff`, `jpg`, `jpeg`, `png`, `webp`. Maximum upload size: **5 MB** (larger files are rejected with `400`). Images are automatically compressed to ~0.5 MB before processing, so very high resolutions do not improve accuracy. The image should contain a single subject — when several faces are present, the largest one is evaluated and a `MULTIPLE_FACES_DETECTED` warning is added."
                  },
                  "face_liveness_score_decline_threshold": {
                    "type": "number",
                    "format": "float",
                    "default": 30,
                    "minimum": 0,
                    "maximum": 100,
                    "description": "Liveness score threshold (0–100). A computed score **at or below** this value emits a `LOW_LIVENESS_SCORE` warning and sets `status` to `Declined`. Default `30`; raise it for a stricter anti-spoofing posture. Values outside 0–100 return `400`.",
                    "example": 30
                  },
                  "rotate_image": {
                    "type": "boolean",
                    "default": false,
                    "description": "When `true`, the service tries 90-degree rotations of the input and uses the orientation that yields the best face detection. Useful when EXIF orientation is missing. Adds latency.",
                    "example": false
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `true` (default), persists the call as an API-type session, stores the face images, emits a `status.updated` webhook, and enrolls the detected face into your face search index. When `false`, the call is one-shot — nothing is stored and the face is not enrolled. Blocklist and duplicate screening run either way.",
                    "example": true
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional opaque identifier (your user id, email, UUID…) stored on the persisted session and echoed back. Also used to exclude prior sessions with the same `vendor_data` from the duplicate-face check, so re-verifying the same user does not raise `DUPLICATED_FACE`.",
                    "example": "user-123"
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional JSON object stored with the session (when `save_api_request=true`) and echoed back. In multipart requests, send it as a JSON-encoded string field — it is parsed into an object.",
                    "example": {
                      "flow": "withdrawal"
                    }
                  }
                }
              },
              "example": {
                "user_image": "(binary JPEG/PNG selfie)",
                "face_liveness_score_decline_threshold": 30,
                "rotate_image": false,
                "save_api_request": true,
                "vendor_data": "user-123",
                "metadata": {
                  "flow": "withdrawal"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Passive liveness completed. `liveness.score` is the model's 0–100 confidence that the image shows a live person; `status` is `Approved` unless an `error`-severity warning fired. A spoofed or blocklisted face still returns `200` — inspect `liveness.status` and `liveness.warnings`, not just the HTTP code. When `save_api_request=true`, `request_id` is the persisted session id.",
            "content": {
              "application/json": {
                "examples": {
                  "Approved": {
                    "summary": "Live face — no warnings",
                    "value": {
                      "request_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
                      "liveness": {
                        "status": "Approved",
                        "method": "PASSIVE",
                        "score": 95,
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                661,
                                728,
                                1688,
                                2188
                              ],
                              "confidence": 0.732973,
                              "age": 26.91,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [],
                        "face_quality": 84.21,
                        "face_luminance": 50.33
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  },
                  "Declined - presentation attack": {
                    "summary": "The model flagged a spoof (print, replay, mask…)",
                    "value": {
                      "request_id": "f1c8c9b2-d3e4-4f5a-9b8c-7d6e5f4a3b2c",
                      "liveness": {
                        "status": "Declined",
                        "method": "PASSIVE",
                        "score": 12.5,
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                100,
                                120,
                                420,
                                540
                              ],
                              "confidence": 0.81,
                              "age": 30.1,
                              "gender": "female",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [
                          {
                            "risk": "LOW_LIVENESS_SCORE",
                            "feature": "LIVENESS",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Low liveness score",
                            "long_description": "The liveness check resulted in a low score, indicating potential use of non-live facial representations or poor-quality biometric data."
                          },
                          {
                            "risk": "LIVENESS_FACE_ATTACK",
                            "feature": "LIVENESS",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Liveness Face Attack",
                            "long_description": "The system detected a potential attempt to bypass the liveness check."
                          }
                        ],
                        "face_quality": 72.4,
                        "face_luminance": 48.2
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  },
                  "Declined - face on blocklist": {
                    "summary": "Live face, but it matches a blocklist entry",
                    "value": {
                      "request_id": "0b54a1de-22aa-4e0f-9c84-5a2b1f3c4d5e",
                      "liveness": {
                        "status": "Declined",
                        "method": "PASSIVE",
                        "score": 93.75,
                        "user_image": {
                          "entities": [
                            {
                              "bbox": [
                                40,
                                40,
                                100,
                                100
                              ],
                              "confidence": 0.722082,
                              "age": 27,
                              "gender": "male",
                              "race": null
                            }
                          ],
                          "best_angle": 0
                        },
                        "warnings": [
                          {
                            "risk": "FACE_IN_BLOCKLIST",
                            "feature": "LIVENESS",
                            "additional_data": {
                              "blocklisted_session_id": "882c42d5-8a4d-4d20-8080-a22f57822c86",
                              "blocklisted_session_number": 3242,
                              "api_service": null
                            },
                            "log_type": "error",
                            "short_description": "Face in blocklist",
                            "long_description": "The system identified a face in the blocklist, which means the face is not allowed to be verified."
                          }
                        ],
                        "face_quality": null,
                        "face_luminance": null
                      },
                      "vendor_data": "user-123",
                      "metadata": null,
                      "created_at": "2026-06-12T01:04:42.763237+00:00"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "liveness": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Declined` when at least one `error`-severity warning fired (no face, low score, presentation attack, or a blocklist hit); `Approved` otherwise. `warning`/`information` entries (multiple faces, duplicates) never decline on their own."
                        },
                        "method": {
                          "type": "string",
                          "enum": [
                            "PASSIVE"
                          ],
                          "description": "Always `PASSIVE` for this endpoint."
                        },
                        "score": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Liveness confidence (0–100, two decimals) that the image depicts a live person rather than a spoof. `null` when the model returned no confidence.",
                          "example": 95
                        },
                        "user_image": {
                          "type": "object",
                          "description": "Face-detection results for the submitted image.",
                          "properties": {
                            "entities": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "bbox": {
                                    "type": "array",
                                    "items": {
                                      "type": "integer"
                                    },
                                    "minItems": 4,
                                    "maxItems": 4,
                                    "description": "Bounding box of the detected face as `[x_min, y_min, x_max, y_max]` pixel coordinates in the processed image.",
                                    "example": [
                                      661,
                                      728,
                                      1688,
                                      2188
                                    ]
                                  },
                                  "confidence": {
                                    "type": "number",
                                    "format": "float",
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Face-detection confidence (0–1).",
                                    "example": 0.732973
                                  },
                                  "age": {
                                    "type": "number",
                                    "format": "float",
                                    "description": "Model-estimated age of the detected face, in years. Informational only — it does not affect the liveness decision.",
                                    "example": 26.91
                                  },
                                  "gender": {
                                    "type": "string",
                                    "description": "Model-predicted gender of the detected face (`male` or `female`). Informational only.",
                                    "example": "male"
                                  },
                                  "race": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Reserved field — always `null` in responses from this endpoint.",
                                    "example": null
                                  }
                                }
                              },
                              "description": "One entry per detected face. The largest face is the one evaluated."
                            },
                            "best_angle": {
                              "type": "integer",
                              "nullable": true,
                              "description": "Rotation (degrees) that produced the best face detection; non-zero only when `rotate_image=true` corrected the orientation.",
                              "example": 0
                            }
                          }
                        },
                        "warnings": {
                          "type": "array",
                          "description": "Risk signals. `error` severity (declines): `NO_FACE_DETECTED`, `LOW_LIVENESS_SCORE` (score at or below the threshold), `LIVENESS_FACE_ATTACK`, `FACE_IN_BLOCKLIST`, `POSSIBLE_FACE_IN_BLOCKLIST`. `warning` severity (review, no decline): `MULTIPLE_FACES_DETECTED`. `information` severity (no decline): `DUPLICATED_FACE`, `POSSIBLE_DUPLICATED_FACE` — the same face already appeared in another approved session.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "risk": {
                                "type": "string",
                                "enum": [
                                  "NO_FACE_DETECTED",
                                  "LOW_LIVENESS_SCORE",
                                  "LIVENESS_FACE_ATTACK",
                                  "MULTIPLE_FACES_DETECTED",
                                  "FACE_IN_BLOCKLIST",
                                  "POSSIBLE_FACE_IN_BLOCKLIST",
                                  "DUPLICATED_FACE",
                                  "POSSIBLE_DUPLICATED_FACE"
                                ],
                                "description": "Machine-readable risk code."
                              },
                              "feature": {
                                "type": "string",
                                "enum": [
                                  "LIVENESS"
                                ],
                                "description": "Feature that raised the warning. Always `LIVENESS` on this endpoint."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": true,
                                "description": "`null` for most warnings. Blocklist hits carry `{blocklisted_session_id, blocklisted_session_number, api_service}`; duplicate hits carry `{duplicated_session_id, duplicated_session_number, api_service}` pointing at the matching session."
                              },
                              "log_type": {
                                "type": "string",
                                "enum": [
                                  "error",
                                  "warning",
                                  "information"
                                ],
                                "description": "Severity. `error` warnings drive `status` to `Declined`; `warning` and `information` entries are advisory and never decline on their own."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "Human-readable one-line summary of the risk."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Human-readable explanation of the risk."
                              }
                            }
                          }
                        },
                        "face_quality": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Capture-quality score of the detected face, normalized to 0–100. `null` when the model could not produce a reliable value.",
                          "example": 84.21
                        },
                        "face_luminance": {
                          "type": "number",
                          "format": "float",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 100,
                          "description": "Brightness of the detected face, normalized to 0–100. `null` when unavailable.",
                          "example": 50.33
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent, or `null`."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Echo of the `metadata` object you sent, or `null`."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO 8601 timestamp (UTC) of when the response was generated, e.g. `2026-06-12T01:04:42.763237+00:00`."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Returned when `user_image` is missing, exceeds 5 MB, has an unsupported extension, fails server-side image decoding, or when an option is out of range. Field errors use DRF's `{field: [messages]}` envelope; image-decoding failures use `{\"error\": ...}`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing image": {
                    "summary": "`user_image` not included in the form data",
                    "value": {
                      "user_image": [
                        "No file was submitted."
                      ]
                    }
                  },
                  "Invalid image format": {
                    "summary": "Allowed extension but undecodable image content",
                    "value": {
                      "error": "Invalid user image format."
                    }
                  },
                  "Unsupported file extension": {
                    "summary": "File extension outside tiff/jpg/jpeg/png/webp",
                    "value": {
                      "user_image": [
                        "File extension “txt” is not allowed. Allowed extensions are: tiff, jpg, jpeg, png, webp."
                      ]
                    }
                  },
                  "File too large": {
                    "summary": "Upload exceeds the 5 MB limit",
                    "value": {
                      "user_image": [
                        "File size should not exceed 5 MB"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any image processing happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/passive-liveness/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'user_image=@./selfie.jpg' \\\n  -F 'face_liveness_score_decline_threshold=30' \\\n  -F 'save_api_request=true' \\\n  -F 'vendor_data=user-123'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nurl = 'https://verification.didit.me/v3/passive-liveness/'\nheaders = {'x-api-key': 'YOUR_API_KEY'}\n\nwith open('selfie.jpg', 'rb') as f:\n    files = {'user_image': ('selfie.jpg', f, 'image/jpeg')}\n    data = {\n        'face_liveness_score_decline_threshold': 30,\n        'save_api_request': 'true',\n        'vendor_data': 'user-123',\n    }\n    resp = requests.post(url, headers=headers, files=files, data=data, timeout=60)\n\nresp.raise_for_status()\nbody = resp.json()\nprint('status:', body['liveness']['status'])\nprint('score:', body['liveness']['score'])\nfor w in body['liveness']['warnings']:\n    print('warning:', w['risk'], '-', w['log_type'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "import fs from 'node:fs';\n\nconst form = new FormData();\nform.append('user_image', new Blob([fs.readFileSync('./selfie.jpg')]), 'selfie.jpg');\nform.append('face_liveness_score_decline_threshold', '30');\nform.append('save_api_request', 'true');\nform.append('vendor_data', 'user-123');\n\nconst response = await fetch('https://verification.didit.me/v3/passive-liveness/', {\n  method: 'POST',\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n  body: form,\n});\n\nif (!response.ok) throw new Error(`Passive liveness failed: ${response.status}`);\nconst body = await response.json();\nconsole.log('status:', body.liveness.status, 'score:', body.liveness.score);\nbody.liveness.warnings.forEach(w => console.log('warning:', w.risk, '-', w.log_type));"
          }
        ]
      }
    },
    "/v3/database-validation/": {
      "post": {
        "summary": "Database Validation (standalone)",
        "operationId": "post_v3database-validation",
        "tags": [
          "Standalone APIs"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [],
        "description": "Validate a person's identity data against official government and registry sources — CPF in Brazil, RENAPER in Argentina, DNI registries in Spain and Peru, INE in Mexico, and 100+ more services across 60+ countries.\n\n**Service selection.** Each country exposes one or more catalog services identified by a `service_id` (e.g. `bra_cpf`, `arg_renaper`, `pan_cedula_sib`). Pin the services you want with the `services` array — if you omit it, exactly **one** default service runs (the longest-established live service for the country). Sending `services` also switches the response to the extended shape with `services_used` and `match_score`. Discover services, their required fields, and prices via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor. Some services are flagged `requires_consent=true` in the catalog and demand `consent=true`; others require onboarding for your organization before they can be called.\n\n**Input fields.** `identification_number` is a universal field that maps to the right country-specific field automatically (see its description). Country format rules are enforced before any provider is called (e.g. BRA CPF must be exactly 11 digits) and format failures are never billed. Biometric services need a `selfie` upload (multipart only); Argentina RENAPER additionally requires `gender`.\n\n**Results.** Each service returns a field-by-field `validation` map plus a vendor-neutral `outcome_code` (`MATCH`, `PARTIAL_MATCH`, `NO_MATCH`, `DOCUMENT_NOT_FOUND`, `INVALID_DOCUMENT_FORMAT`, `INVALID_INPUT`, `MINOR_BLOCKED`, `DECEASED`, `BIOMETRIC_NO_MATCH`, `BIOMETRIC_IMAGE_UNUSABLE`, `INCONCLUSIVE`, `REGISTRY_UNAVAILABLE`, `REGISTRY_ERROR`). Note the distinctions: `NO_MATCH` is a definitive mismatch, `INCONCLUSIVE` means the registry could not confirm either way (review, not decline), and `BIOMETRIC_IMAGE_UNUSABLE` is a technical selfie problem (retake), unlike the definitive `BIOMETRIC_NO_MATCH`. The overall `match_type` aggregates across services (any full → `full_match`, else any partial → `partial_match`, else `no_match`) and `no_match_action`/`partial_match_action` translate it into the final `status`. `validation_type` is derived: `two_by_two` when two or more distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions), else `one_by_one`.\n\n**Persistence.** `save_api_request` defaults to **true**: the result is stored as a session (`request_id` works with `GET /v3/session/{sessionId}/decision/`), appears in the console, and fires a `status.updated` webhook. It also controls the `validations` shape — per-service objects when saved, a merged `{field: match}` map when not.\n\n**Billing.** Per-service: each service that returns a billable result is charged its own catalog price. Failed or skipped services are not billed, and requests rejected with `400` cost nothing. The pre-flight balance check covers the sum of the selected services' prices (`403` when short).\n\n**Failures.** When *no* selected service returns a usable result the API responds `502` with a `validation_errors` array; nothing is billed (a session is still recorded with status `Not Finished` when `save_api_request=true`). When only some services fail, the `200` response carries their failures in `database_validation.errors`.\n\n**Sandbox.** Keys from sandbox applications skip the registry calls and billing and return a static `full_match` response — the sandbox payload always includes `services_used` and `match_score: 100` regardless of how `services` was sent, so don't validate the live extended-shape gating or the 0.0–1.0 `match_score` fraction against sandbox responses.\n\nSend the request as `application/json`, or as `multipart/form-data` when uploading a `selfie`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "issuing_state"
                ],
                "properties": {
                  "issuing_state": {
                    "type": "string",
                    "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.",
                    "example": "BRA"
                  },
                  "services": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.",
                    "example": [
                      "bra_cpf"
                    ]
                  },
                  "validation_type": {
                    "type": "string",
                    "deprecated": true,
                    "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services."
                  },
                  "consent": {
                    "type": "boolean",
                    "default": false,
                    "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`."
                  },
                  "identification_number": {
                    "type": "string",
                    "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field."
                  },
                  "first_name": {
                    "type": "string",
                    "description": "The individual's first name. Required by some services/countries.",
                    "example": "John"
                  },
                  "last_name": {
                    "type": "string",
                    "description": "The individual's last name. Required by some services/countries.",
                    "example": "Doe"
                  },
                  "middle_name": {
                    "type": "string",
                    "description": "Middle name, used by some country services (AUS, NZL, …)."
                  },
                  "full_name": {
                    "type": "string",
                    "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)."
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "description": "Date of birth, `YYYY-MM-DD`. Required by many services.",
                    "example": "1980-01-01"
                  },
                  "personal_number": {
                    "type": "string",
                    "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead."
                  },
                  "tax_number": {
                    "type": "string",
                    "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead."
                  },
                  "document_number": {
                    "type": "string",
                    "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead."
                  },
                  "document_type": {
                    "type": "string",
                    "enum": [
                      "P",
                      "DL",
                      "ID",
                      "RP",
                      "SSC",
                      "HIC",
                      "WP",
                      "TC",
                      "VISA",
                      "PSC",
                      "BC",
                      "OTHER"
                    ],
                    "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)."
                  },
                  "expiration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.",
                    "example": "2030-01-15"
                  },
                  "date_of_issue": {
                    "type": "string",
                    "format": "date",
                    "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.",
                    "example": "2015-06-20"
                  },
                  "nationality": {
                    "type": "string",
                    "description": "Nationality as ISO 3166-1 alpha-3. Required by some services."
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "M",
                      "F",
                      "X"
                    ],
                    "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)."
                  },
                  "address": {
                    "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.",
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "street_1": {
                            "type": "string"
                          },
                          "street_2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "region": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      },
                      {
                        "type": "string",
                        "description": "Complete single-line address (legacy)."
                      }
                    ]
                  },
                  "address_element_1": {
                    "type": "string",
                    "description": "Street address including street number and type. If omitted, derived from `address`."
                  },
                  "address_element_2": {
                    "type": "string",
                    "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly."
                  },
                  "address_element_3": {
                    "type": "string",
                    "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`."
                  },
                  "address_element_4": {
                    "type": "string",
                    "description": "State, province, region, or town. If omitted, derived from `address`."
                  },
                  "address_element_5": {
                    "type": "string",
                    "description": "Postcode or postal code. `postal_code` is accepted as an alias."
                  },
                  "postal_code": {
                    "type": "string",
                    "description": "Postal code for address-based services (alias of `address_element_5`)."
                  },
                  "driver_license_number": {
                    "type": "string",
                    "description": "Driver licence number for government document-verification services (AUS, NZL, IND)."
                  },
                  "driver_license_card_number": {
                    "type": "string",
                    "description": "Physical driver licence card number, where required separately from the licence number (AUS)."
                  },
                  "driver_license_state": {
                    "type": "string",
                    "description": "Driver licence state or territory of issue (AUS)."
                  },
                  "driver_license_version": {
                    "type": "string",
                    "description": "Driver licence version code (NZL)."
                  },
                  "passport_number": {
                    "type": "string",
                    "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)."
                  },
                  "passport_expiration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services."
                  },
                  "passport_file_number": {
                    "type": "string",
                    "description": "Passport file number (IND)."
                  },
                  "passport_issue_country": {
                    "type": "string",
                    "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services."
                  },
                  "medicare_card_number": {
                    "type": "string",
                    "description": "Medicare card number (AUS)."
                  },
                  "immi_card_number": {
                    "type": "string",
                    "description": "Australian ImmiCard number."
                  },
                  "immi_card_expiry_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Australian ImmiCard expiry date."
                  },
                  "citizenship_certificate_number": {
                    "type": "string",
                    "description": "Citizenship certificate number (AUS)."
                  },
                  "birth_registration_number": {
                    "type": "string",
                    "description": "Birth-certificate registration number (AUS)."
                  },
                  "birth_registration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Birth-certificate registration date (AUS)."
                  },
                  "birth_registration_state": {
                    "type": "string",
                    "description": "Birth-certificate registration state (AUS)."
                  },
                  "marriage_certificate_number": {
                    "type": "string",
                    "description": "Marriage certificate number (AUS)."
                  },
                  "change_of_name_certificate_number": {
                    "type": "string",
                    "description": "Change-of-name certificate number (AUS)."
                  },
                  "first_partner_name": {
                    "type": "string",
                    "description": "First name of the partner on a marriage certificate (AUS)."
                  },
                  "last_partner_name": {
                    "type": "string",
                    "description": "Last name of the partner on a marriage certificate (AUS)."
                  },
                  "voter_id": {
                    "type": "string",
                    "description": "Voter registration number (IND EPIC, IRL)."
                  },
                  "epic_card": {
                    "type": "string",
                    "description": "India EPIC voter card number."
                  },
                  "pan": {
                    "type": "string",
                    "description": "India PAN (Permanent Account Number)."
                  },
                  "national_id": {
                    "type": "string",
                    "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)."
                  },
                  "cic": {
                    "type": "string",
                    "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)."
                  },
                  "identificador_ciudadano": {
                    "type": "string",
                    "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`."
                  },
                  "ocr": {
                    "type": "string",
                    "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`."
                  },
                  "voter_number": {
                    "type": "string",
                    "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`."
                  },
                  "emission_number": {
                    "type": "string",
                    "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`."
                  },
                  "bvn": {
                    "type": "string",
                    "description": "Nigerian Bank Verification Number."
                  },
                  "bank_card_number": {
                    "type": "string",
                    "description": "Bank card number (CHN bank-card verification)."
                  },
                  "ssn": {
                    "type": "string",
                    "description": "Social Security Number (USA)."
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone number for phone-verification services."
                  },
                  "landline": {
                    "type": "string",
                    "description": "Landline number for phone-verification services."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address for identity-verification services."
                  },
                  "partial_match_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "NO_ACTION",
                    "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)."
                  },
                  "no_match_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`."
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Your identifier for the validated user; echoed back and stored with the session."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Free-form JSON stored with the request and echoed back."
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "issuing_state"
                ],
                "properties": {
                  "issuing_state": {
                    "type": "string",
                    "description": "ISO 3166-1 **alpha-3** country code of the registry to validate against (e.g. `BRA`, `ESP`, `COL`). Determines which services are available. Unsupported codes return `400` with the full list of valid options.",
                    "example": "BRA"
                  },
                  "services": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Catalog `service_id`s to run for this country (e.g. `[\"bra_cpf\"]`). Also accepts a single string or a comma-separated/JSON-encoded string. **If omitted or empty, exactly one default service runs** — the longest-established live service for the country; newer, biometric, and pay-as-you-go services must be named explicitly. Sending `services` switches the response to the extended shape (adds `services_used` and `match_score`). Every id must exist and be live for the `issuing_state`; services that require onboarding return `400` until activated for your organization. Discover available services per country via `GET /v1/organization/database-validation-countries/` (a catalog endpoint not documented in this spec) or the Business Console workflow editor.",
                    "example": [
                      "bra_cpf"
                    ]
                  },
                  "validation_type": {
                    "type": "string",
                    "deprecated": true,
                    "description": "DEPRECATED. Accepted for backward compatibility but ignored — the response `validation_type` is now derived from how many services full-matched. Use `services` to pin specific services."
                  },
                  "consent": {
                    "type": "boolean",
                    "default": false,
                    "description": "Set to `true` when the end user has explicitly consented to the selected validation services. Required for services flagged `requires_consent=true` in the catalog — without it those services return `400` with `services_requiring_consent`."
                  },
                  "identification_number": {
                    "type": "string",
                    "description": "Universal identification number — automatically mapped to the correct country-specific field, so you can use it instead of `personal_number`/`tax_number`/`document_number`: ARG→document_number (DNI), BOL→document_number (CI), BRA→tax_number (CPF, 11 digits), CHL→personal_number (RUT), COL→personal_number (Cédula), CRI→personal_number (Cédula), DOM→personal_number (Cédula, 11 digits), ECU→personal_number (Cédula, 10 digits), ESP→personal_number (DNI/NIE), GTM→document_number (DPI), HND→document_number (DNI), MEX→personal_number (CURP, 18 chars), PAN→personal_number (Cédula), PER→personal_number (DNI, 8 digits), PRY→document_number (CI), SLV→document_number (DUI), URY→personal_number (CI), VEN→document_number (Cédula). It never overrides an explicitly provided country-specific field."
                  },
                  "first_name": {
                    "type": "string",
                    "description": "The individual's first name. Required by some services/countries.",
                    "example": "John"
                  },
                  "last_name": {
                    "type": "string",
                    "description": "The individual's last name. Required by some services/countries.",
                    "example": "Doe"
                  },
                  "middle_name": {
                    "type": "string",
                    "description": "Middle name, used by some country services (AUS, NZL, …)."
                  },
                  "full_name": {
                    "type": "string",
                    "description": "Full name — some services accept this in lieu of first/last name (e.g. CHN services, which match the native-script name)."
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "description": "Date of birth, `YYYY-MM-DD`. Required by many services.",
                    "example": "1980-01-01"
                  },
                  "personal_number": {
                    "type": "string",
                    "description": "Government-issued unique personal identifier. Used by: CHL (RUT), COL (Cédula), CRI, DOM, ECU, ESP (DNI/NIE), MEX (CURP), PAN (Cédula), PER (DNI), URY. Consider `identification_number` instead."
                  },
                  "tax_number": {
                    "type": "string",
                    "description": "Tax identification number. Used by: BRA (CPF, 11 digits). Consider `identification_number` instead."
                  },
                  "document_number": {
                    "type": "string",
                    "description": "Document number. Used by: ARG (DNI), BOL (CI), GTM (DPI), HND (DNI), PRY (CI), SLV (DUI), VEN (Cédula). Consider `identification_number` instead."
                  },
                  "document_type": {
                    "type": "string",
                    "enum": [
                      "P",
                      "DL",
                      "ID",
                      "RP",
                      "SSC",
                      "HIC",
                      "WP",
                      "TC",
                      "VISA",
                      "PSC",
                      "BC",
                      "OTHER"
                    ],
                    "description": "Type of document being validated: `P` passport, `DL` driver license, `ID` national ID, `RP` residence permit (plus `SSC`, `HIC`, `WP`, `TC`, `VISA`, `PSC`, `BC`, `OTHER`). Required by some services (e.g. ESP)."
                  },
                  "expiration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Document expiration date, `YYYY-MM-DD`. Required for ESP (Spain) validation.",
                    "example": "2030-01-15"
                  },
                  "date_of_issue": {
                    "type": "string",
                    "format": "date",
                    "description": "Document issue date, `YYYY-MM-DD` (fecha de expedición). Required for COL (Colombia) cédula validation.",
                    "example": "2015-06-20"
                  },
                  "nationality": {
                    "type": "string",
                    "description": "Nationality as ISO 3166-1 alpha-3. Required by some services."
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "M",
                      "F",
                      "X"
                    ],
                    "description": "Gender: `M`, `F`, or `X` (other/unknown). Required for Argentina's RENAPER validation (`arg_renaper`)."
                  },
                  "address": {
                    "description": "Residential address. Prefer the structured object `{\"street_1\":\"123 Main St\",\"street_2\":\"Apt 4B\",\"city\":\"Springfield\",\"region\":\"IL\",\"postal_code\":\"62701\",\"country\":\"US\"}`; a complete single-line string is still accepted. Required by address-verification services; when a service defines address requirements, missing parts return `400` with `address_fields_required_by`.",
                    "oneOf": [
                      {
                        "type": "object",
                        "properties": {
                          "street_1": {
                            "type": "string"
                          },
                          "street_2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "region": {
                            "type": "string"
                          },
                          "postal_code": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      },
                      {
                        "type": "string",
                        "description": "Complete single-line address (legacy)."
                      }
                    ]
                  },
                  "address_element_1": {
                    "type": "string",
                    "description": "Street address including street number and type. If omitted, derived from `address`."
                  },
                  "address_element_2": {
                    "type": "string",
                    "description": "Apartment, unit, building, floor, or extra address line. Only send when you have it explicitly."
                  },
                  "address_element_3": {
                    "type": "string",
                    "description": "City, suburb, district, locality, or neighborhood. If omitted, derived from `address`."
                  },
                  "address_element_4": {
                    "type": "string",
                    "description": "State, province, region, or town. If omitted, derived from `address`."
                  },
                  "address_element_5": {
                    "type": "string",
                    "description": "Postcode or postal code. `postal_code` is accepted as an alias."
                  },
                  "postal_code": {
                    "type": "string",
                    "description": "Postal code for address-based services (alias of `address_element_5`)."
                  },
                  "driver_license_number": {
                    "type": "string",
                    "description": "Driver licence number for government document-verification services (AUS, NZL, IND)."
                  },
                  "driver_license_card_number": {
                    "type": "string",
                    "description": "Physical driver licence card number, where required separately from the licence number (AUS)."
                  },
                  "driver_license_state": {
                    "type": "string",
                    "description": "Driver licence state or territory of issue (AUS)."
                  },
                  "driver_license_version": {
                    "type": "string",
                    "description": "Driver licence version code (NZL)."
                  },
                  "passport_number": {
                    "type": "string",
                    "description": "Passport number for government passport-verification services (AUS, NZL, IND, CHN)."
                  },
                  "passport_expiration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Passport expiry date, `YYYY-MM-DD`, for passport-verification services."
                  },
                  "passport_file_number": {
                    "type": "string",
                    "description": "Passport file number (IND)."
                  },
                  "passport_issue_country": {
                    "type": "string",
                    "description": "Issuing country of the passport (ISO 3166-1 alpha-3) for passport-verification services."
                  },
                  "medicare_card_number": {
                    "type": "string",
                    "description": "Medicare card number (AUS)."
                  },
                  "immi_card_number": {
                    "type": "string",
                    "description": "Australian ImmiCard number."
                  },
                  "immi_card_expiry_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Australian ImmiCard expiry date."
                  },
                  "citizenship_certificate_number": {
                    "type": "string",
                    "description": "Citizenship certificate number (AUS)."
                  },
                  "birth_registration_number": {
                    "type": "string",
                    "description": "Birth-certificate registration number (AUS)."
                  },
                  "birth_registration_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Birth-certificate registration date (AUS)."
                  },
                  "birth_registration_state": {
                    "type": "string",
                    "description": "Birth-certificate registration state (AUS)."
                  },
                  "marriage_certificate_number": {
                    "type": "string",
                    "description": "Marriage certificate number (AUS)."
                  },
                  "change_of_name_certificate_number": {
                    "type": "string",
                    "description": "Change-of-name certificate number (AUS)."
                  },
                  "first_partner_name": {
                    "type": "string",
                    "description": "First name of the partner on a marriage certificate (AUS)."
                  },
                  "last_partner_name": {
                    "type": "string",
                    "description": "Last name of the partner on a marriage certificate (AUS)."
                  },
                  "voter_id": {
                    "type": "string",
                    "description": "Voter registration number (IND EPIC, IRL)."
                  },
                  "epic_card": {
                    "type": "string",
                    "description": "India EPIC voter card number."
                  },
                  "pan": {
                    "type": "string",
                    "description": "India PAN (Permanent Account Number)."
                  },
                  "national_id": {
                    "type": "string",
                    "description": "National ID number (CHN, MYS, KEN, KHM, NGA, ZAF, …)."
                  },
                  "cic": {
                    "type": "string",
                    "description": "Mexican INE/IFE Código de Identificación de Credencial (CIC), 9 digits. Used by the MEX INE credential-validity service (`mex_ine_vigencia`)."
                  },
                  "identificador_ciudadano": {
                    "type": "string",
                    "description": "Mexican INE Identificador del Ciudadano (9 digits). Used with `cic` for modern INE models (E/F/G/H) in `mex_ine_vigencia`."
                  },
                  "ocr": {
                    "type": "string",
                    "description": "Mexican INE OCR number (13 digits, back of card). Used with `cic` for Model D in `mex_ine_vigencia`."
                  },
                  "voter_number": {
                    "type": "string",
                    "description": "Mexican INE Clave de Elector (18 chars). Used with `emission_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`."
                  },
                  "emission_number": {
                    "type": "string",
                    "description": "Mexican INE Número de Emisión. Used with `voter_number` for legacy IFE models (A/B/C) in `mex_ine_vigencia`."
                  },
                  "bvn": {
                    "type": "string",
                    "description": "Nigerian Bank Verification Number."
                  },
                  "bank_card_number": {
                    "type": "string",
                    "description": "Bank card number (CHN bank-card verification)."
                  },
                  "ssn": {
                    "type": "string",
                    "description": "Social Security Number (USA)."
                  },
                  "phone": {
                    "type": "string",
                    "description": "Phone number for phone-verification services."
                  },
                  "landline": {
                    "type": "string",
                    "description": "Landline number for phone-verification services."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Email address for identity-verification services."
                  },
                  "partial_match_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "NO_ACTION",
                    "description": "What a `partial_match` does to `database_validation.status`. Defaults to `NO_ACTION` (status stays `Approved`, with a warning)."
                  },
                  "no_match_action": {
                    "type": "string",
                    "enum": [
                      "DECLINE",
                      "NO_ACTION"
                    ],
                    "default": "DECLINE",
                    "description": "What a `no_match` does to `database_validation.status`. Defaults to `DECLINE`."
                  },
                  "save_api_request": {
                    "type": "boolean",
                    "default": true,
                    "description": "Persist the validation as a session (console visibility, decision endpoint, `status.updated` webhook). Also changes the `validations` response shape: per-service objects when `true` (default), a merged field map when `false`."
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Your identifier for the validated user; echoed back and stored with the session."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Free-form JSON stored with the request and echoed back."
                  },
                  "selfie": {
                    "type": "string",
                    "format": "binary",
                    "description": "Selfie image (`jpg`, `jpeg`, `png`, `webp`; max 2 MB — images are compressed to ~1.5 MB before upload to the registry) for biometric validation services, e.g. Argentina RENAPER (`arg_renaper`) and Panama SIB (`pan_cedula_sib`, `pan_cedula_sib_plus`). Required whenever a selected service lists it as a required field. Only available via `multipart/form-data`."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/database-validation/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"issuing_state\": \"BRA\",\n    \"services\": [\"bra_cpf\"],\n    \"identification_number\": \"12345678900\",\n    \"first_name\": \"John\",\n    \"last_name\": \"Doe\",\n    \"date_of_birth\": \"1980-01-01\",\n    \"vendor_data\": \"user-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/database-validation/\",\n    headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]},\n    json={\n        \"issuing_state\": \"BRA\",\n        \"services\": [\"bra_cpf\"],\n        \"identification_number\": \"12345678900\",\n        \"first_name\": \"John\",\n        \"last_name\": \"Doe\",\n        \"date_of_birth\": \"1980-01-01\",\n        \"vendor_data\": \"user-1234\",\n    },\n    timeout=45,\n)\nresp.raise_for_status()\ndv = resp.json()[\"database_validation\"]\nprint(dv[\"status\"], dv[\"match_type\"])  # e.g. Approved full_match\n\n# Biometric services need a selfie via multipart/form-data:\n# requests.post(..., data={\"issuing_state\": \"ARG\", \"services\": '[\"arg_renaper\"]',\n#                          \"identification_number\": \"12345678\", \"gender\": \"M\", ...},\n#               files={\"selfie\": open(\"selfie.jpg\", \"rb\")})"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/database-validation/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    issuing_state: 'BRA',\n    services: ['bra_cpf'],\n    identification_number: '12345678900',\n    first_name: 'John',\n    last_name: 'Doe',\n    date_of_birth: '1980-01-01',\n    vendor_data: 'user-1234',\n  }),\n});\nconst data = await res.json();\nconsole.log(data.database_validation.status, data.database_validation.match_type);"
          }
        ],
        "responses": {
          "200": {
            "description": "Validation completed — at least one selected service returned a usable result. Inspect `database_validation.match_type` for the aggregate outcome, `validations` for the per-service field comparisons and `outcome_code`s, and `errors` (when present) for services that failed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Persisted session id when `save_api_request=true` (usable with `GET /v3/session/{sessionId}/decision/`); otherwise a transient correlation UUID."
                    },
                    "database_validation": {
                      "type": "object",
                      "properties": {
                        "status": {
                          "type": "string",
                          "enum": [
                            "Approved",
                            "Declined"
                          ],
                          "description": "`Declined` when the overall `match_type` triggers a `DECLINE` action (`no_match_action` defaults to `DECLINE`, `partial_match_action` to `NO_ACTION`); `Approved` otherwise."
                        },
                        "issuing_state": {
                          "type": "string",
                          "description": "Echo of the ISO 3166-1 alpha-3 country validated against.",
                          "example": "BRA"
                        },
                        "validation_type": {
                          "type": "string",
                          "enum": [
                            "one_by_one",
                            "two_by_two"
                          ],
                          "description": "Derived from the results: `two_by_two` when ≥2 distinct services produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions); `one_by_one` otherwise. (The request field of the same name is deprecated and ignored.)"
                        },
                        "screened_data": {
                          "type": "object",
                          "description": "Echo of the input fields that were sent to the selected services (dates normalized to `YYYY-MM-DD`; a `selfie`, when sent, is returned as a pre-signed URL; `consent_obtained: true` is added when consent was given)."
                        },
                        "match_type": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "full_match",
                            "partial_match",
                            "no_match"
                          ],
                          "description": "Overall result aggregated across services with field-level comparisons: any `full_match` → `full_match`; else any `partial_match` → `partial_match`; else `no_match`. Outcome-only services, such as biometric criminal screening, can return `null`; inspect each item in `validations` for its `outcome_code`."
                        },
                        "validations": {
                          "description": "Shape depends on `save_api_request`. **`true` (default):** an array of per-service objects (`service_id`, `service_name`, `validation`, `outcome_code`, `outcome_detail`, `source_data`). **`false`:** a single flat object merging the field-level results of all services (`{\"full_name\": \"full_match\", ...}`).",
                          "oneOf": [
                            {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "description": "Per-service validation result (returned when `save_api_request=true`, the default).",
                                "properties": {
                                  "service_id": {
                                    "type": "string",
                                    "description": "Catalog id of the service that produced this result (e.g. `bra_cpf`).",
                                    "example": "bra_cpf"
                                  },
                                  "service_name": {
                                    "type": "string",
                                    "description": "Human-readable catalog name of the service.",
                                    "example": "Brazil - CPF status check"
                                  },
                                  "validation": {
                                    "type": "object",
                                    "description": "Field-by-field comparison between your input and the registry record. Keys are canonical field names (`full_name`, `date_of_birth`, `identification_number`, `address`, …); values are `full_match`, `partial_match`, or `no_match`.",
                                    "additionalProperties": {
                                      "type": "string",
                                      "enum": [
                                        "full_match",
                                        "partial_match",
                                        "no_match"
                                      ]
                                    }
                                  },
                                  "outcome_code": {
                                    "type": "string",
                                    "enum": [
                                      "MATCH",
                                      "PARTIAL_MATCH",
                                      "NO_MATCH",
                                      "DOCUMENT_NOT_FOUND",
                                      "INVALID_DOCUMENT_FORMAT",
                                      "INVALID_INPUT",
                                      "MINOR_BLOCKED",
                                      "DECEASED",
                                      "BIOMETRIC_NO_MATCH",
                                      "BIOMETRIC_IMAGE_UNUSABLE",
                                      "INCONCLUSIVE",
                                      "REGISTRY_UNAVAILABLE",
                                      "REGISTRY_ERROR"
                                    ],
                                    "description": "Vendor-neutral outcome of the registry lookup. `MATCH`/`PARTIAL_MATCH`/`NO_MATCH` describe the comparison; `DOCUMENT_NOT_FOUND` means the identifier is not in the registry; `INCONCLUSIVE` means the registry could not confirm either way (routed to review — NOT a no-match); `BIOMETRIC_NO_MATCH` is a definitive face mismatch while `BIOMETRIC_IMAGE_UNUSABLE` is a technical image problem (retake the selfie); `DECEASED`/`MINOR_BLOCKED` are registry-policy outcomes; `REGISTRY_UNAVAILABLE`/`REGISTRY_ERROR` indicate upstream failures."
                                  },
                                  "outcome_detail": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Raw upstream status detail backing the outcome code (e.g. an HTTP or registry status code)."
                                  },
                                  "source_data": {
                                    "type": "object",
                                    "nullable": true,
                                    "description": "Cleaned, normalized view of the registry record using canonical field names (`identification_number`, `first_name`, `last_name`, `date_of_birth`, plus registry-specific flags). Image fields, when present, are returned as pre-signed URLs."
                                  }
                                }
                              }
                            },
                            {
                              "type": "object",
                              "additionalProperties": {
                                "type": "string",
                                "enum": [
                                  "full_match",
                                  "partial_match",
                                  "no_match"
                                ]
                              },
                              "description": "Merged field-level results (stateless `save_api_request=false` shape)."
                            }
                          ]
                        },
                        "warnings": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "feature": {
                                "type": "string",
                                "description": "Feature that raised the warning."
                              },
                              "risk": {
                                "type": "string",
                                "description": "Machine-readable risk identifier."
                              },
                              "additional_data": {
                                "type": "object",
                                "nullable": true,
                                "description": "Extra context for the warning, when available."
                              },
                              "log_type": {
                                "type": "string",
                                "description": "`warning`, `information`, or `error` — how the risk affected the outcome."
                              },
                              "short_description": {
                                "type": "string",
                                "description": "One-line human-readable summary."
                              },
                              "long_description": {
                                "type": "string",
                                "description": "Full human-readable explanation."
                              }
                            }
                          },
                          "description": "`DATABASE_VALIDATION_NO_MATCH` or `DATABASE_VALIDATION_PARTIAL_MATCH` when applicable; empty on a full match."
                        },
                        "errors": {
                          "type": "array",
                          "description": "Present only when some (but not all) selected services failed. Each entry carries `service_id`, `code`, and `message`. Failed services are not billed.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "service_id": {
                                "type": "string"
                              },
                              "code": {
                                "type": "string",
                                "example": "empty_provider_response"
                              },
                              "message": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "services_used": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Catalog ids of the services that returned a billable result. **Only present when the request explicitly sent `services`.**"
                        },
                        "match_score": {
                          "type": "number",
                          "description": "Fraction (0.0–1.0) of attempted services that produced a full match (on the identification number, on full name + date of birth together, or on the address without contradictions). **Only present when the request explicitly sent `services`.**"
                        }
                      }
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "Echo of the `vendor_data` you sent."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true,
                      "description": "Echo of the `metadata` you sent."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "examples": {
                  "Full match (Approved)": {
                    "summary": "Default save_api_request=true — per-service `validations` objects",
                    "value": {
                      "request_id": "7f9a1c0e-1f2b-4f6e-9d3a-2b1c0e7f9a1c",
                      "database_validation": {
                        "status": "Approved",
                        "issuing_state": "BRA",
                        "validation_type": "one_by_one",
                        "screened_data": {
                          "tax_number": "12345678900",
                          "first_name": "John",
                          "last_name": "Doe",
                          "date_of_birth": "1980-01-01"
                        },
                        "match_type": "full_match",
                        "validations": [
                          {
                            "validation": {
                              "full_name": "full_match",
                              "date_of_birth": "full_match",
                              "identification_number": "full_match"
                            },
                            "outcome_code": "MATCH",
                            "outcome_detail": "200",
                            "service_id": "bra_cpf",
                            "service_name": "Brazil - CPF status check",
                            "source_data": {
                              "identification_number": "12345678900",
                              "first_name": "JOHN",
                              "last_name": "DOE",
                              "date_of_birth": "1980-01-01",
                              "lgpd_minor": false,
                              "minor_under_16": false,
                              "minor_under_18": false
                            }
                          }
                        ],
                        "warnings": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-11T10:30:00.000000+00:00"
                    }
                  },
                  "Explicit services (extended shape)": {
                    "summary": "Request sent `services` — response adds `services_used` and `match_score`",
                    "value": {
                      "request_id": "b3e7c1a2-9d4f-4f0b-8a6c-5e2d1f0a9b3e",
                      "database_validation": {
                        "status": "Approved",
                        "issuing_state": "BRA",
                        "validation_type": "one_by_one",
                        "screened_data": {
                          "tax_number": "12345678900",
                          "first_name": "John",
                          "last_name": "Doe",
                          "date_of_birth": "1980-01-01"
                        },
                        "match_type": "full_match",
                        "validations": [
                          {
                            "validation": {
                              "full_name": "full_match",
                              "date_of_birth": "full_match",
                              "identification_number": "full_match"
                            },
                            "outcome_code": "MATCH",
                            "outcome_detail": "200",
                            "service_id": "bra_cpf",
                            "service_name": "Brazil - CPF status check",
                            "source_data": {
                              "identification_number": "12345678900",
                              "first_name": "JOHN",
                              "last_name": "DOE",
                              "date_of_birth": "1980-01-01",
                              "lgpd_minor": false,
                              "minor_under_16": false,
                              "minor_under_18": false
                            }
                          }
                        ],
                        "warnings": [],
                        "services_used": [
                          "bra_cpf"
                        ],
                        "match_score": 1
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-11T10:30:00.000000+00:00"
                    }
                  },
                  "No match (Declined)": {
                    "summary": "Registry record does not match — default `no_match_action=DECLINE`",
                    "value": {
                      "request_id": "0a1b2c3d-4e5f-6789-abcd-ef0123456789",
                      "database_validation": {
                        "status": "Declined",
                        "issuing_state": "BRA",
                        "validation_type": "one_by_one",
                        "screened_data": {
                          "tax_number": "12345678900",
                          "first_name": "John",
                          "last_name": "Doe",
                          "date_of_birth": "1980-01-01"
                        },
                        "match_type": "no_match",
                        "validations": [
                          {
                            "validation": {
                              "full_name": "no_match",
                              "date_of_birth": "no_match",
                              "identification_number": "no_match"
                            },
                            "outcome_code": "NO_MATCH",
                            "outcome_detail": "200",
                            "service_id": "bra_cpf",
                            "service_name": "Brazil - CPF status check",
                            "source_data": {
                              "identification_number": "00987654321",
                              "first_name": "TOTALLY",
                              "last_name": "DIFFERENT NAME",
                              "date_of_birth": "1999-01-01",
                              "lgpd_minor": false,
                              "minor_under_16": false,
                              "minor_under_18": false
                            }
                          }
                        ],
                        "warnings": [
                          {
                            "feature": "DATABASE_VALIDATION",
                            "risk": "DATABASE_VALIDATION_NO_MATCH",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Database validation no match",
                            "long_description": "The system identified a no match in the database validation, requiring further investigation."
                          }
                        ]
                      },
                      "vendor_data": null,
                      "metadata": null,
                      "created_at": "2026-06-11T10:31:00.000000+00:00"
                    }
                  },
                  "Stateless (save_api_request=false)": {
                    "summary": "`validations` collapses to a merged field map; nothing is stored",
                    "value": {
                      "request_id": "c4d5e6f7-8901-2345-6789-abcdef012345",
                      "database_validation": {
                        "status": "Approved",
                        "issuing_state": "BRA",
                        "validation_type": "one_by_one",
                        "screened_data": {
                          "tax_number": "12345678900",
                          "first_name": "John",
                          "last_name": "Doe",
                          "date_of_birth": "1980-01-01"
                        },
                        "match_type": "full_match",
                        "validations": {
                          "full_name": "full_match",
                          "date_of_birth": "full_match",
                          "identification_number": "full_match"
                        },
                        "warnings": []
                      },
                      "vendor_data": null,
                      "metadata": null,
                      "created_at": "2026-06-11T10:32:00.000000+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — nothing is billed and no provider is called. Field-level problems use DRF's standard envelope; catalog problems return a `services` array (plus `requires_onboarding` or `services_requiring_consent` when relevant); missing-field errors include `fields_required_by` mapping each missing field to the services that demand it; format problems checked per service return `invalid_fields_by_service`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid issuing_state": {
                    "summary": "Country not supported — message lists every valid alpha-3 code",
                    "value": {
                      "error": [
                        "Invalid issuing state. Valid options are: ARE, ARG, AUS, AUT, BEL, BGD, BOL, BRA, CAN, CHE, CHL, CHN, CIV, COL, CRI, CZE, DEU, DNK, DOM, ECU, ESP, FIN, FRA, GBR, GHA, GLB, GRC, GTM, HKG, HND, IDN, IND, IRL, ITA, KEN, KHM, MAR, MEX, MYS, NGA, NLD, NOR, NZL, OMN, PAN, PER, PHL, POL, PRT, PRY, QAT, SGP, SLV, SVK, SWE, THA, UGA, URY, USA, VEN, ZAF, ZMB, ZWE"
                      ]
                    }
                  },
                  "Missing required fields": {
                    "summary": "Union of required fields across selected services, with the universal-field hint",
                    "value": {
                      "error": [
                        "Missing required fields: tax_number (or use 'identification_number' which maps to 'tax_number' for BRA)"
                      ],
                      "fields_required_by": {
                        "tax_number": [
                          "bra_cpf"
                        ]
                      }
                    }
                  },
                  "Unknown service id": {
                    "summary": "`services` contains an id that is not in the catalog",
                    "value": {
                      "services": [
                        "Unknown service_id(s): ['bogus_service']"
                      ]
                    }
                  },
                  "Service not live for country": {
                    "summary": "`services` names a service from another country (or a stub entry)",
                    "value": {
                      "services": [
                        "Services not currently live for PER: ['bra_cpf']. Available services: ['per_dni', 'per_residential', 'per_tax_registration']"
                      ]
                    }
                  },
                  "Service requires onboarding": {
                    "summary": "Service exists but is not activated for your organization yet",
                    "value": {
                      "services": [
                        "Services require onboarding before they can be enabled for AUS: ['aus_australia_driver_licence']. Contact Didit support to activate them."
                      ],
                      "requires_onboarding": [
                        "aus_australia_driver_licence"
                      ]
                    }
                  },
                  "Consent missing": {
                    "summary": "A selected service is flagged requires_consent=true and `consent` was not `true`",
                    "value": {
                      "consent": [
                        "Explicit end-user consent is required for these database validation services: ['chn_national_id']. Send `consent=true`."
                      ],
                      "services_requiring_consent": [
                        "chn_national_id"
                      ]
                    }
                  },
                  "Country format rule violation": {
                    "summary": "Identifier fails the country's format rule (length/digits/regex)",
                    "value": {
                      "tax_number": [
                        "Invalid tax number for BRA: Brazilian CPF (exactly 11 digits). Must be exactly 11 characters long (got 10)."
                      ]
                    }
                  },
                  "Service-specific format violation": {
                    "summary": "A field fails the selected service's own format requirements",
                    "value": {
                      "error": [
                        "One or more fields do not match the format required by the selected service."
                      ],
                      "invalid_fields_by_service": {
                        "chn_national_id": [
                          "national_id_format"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — and also when the calling organization's balance cannot cover the call. Authentication failures return `403` with `{\"detail\": ...}`; this API never returns `401`. Credit shortfalls return `403` with `{\"error\": ...}` before any screening or provider call happens.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the call",
                    "value": {
                      "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "502": {
            "description": "No selected service returned a usable result (provider outage, empty registry response). Nothing is billed. When `save_api_request=true` (default) the failed attempt is still recorded as a session with status `Not Finished` and a `COULD_NOT_PERFORM_DATABASE_VALIDATION` warning. Safe to retry later. Partial failures do **not** use this code — they return `200` with `database_validation.errors`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "validation_errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "service_id": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Provider returned no result": {
                    "summary": "Every selected service failed",
                    "value": {
                      "error": "Database validation could not be performed. The external validation service did not return a result. Please try again later.",
                      "validation_errors": [
                        {
                          "service_id": "bra_cpf",
                          "code": "empty_provider_response",
                          "message": "The provider did not return a database validation result."
                        }
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/phone/send/": {
      "post": {
        "summary": "Send Phone Code",
        "description": "Send a one-time passcode (OTP) to a phone number over WhatsApp, SMS, or another supported channel, then verify it with [`POST /v3/phone/check/`](/standalone-apis/phone-check).\n\n**How send and check pair up.** Verification state is keyed by your application plus the E.164 `phone_number` — the check call does not take `request_id`. Each new send creates a pending verification that lives for **5 minutes**: call the check with the same number and the code the user received before the window closes. Calling send again for the same number while a verification is pending re-sends a code for that same verification (`status: \"Retry\"`, same `request_id`). At most one retry is attached this way (two sends total); a further send starts a fresh verification with a new `request_id`. The 5-minute window is measured from the first send and is not extended by retries.\n\n**Delivery channels.** `options.preferred_channel` selects the channel (`whatsapp` by default; `sms`, `telegram`, `voice`, `rcs`, `viber`, and `zalo` are also supported). When the preferred channel is unavailable for the destination country or number, delivery automatically falls back to SMS — the send still reports `Success`, and the channel actually used is reported by the check response (`phone.verification_method` and the `lifecycle` events).\n\n**Send statuses.** `Success` — OTP sent for a new verification; `Retry` — OTP re-sent for the pending verification; `Blocked` — the anti-fraud layer refused to send (see `reason`, e.g. `repeated_attempts`, `suspicious`, `spam`). A `Blocked` send immediately finalizes the verification as `Declined` with a high-risk warning, so a follow-up check returns `Expired or Not Found`.\n\n**Billing.** The price depends on the destination country and the channel that actually delivers the message — see [Phone Verification Pricing](/getting-started/phone-verification-pricing). Only `Blocked` sends are guaranteed free: any other unbilled send attempt is charged at verification finalization or expiration, even if delivery was never confirmed or the delivery provider reported the message undeliverable. Checks are always free. As an anti-abuse measure, phone verification is disabled until your organization's first top-up (`403`).\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no message is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints, plus an anti-abuse cap of 4 send attempts per phone number per hour; exceeding either returns `429`.",
        "operationId": "post_v3phone_send",
        "tags": [
          "Standalone APIs"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/phone/send/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"phone_number\": \"+14155552671\",\n    \"options\": {\n      \"code_size\": 6,\n      \"preferred_channel\": \"whatsapp\",\n      \"locale\": \"en-US\"\n    },\n    \"vendor_data\": \"user-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/phone/send/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"phone_number\": \"+14155552671\",\n        \"options\": {\n            \"code_size\": 6,\n            \"preferred_channel\": \"whatsapp\",\n            \"locale\": \"en-US\",\n        },\n        \"vendor_data\": \"user-1234\",\n    },\n    timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json())  # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/phone/check/ using the same phone_number"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/phone/send/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    phone_number: '+14155552671',\n    options: { code_size: 6, preferred_channel: 'whatsapp', locale: 'en-US' },\n    vendor_data: 'user-1234',\n  }),\n});\nif (!res.ok) throw new Error(`Phone send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/phone/check/ using the same phone_number"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "phone_number": {
                    "type": "string",
                    "maxLength": 20,
                    "description": "Recipient phone number in E.164 format — leading `+` and country code (e.g., `+14155552671`). Numbers are validated and normalized to E.164; unparseable or invalid numbers return `400` with a `phone_number` field error.",
                    "example": "+14155552671"
                  },
                  "options": {
                    "type": "object",
                    "description": "OTP delivery options. All fields are optional.",
                    "properties": {
                      "code_size": {
                        "type": "integer",
                        "minimum": 4,
                        "maximum": 8,
                        "default": 6,
                        "description": "Number of digits in the OTP."
                      },
                      "locale": {
                        "type": "string",
                        "maxLength": 5,
                        "pattern": "^[a-z]{2,3}(-[A-Z]{2,3})?$",
                        "description": "BCP-47 locale used to localize the verification message (e.g., `en-US`, `fr`, `es`). Invalid formats return `400`.",
                        "example": "en-US"
                      },
                      "preferred_channel": {
                        "type": "string",
                        "enum": [
                          "whatsapp",
                          "sms",
                          "telegram",
                          "voice",
                          "rcs",
                          "viber",
                          "zalo"
                        ],
                        "default": "whatsapp",
                        "description": "Preferred delivery channel. If the channel is unavailable for the destination country or number, the message automatically falls back to SMS — the send still reports `Success`."
                      }
                    }
                  },
                  "signals": {
                    "type": "object",
                    "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.",
                    "properties": {
                      "ip": {
                        "type": "string",
                        "description": "IP address of the end user's device. IPv4 or IPv6.",
                        "example": "192.0.2.1"
                      },
                      "device_id": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.",
                        "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2"
                      },
                      "device_platform": {
                        "type": "string",
                        "enum": [
                          "android",
                          "ios",
                          "ipados",
                          "tvos",
                          "web"
                        ],
                        "description": "Platform of the end user's device."
                      },
                      "device_model": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Model of the end user's device.",
                        "example": "iPhone17,2"
                      },
                      "os_version": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Operating-system version of the end user's device.",
                        "example": "18.0.1"
                      },
                      "app_version": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Version of your application.",
                        "example": "1.2.34"
                      },
                      "user_agent": {
                        "type": "string",
                        "maxLength": 512,
                        "description": "User agent of the end user's browser or app.",
                        "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1"
                      }
                    }
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console."
                  }
                },
                "required": [
                  "phone_number"
                ]
              },
              "examples": {
                "WhatsApp (default)": {
                  "summary": "6-digit OTP via WhatsApp, falling back to SMS automatically",
                  "value": {
                    "phone_number": "+14155552671",
                    "options": {
                      "preferred_channel": "whatsapp",
                      "locale": "en-US"
                    },
                    "vendor_data": "user-1234"
                  }
                },
                "SMS with custom code length": {
                  "summary": "Force SMS delivery with a 4-digit code",
                  "value": {
                    "phone_number": "+34699999999",
                    "options": {
                      "preferred_channel": "sms",
                      "code_size": 4,
                      "locale": "es"
                    }
                  }
                },
                "With fraud signals": {
                  "summary": "Forward device signals to strengthen anti-fraud screening",
                  "value": {
                    "phone_number": "+14155552671",
                    "signals": {
                      "ip": "192.0.2.1",
                      "device_id": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2",
                      "device_platform": "ios",
                      "device_model": "iPhone17,2",
                      "os_version": "18.0.1",
                      "app_version": "1.2.34"
                    },
                    "vendor_data": "user-1234"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Blocked` means the anti-fraud layer refused and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneVerificationSendResponse"
                },
                "examples": {
                  "Success": {
                    "summary": "OTP sent for a new verification",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Success",
                      "reason": null,
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  },
                  "Retry": {
                    "summary": "Second send for the same number inside the 5-minute window — same request_id",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Retry",
                      "reason": null,
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  },
                  "Blocked": {
                    "summary": "Anti-fraud refusal — verification finalized as Declined, nothing billed",
                    "value": {
                      "request_id": "0d8af1f4-0c43-46d7-b75c-6e4e22c0a7b3",
                      "status": "Blocked",
                      "reason": "repeated_attempts",
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Malformed input returns DRF's field-error envelope (one array of messages per offending field, nested under `options` for option errors). A number that parses but is rejected at send time (unreachable, or an ineligible line type such as a short code) returns a `{\"detail\": ...}` envelope instead.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid phone number (field error)": {
                    "summary": "Number is not valid E.164",
                    "value": {
                      "phone_number": [
                        "Invalid phone number provided."
                      ]
                    }
                  },
                  "Missing phone number": {
                    "summary": "`phone_number` not included in the body",
                    "value": {
                      "phone_number": [
                        "This field is required."
                      ]
                    }
                  },
                  "Invalid options": {
                    "summary": "Out-of-range `code_size`, malformed `locale`, unknown `preferred_channel`",
                    "value": {
                      "options": {
                        "code_size": [
                          "Ensure this value is less than or equal to 8."
                        ],
                        "locale": [
                          "Ensure this field has no more than 5 characters."
                        ],
                        "preferred_channel": [
                          "\"carrier_pigeon\" is not a valid choice."
                        ]
                      }
                    }
                  },
                  "Number rejected at send time": {
                    "summary": "Valid E.164 shape but the number cannot receive an OTP",
                    "value": {
                      "detail": "Invalid phone number provided."
                    }
                  },
                  "Ineligible line type": {
                    "summary": "Line type cannot receive an OTP (e.g., short code)",
                    "value": {
                      "detail": "Invalid phone line type provided."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization has never topped up (anti-abuse gate) or when its balance cannot cover the destination's send price (`{\"error\": ...}`).",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Top-up required": {
                    "summary": "Phone verification stays disabled until the organization's first top-up",
                    "value": {
                      "detail": "To protect against abuse, phone verification is disabled until your first top-up."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the send price",
                    "value": {
                      "error": "You don't have enough credits to perform this request."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Per-number cap": {
                    "summary": "More than 4 verification attempts for the same number in the last hour",
                    "value": {
                      "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number."
                    }
                  },
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected delivery failure while creating the verification (e.g., the delivery provider is unreachable). Safe to retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Provider error": {
                    "summary": "OTP delivery could not be initiated",
                    "value": {
                      "detail": "Error creating phone verification"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/phone/check/": {
      "post": {
        "summary": "Check Phone Code",
        "description": "Verify the OTP delivered by [`POST /v3/phone/send/`](/standalone-apis/phone-send) and get the final verification result plus phone intelligence: carrier name and line type, virtual (VoIP) and disposable flags, and duplicate usage of the number across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the E.164 `phone_number` — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `phone: null`; the third wrong code finalizes the verification as `Declined` with a `VERIFICATION_CODE_ATTEMPTS_EXCEEDED` warning and returns the full phone report.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted or high-risk number), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `phone` carries the full report (`carrier`, `is_virtual`, `is_disposable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_phone_number_action`, `disposable_number_action`, and `voip_number_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `phone.warnings` without affecting the status.\n\n**Billing.** Checks are free — delivery is billed on the send side (see [Phone Verification Pricing](/getting-started/phone-verification-pricing)).\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: code `123456` returns a static `Approved` payload with a simplified flat `phone` object (`status`, `phone_number`, `country_code`, `carrier_name`, `line_type`, `flags`); any other code returns `Failed`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limits.** Shared write budget of 300 requests/min per API key, plus the upstream anti-abuse cap of 4 attempts per number per hour; exceeding either returns `429`.",
        "operationId": "post_v3phone_check",
        "tags": [
          "Standalone APIs"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/phone/check/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"phone_number\": \"+14155552671\",\n    \"code\": \"123456\",\n    \"voip_number_action\": \"DECLINE\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/phone/check/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"phone_number\": \"+14155552671\",  # same number as the send call\n        \"code\": \"123456\",\n        \"voip_number_action\": \"DECLINE\",\n    },\n    timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"])  # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n    print(result[\"phone\"][\"carrier\"], result[\"phone\"][\"is_virtual\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/phone/check/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    phone_number: '+14155552671', // same number as the send call\n    code: '123456',\n    voip_number_action: 'DECLINE',\n  }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n  // full phone report is in data.phone (carrier, is_virtual, matches, ...)\n}"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "phone_number": {
                    "type": "string",
                    "maxLength": 20,
                    "description": "The same phone number used in the matching [`POST /v3/phone/send/`](/standalone-apis/phone-send) call, in E.164 format. This is what links the check to the send.",
                    "example": "+14155552671"
                  },
                  "code": {
                    "type": "string",
                    "minLength": 4,
                    "maxLength": 8,
                    "pattern": "^[0-9]{4,8}$",
                    "description": "The OTP the end user received. Must be 4–8 numeric digits — anything else returns `400` without consuming an attempt. A well-formed but wrong code returns `200` with `status: \"Failed\"` and consumes one of the 3 attempts.",
                    "example": "123456"
                  },
                  "duplicated_phone_number_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the same number was already used by a different user (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings` and fills `phone.matches`."
                  },
                  "disposable_number_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the carrier lookup flags the number as a temporary/burner line (`phone.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`."
                  },
                  "voip_number_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the carrier lookup reports a virtual line type — `voip`, `isp`, or `vpn` (`phone.is_virtual`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `phone.warnings`."
                  }
                },
                "required": [
                  "phone_number",
                  "code"
                ]
              },
              "examples": {
                "Default": {
                  "summary": "Record any risks but never auto-decline",
                  "value": {
                    "phone_number": "+14155552671",
                    "code": "123456"
                  }
                },
                "Strict risk policy": {
                  "summary": "Auto-decline VoIP and disposable lines",
                  "value": {
                    "phone_number": "+14155552671",
                    "code": "123456",
                    "voip_number_action": "DECLINE",
                    "disposable_number_action": "DECLINE"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `phone` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneVerificationCheckResponse"
                },
                "examples": {
                  "Approved": {
                    "summary": "Correct code — full phone report with carrier, lifecycle, and matches",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Approved",
                      "message": "The verification code is correct.",
                      "phone": {
                        "status": "Approved",
                        "phone_number_prefix": "+1",
                        "phone_number": "4155552671",
                        "full_number": "+14155552671",
                        "country_code": "US",
                        "country_name": "United States",
                        "carrier": {
                          "name": "AT&T",
                          "type": "mobile"
                        },
                        "is_disposable": false,
                        "is_virtual": false,
                        "verification_method": "whatsapp",
                        "verification_attempts": 1,
                        "verified_at": "2026-06-12T01:24:47.311323Z",
                        "warnings": [],
                        "lifecycle": [
                          {
                            "type": "PHONE_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null,
                              "channel": "whatsapp",
                              "actual_channel": "whatsapp"
                            },
                            "fee": 0.0709
                          },
                          {
                            "type": "PHONE_DELIVERY_DELIVERED",
                            "timestamp": "2026-06-12T01:23:43.118220+00:00",
                            "details": {
                              "channel": "whatsapp",
                              "status": "delivered"
                            },
                            "fee": 0
                          },
                          {
                            "type": "VALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "123456",
                              "status": "Approved"
                            },
                            "fee": 0
                          },
                          {
                            "type": "PHONE_VERIFICATION_APPROVED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": null,
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Failed (attempts remaining)": {
                    "summary": "Wrong code — the user can retry; request_id here is a one-off UUID",
                    "value": {
                      "request_id": "11111111-2222-3333-4444-555555555555",
                      "status": "Failed",
                      "message": "The verification code is incorrect. Attempts remaining: 2",
                      "phone": null,
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:21.512340+00:00"
                    }
                  },
                  "Declined (max attempts)": {
                    "summary": "Third wrong code — verification finalized as Declined",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Declined",
                      "message": "The verification code is incorrect. Maximum attempts reached.",
                      "phone": {
                        "status": "Declined",
                        "phone_number_prefix": "+1",
                        "phone_number": "4155552671",
                        "full_number": "+14155552671",
                        "country_code": "US",
                        "country_name": "United States",
                        "carrier": {
                          "name": "AT&T",
                          "type": "mobile"
                        },
                        "is_disposable": false,
                        "is_virtual": false,
                        "verification_method": "whatsapp",
                        "verification_attempts": 1,
                        "verified_at": null,
                        "warnings": [
                          {
                            "feature": "PHONE",
                            "risk": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Verification code attempts exceeded",
                            "long_description": "The system detected that the phone number has exceeded the maximum number of verification code attempts."
                          }
                        ],
                        "lifecycle": [
                          {
                            "type": "PHONE_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null,
                              "channel": "whatsapp",
                              "actual_channel": "whatsapp"
                            },
                            "fee": 0.0709
                          },
                          {
                            "type": "INVALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:01.118220+00:00",
                            "details": {
                              "code_tried": "111111",
                              "status": "Failed"
                            },
                            "fee": 0
                          },
                          {
                            "type": "INVALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:21.512340+00:00",
                            "details": {
                              "code_tried": "222222",
                              "status": "Failed"
                            },
                            "fee": 0
                          },
                          {
                            "type": "INVALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "333333",
                              "status": "Failed"
                            },
                            "fee": 0
                          },
                          {
                            "type": "PHONE_VERIFICATION_DECLINED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": {
                              "reason": "VERIFICATION_CODE_ATTEMPTS_EXCEEDED"
                            },
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Declined (risk policy)": {
                    "summary": "Correct code, but the line is VoIP and voip_number_action=DECLINE",
                    "value": {
                      "request_id": "7f1c9d2e-44a1-4b6e-9a3e-5b8e6f1c2d3a",
                      "status": "Declined",
                      "message": "The verification code is correct.",
                      "phone": {
                        "status": "Declined",
                        "phone_number_prefix": "+1",
                        "phone_number": "5005550006",
                        "full_number": "+15005550006",
                        "country_code": "US",
                        "country_name": "United States",
                        "carrier": {
                          "name": "CLEC LLC",
                          "type": "voip"
                        },
                        "is_disposable": false,
                        "is_virtual": true,
                        "verification_method": "sms",
                        "verification_attempts": 1,
                        "verified_at": "2026-06-12T01:24:47.311323Z",
                        "warnings": [
                          {
                            "feature": "PHONE",
                            "risk": "VOIP_NUMBER_DETECTED",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "VoIP number detected",
                            "long_description": "The system detected that the phone number is a VoIP number, which is not allowed."
                          }
                        ],
                        "lifecycle": [
                          {
                            "type": "PHONE_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null,
                              "channel": "whatsapp",
                              "actual_channel": "sms"
                            },
                            "fee": 0.0709
                          },
                          {
                            "type": "VALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "123456",
                              "status": "Approved"
                            },
                            "fee": 0
                          },
                          {
                            "type": "PHONE_VERIFICATION_DECLINED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": {
                              "reason": "VOIP_NUMBER_DETECTED"
                            },
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Expired or Not Found": {
                    "summary": "No pending verification for this number in the last 5 minutes",
                    "value": {
                      "request_id": "69cc35c6-280a-47a6-ab8c-4f32abc02ed0",
                      "status": "Expired or Not Found",
                      "message": "No pending phone verification found in the last 5 minutes.",
                      "vendor_data": null,
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.311323+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid code format": {
                    "summary": "`code` is not 4–8 numeric digits",
                    "value": {
                      "code": [
                        "Invalid code format. Enter 4 to 8 numeric digits."
                      ]
                    }
                  },
                  "Invalid phone number": {
                    "summary": "`phone_number` is not valid E.164",
                    "value": {
                      "phone_number": [
                        "Invalid phone number provided."
                      ]
                    }
                  },
                  "Invalid action": {
                    "summary": "Risk actions accept only NO_ACTION or DECLINE",
                    "value": {
                      "voip_number_action": [
                        "\"REVIEW\" is not a valid choice."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. Additionally, an anti-abuse cap of 4 verification attempts per phone number per hour applies upstream. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers. Note: the `X-RateLimit-*`/`Retry-After` headers accompany only the 300/min write-limit 429; the per-number cap 429 carries no rate-limit headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Per-number cap": {
                    "summary": "More than 4 verification attempts for the same number in the last hour",
                    "value": {
                      "detail": "Maximum verification attempts reached for this phone number. Only 4 authentication attempts are allowed per hour. Try again later or use a different number."
                    }
                  },
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected failure while verifying the code upstream. Safe to retry; the attempt budget is only consumed by completed checks.",
            "content": {
              "application/json": {
                "examples": {
                  "Provider error": {
                    "summary": "Code verification could not be completed",
                    "value": {
                      "detail": "Error checking phone verification"
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/email/send/": {
      "post": {
        "summary": "Send Email Code",
        "description": "Send a one-time passcode (OTP) to an email address, then verify it with [`POST /v3/email/check/`](/standalone-apis/email-check).\n\n**How send and check pair up.** Verification state is keyed by your application plus the `email` address — the check call does not take `request_id`. Each new send creates a pending verification that lives for **5 minutes**: call the check with the same address and the code the user received before the window closes. Calling send again for the same address while a verification is pending generates a fresh code for that same verification (`status: \"Retry\"`, same `request_id`); the previous code stops working. At most one retry is attached this way (two sends total); a further send starts a fresh verification with a new `request_id`. The 5-minute window is measured from the first send and is not extended by retries.\n\n**Deliverability pre-check.** Before anything is sent, the address goes through syntax and DNS/MX validation. Addresses that cannot receive mail return `200` with `status: \"Undeliverable\"` and `reason: \"email_can_not_be_delivered\"` — no email is sent, nothing is billed, and the verification is immediately finalized as `Declined` (a follow-up check returns `Expired or Not Found`). A downstream send failure reports the same way.\n\n**Code format and branding.** Codes are 4–8 characters (`options.code_size`, default 6), numeric by default; set `options.alphanumeric_code: true` for uppercase letters and digits (the check comparison is case-insensitive). The email template is localized via `options.locale` (54 supported languages) and can use your application's white-label branding via `options.use_white_label_customization`.\n\n**Billing.** One Email Verification API credit per successful send (`status: \"Success\"`), charged at send time. `Retry` and `Undeliverable` sends are free, and checks are free.\n\n**Session persistence.** Every new verification is persisted as an API-type session: `request_id` is a real session id you can pass to `GET /v3/session/{sessionId}/decision/`, the verification appears in the Business Console, and `status.updated` webhooks fire as it progresses.\n\n**Sandbox.** Sandbox API keys skip delivery and billing: after request validation (malformed input still returns `400`), the endpoint returns a static `Success` payload with a random `request_id`; no email is sent and nothing is persisted. Use code `123456` on the sandbox check.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3email_send",
        "tags": [
          "Standalone APIs"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/email/send/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"email\": \"alice@example.com\",\n    \"options\": { \"code_size\": 6, \"locale\": \"en\" },\n    \"vendor_data\": \"user-1234\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/email/send/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"email\": \"alice@example.com\",\n        \"options\": {\"code_size\": 6, \"locale\": \"en\"},\n        \"vendor_data\": \"user-1234\",\n    },\n    timeout=15,\n)\nresp.raise_for_status()\nprint(resp.json())  # {request_id, status, reason, vendor_data, metadata}\n# Then verify with POST /v3/email/check/ using the same email"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/email/send/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    email: 'alice@example.com',\n    options: { code_size: 6, locale: 'en' },\n    vendor_data: 'user-1234',\n  }),\n});\nif (!res.ok) throw new Error(`Email send failed: ${res.status}`);\nconst data = await res.json();\nconsole.log(data); // { request_id, status, reason, vendor_data, metadata }\n// Then verify with POST /v3/email/check/ using the same email"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Recipient email address. Malformed addresses return `400`; syntactically valid addresses that cannot receive mail (failed DNS/MX validation) return `200` with `status: \"Undeliverable\"`.",
                    "example": "alice@example.com"
                  },
                  "options": {
                    "type": "object",
                    "description": "OTP format, localization, and branding options. All fields are optional.",
                    "properties": {
                      "code_size": {
                        "type": "integer",
                        "minimum": 4,
                        "maximum": 8,
                        "default": 6,
                        "description": "Number of characters in the OTP (digits by default; uppercase letters and digits when `alphanumeric_code` is `true`)."
                      },
                      "alphanumeric_code": {
                        "type": "boolean",
                        "default": false,
                        "description": "When `true`, the OTP is composed of uppercase letters `A–Z` and digits `0–9` instead of digits only. The `/v3/email/check/` comparison is case-insensitive."
                      },
                      "locale": {
                        "type": "string",
                        "maxLength": 5,
                        "enum": [
                          "en",
                          "ar",
                          "bn",
                          "bg",
                          "bs",
                          "ca",
                          "cs",
                          "da",
                          "de",
                          "el",
                          "es",
                          "et",
                          "fa",
                          "fi",
                          "fr",
                          "he",
                          "hi",
                          "hr",
                          "hu",
                          "hy",
                          "id",
                          "it",
                          "ja",
                          "ka",
                          "kk",
                          "ko",
                          "ky",
                          "lt",
                          "lv",
                          "cnr",
                          "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"
                        ],
                        "description": "Language of the OTP email template. Defaults to `en`. Unsupported values return `400` listing all supported locales.",
                        "example": "en"
                      },
                      "use_white_label_customization": {
                        "type": "boolean",
                        "default": false,
                        "description": "When `true`, the OTP email uses your application's white-label customization (logo, colors, sender branding) instead of the default Didit template."
                      }
                    }
                  },
                  "signals": {
                    "type": "object",
                    "description": "Optional device and network signals about the end user, forwarded to the anti-fraud layer to improve detection of abusive or automated traffic. All fields are optional.",
                    "properties": {
                      "ip": {
                        "type": "string",
                        "description": "IP address of the end user's device. IPv4 or IPv6.",
                        "example": "192.0.2.1"
                      },
                      "device_id": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Unique identifier of the end user's device. Android: `ANDROID_ID`; iOS: `identifierForVendor`.",
                        "example": "8F0B8FDD-C2CB-4387-B20A-56E9B2E5A0D2"
                      },
                      "device_platform": {
                        "type": "string",
                        "enum": [
                          "android",
                          "ios",
                          "ipados",
                          "tvos",
                          "web"
                        ],
                        "description": "Platform of the end user's device."
                      },
                      "device_model": {
                        "type": "string",
                        "maxLength": 255,
                        "description": "Model of the end user's device.",
                        "example": "iPhone17,2"
                      },
                      "os_version": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Operating-system version of the end user's device.",
                        "example": "18.0.1"
                      },
                      "app_version": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Version of your application.",
                        "example": "1.2.34"
                      },
                      "user_agent": {
                        "type": "string",
                        "maxLength": 512,
                        "description": "User agent of the end user's browser or app.",
                        "example": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1"
                      }
                    }
                  },
                  "vendor_data": {
                    "type": "string",
                    "description": "Optional caller-controlled identifier (your internal user id, an email, a UUID, etc.) persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console. Use it to correlate Didit's `request_id` with your user record."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Optional free-form JSON object persisted on the session and echoed back in the send response, the matching check response, webhooks, and the Business Console."
                  }
                },
                "required": [
                  "email"
                ]
              },
              "examples": {
                "Numeric 6-digit code": {
                  "summary": "Default — 6-digit numeric code in English",
                  "value": {
                    "email": "alice@example.com",
                    "options": {
                      "locale": "en"
                    },
                    "vendor_data": "user-1234"
                  }
                },
                "Alphanumeric 8-character code": {
                  "summary": "Uppercase letters + digits, Spanish template",
                  "value": {
                    "email": "bob@example.com",
                    "options": {
                      "code_size": 8,
                      "alphanumeric_code": true,
                      "locale": "es"
                    }
                  }
                },
                "White-label branding": {
                  "summary": "Send the OTP email with your application's white-label customization",
                  "value": {
                    "email": "carol@example.com",
                    "options": {
                      "locale": "en",
                      "use_white_label_customization": true
                    },
                    "vendor_data": "user-5678"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Send acknowledged. Inspect `status`: `Success` and `Retry` mean a code is on its way; `Undeliverable` means the address cannot receive mail and the verification is already finalized as `Declined`. `request_id` is the persisted session id (same id on a `Retry`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailVerificationSendResponse"
                },
                "examples": {
                  "Success": {
                    "summary": "OTP emailed for a new verification (billed)",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Success",
                      "reason": null,
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  },
                  "Retry": {
                    "summary": "Second send inside the 5-minute window — fresh code, same request_id, free",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Retry",
                      "reason": null,
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  },
                  "Undeliverable": {
                    "summary": "Address failed DNS/MX validation — verification finalized as Declined, nothing billed",
                    "value": {
                      "request_id": "cb1a5011-344d-444d-bdce-218904745d7f",
                      "status": "Undeliverable",
                      "reason": "email_can_not_be_delivered",
                      "vendor_data": "user-1234",
                      "metadata": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — DRF's field-error envelope: one array of messages per offending field, nested under `options` for option errors.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid email": {
                    "summary": "Malformed email address",
                    "value": {
                      "email": [
                        "Enter a valid email address."
                      ]
                    }
                  },
                  "Unsupported locale": {
                    "summary": "`options.locale` outside the supported set",
                    "value": {
                      "options": {
                        "locale": [
                          "Invalid locale. Supported locales are en, ar, bn, bg, bs, ca, cs, da, de, el, es, et, fa, fi, fr, he, hi, hr, hu, hy, id, it, ja, ka, kk, ko, ky, lt, lv, cnr, 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."
                        ]
                      }
                    }
                  },
                  "Code size out of range": {
                    "summary": "`options.code_size` outside 4–8",
                    "value": {
                      "options": {
                        "code_size": [
                          "Ensure this value is less than or equal to 8."
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment (`{\"detail\": ...}`) — this API never returns `401`. Also returned before any send when the organization's balance cannot cover one Email Verification API credit (`{\"error\": ...}`).",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  },
                  "Not enough credits": {
                    "summary": "Organization balance cannot cover the send price",
                    "value": {
                      "error": "You don't have enough credits to perform this request."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/email/check/": {
      "post": {
        "summary": "Check Email Code",
        "description": "Verify the OTP delivered by [`POST /v3/email/send/`](/standalone-apis/email-send) and get the final verification result plus email intelligence: deliverability, breach exposure (`is_breached` and the `breaches` list from a breach-intelligence database), disposable-provider detection, and duplicate usage of the address across your sessions.\n\n**How the check finds the verification.** Matching is by your application plus the `email` address — `request_id` is not an input. The most recent pending verification created within the last **5 minutes** is checked. If there is none (never sent, already finalized, undeliverable at send time, or older than 5 minutes) the endpoint returns `200` with `status: \"Expired or Not Found\"`.\n\n**Attempt budget.** Each verification allows **3 code attempts**. The first two wrong codes return `status: \"Failed\"` with the attempts remaining and `email: null`; the third wrong code finalizes the verification as `Declined` with an `EMAIL_CODE_ATTEMPTS_EXCEEDED` warning and returns the full email report. Codes are compared case-insensitively (relevant for `alphanumeric_code` sends); only the most recently sent code is valid.\n\n**Outcomes.** `Approved` — correct code and no declining risk. `Declined` — terminal: the code was correct but a declining risk matched (a `DECLINE` action below, a blocklisted address, or the address found undeliverable at finalization), or the attempt budget was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — nothing to check. On `Approved`/`Declined` the `request_id` equals the send's `request_id` (the session id) and `email` carries the full report (`is_breached`, `breaches`, `is_disposable`, `is_undeliverable`, `warnings`, `lifecycle`, `matches`); on `Failed` and `Expired or Not Found` the `request_id` is a one-off random UUID.\n\n**Risk actions.** `duplicated_email_action`, `breached_email_action`, and `disposable_email_action` decide what happens when the corresponding risk is detected on a correct code: `DECLINE` flips the final status to `Declined`; `NO_ACTION` (default) records the risk in `email.warnings` without affecting the status.\n\n**Billing.** Checks are free — the credit is consumed by the successful send.\n\n**Session persistence.** A finalized check updates the session created by the send (visible in the Business Console, queryable via `GET /v3/session/{sessionId}/decision/`) and fires a `status.updated` webhook.\n\n**Sandbox.** Sandbox API keys skip all processing: code `123456` returns a static `Approved` payload with a simplified `email` object (`status`, `email`, `is_breached`, `is_disposable`, `is_undeliverable`); any other code returns `Failed`. Nothing is persisted.\n\n**Authentication.** Send your application's API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit.** Shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.",
        "operationId": "post_v3email_check",
        "tags": [
          "Standalone APIs"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/email/check/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"email\": \"alice@example.com\",\n    \"code\": \"123456\",\n    \"disposable_email_action\": \"DECLINE\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os, requests\n\nresp = requests.post(\n    \"https://verification.didit.me/v3/email/check/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"email\": \"alice@example.com\",  # same address as the send call\n        \"code\": \"123456\",\n        \"disposable_email_action\": \"DECLINE\",\n    },\n    timeout=15,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result[\"status\"])  # Approved / Declined / Failed / Expired or Not Found\nif result[\"status\"] in (\"Approved\", \"Declined\"):\n    print(result[\"email\"][\"is_breached\"], result[\"email\"][\"is_disposable\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const res = await fetch('https://verification.didit.me/v3/email/check/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    email: 'alice@example.com', // same address as the send call\n    code: '123456',\n    disposable_email_action: 'DECLINE',\n  }),\n});\nconst data = await res.json();\nif (data.status === 'Approved') {\n  // full email report is in data.email (breaches, is_disposable, matches, ...)\n}"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "The same email address used in the matching [`POST /v3/email/send/`](/standalone-apis/email-send) call. This is what links the check to the send.",
                    "example": "alice@example.com"
                  },
                  "code": {
                    "type": "string",
                    "maxLength": 10,
                    "description": "The OTP the end user received: 4–8 digits, or 4–8 letters/digits when `alphanumeric_code: true` was used at send time. Comparison is case-insensitive.",
                    "example": "123456"
                  },
                  "duplicated_email_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the same address was already used and approved by a different user (only previously Approved verifications count) (different `vendor_data`) of your application. `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings` and fills `email.matches`."
                  },
                  "breached_email_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the address appears in known data breaches (`email.is_breached`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`."
                  },
                  "disposable_email_action": {
                    "type": "string",
                    "enum": [
                      "NO_ACTION",
                      "DECLINE"
                    ],
                    "default": "NO_ACTION",
                    "description": "What to do when the domain belongs to a disposable/temporary-mail provider (`email.is_disposable`). `DECLINE` flips the final `status` to `Declined`; `NO_ACTION` records the risk in `email.warnings`."
                  }
                },
                "required": [
                  "email",
                  "code"
                ]
              },
              "examples": {
                "Default": {
                  "summary": "Record any risks but never auto-decline",
                  "value": {
                    "email": "alice@example.com",
                    "code": "123456"
                  }
                },
                "Strict risk policy": {
                  "summary": "Auto-decline disposable and breached inboxes",
                  "value": {
                    "email": "alice@example.com",
                    "code": "123456",
                    "disposable_email_action": "DECLINE",
                    "breached_email_action": "DECLINE"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Check completed — wrong codes and missing verifications also return `200`; inspect `status`, not the HTTP code. `email` is populated only on finalized outcomes (`Approved`/`Declined`), `null` on `Failed`, and absent on `Expired or Not Found`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailVerificationCheckResponse"
                },
                "examples": {
                  "Approved": {
                    "summary": "Correct code — clean address, full email report",
                    "value": {
                      "request_id": "e39cb057-92fc-4b59-b84e-02fec29a0f24",
                      "status": "Approved",
                      "message": "The verification code is correct.",
                      "email": {
                        "status": "Approved",
                        "email": "alice@example.com",
                        "is_breached": false,
                        "breaches": [],
                        "is_disposable": false,
                        "is_undeliverable": false,
                        "verification_attempts": 1,
                        "verified_at": "2026-06-12T01:24:47.311323Z",
                        "warnings": [],
                        "lifecycle": [
                          {
                            "type": "EMAIL_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null
                            },
                            "fee": 0.03
                          },
                          {
                            "type": "VALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "123456",
                              "status": "Approved"
                            },
                            "fee": 0
                          },
                          {
                            "type": "EMAIL_VERIFICATION_APPROVED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": null,
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Approved with breach exposure": {
                    "summary": "Correct code; breach exposure recorded as a warning (breached_email_action=NO_ACTION)",
                    "value": {
                      "request_id": "3a1f2b6c-9d4e-4f7a-8b2c-1e5d6f7a8b9c",
                      "status": "Approved",
                      "message": "The verification code is correct.",
                      "email": {
                        "status": "Approved",
                        "email": "bob@example.com",
                        "is_breached": true,
                        "breaches": [
                          {
                            "name": "ExampleApp",
                            "domain": "exampleapp.com",
                            "breach_date": "2019-03-21",
                            "breach_emails_count": 763117,
                            "description": "In March 2019, the social platform ExampleApp suffered a data breach that exposed email addresses and passwords.",
                            "logo_path": "https://<media-host>/logos/ExampleApp.png",
                            "data_classes": [
                              "email_addresses",
                              "passwords"
                            ],
                            "is_verified": true
                          }
                        ],
                        "is_disposable": false,
                        "is_undeliverable": false,
                        "verification_attempts": 1,
                        "verified_at": "2026-06-12T01:24:47.311323Z",
                        "warnings": [
                          {
                            "feature": "EMAIL",
                            "risk": "BREACHED_EMAIL_DETECTED",
                            "additional_data": null,
                            "log_type": "information",
                            "short_description": "Breached email detected",
                            "long_description": "This email address was found in one or more known data breaches."
                          }
                        ],
                        "lifecycle": [
                          {
                            "type": "EMAIL_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null
                            },
                            "fee": 0.03
                          },
                          {
                            "type": "VALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "835126",
                              "status": "Approved"
                            },
                            "fee": 0
                          },
                          {
                            "type": "EMAIL_VERIFICATION_APPROVED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": null,
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-5678",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Failed (attempts remaining)": {
                    "summary": "Wrong code — the user can retry; request_id here is a one-off UUID",
                    "value": {
                      "request_id": "11111111-2222-3333-4444-555555555555",
                      "status": "Failed",
                      "message": "The verification code is incorrect. Attempts remaining: 2",
                      "email": null,
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:21.512340+00:00"
                    }
                  },
                  "Declined (risk policy)": {
                    "summary": "Correct code, but the address is disposable and disposable_email_action=DECLINE",
                    "value": {
                      "request_id": "8c2d3e4f-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                      "status": "Declined",
                      "message": "The verification code is correct.",
                      "email": {
                        "status": "Declined",
                        "email": "tempuser42@mailinator.com",
                        "is_breached": false,
                        "breaches": [],
                        "is_disposable": true,
                        "is_undeliverable": false,
                        "verification_attempts": 1,
                        "verified_at": "2026-06-12T01:24:47.311323Z",
                        "warnings": [
                          {
                            "feature": "EMAIL",
                            "risk": "DISPOSABLE_EMAIL_DETECTED",
                            "additional_data": null,
                            "log_type": "error",
                            "short_description": "Disposable email detected",
                            "long_description": "The system detected that the email is disposable, which is not allowed."
                          }
                        ],
                        "lifecycle": [
                          {
                            "type": "EMAIL_VERIFICATION_MESSAGE_SENT",
                            "timestamp": "2026-06-12T01:23:39.580554+00:00",
                            "details": {
                              "status": "Success",
                              "reason": null
                            },
                            "fee": 0.03
                          },
                          {
                            "type": "VALID_CODE_ENTERED",
                            "timestamp": "2026-06-12T01:24:47.311201+00:00",
                            "details": {
                              "code_tried": "123456",
                              "status": "Approved"
                            },
                            "fee": 0
                          },
                          {
                            "type": "EMAIL_VERIFICATION_DECLINED",
                            "timestamp": "2026-06-12T01:24:47.384292+00:00",
                            "details": {
                              "reason": "DISPOSABLE_EMAIL_DETECTED"
                            },
                            "fee": 0
                          }
                        ],
                        "matches": []
                      },
                      "vendor_data": "user-1234",
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:47.401719+00:00"
                    }
                  },
                  "Expired or Not Found": {
                    "summary": "No pending verification for this address in the last 5 minutes",
                    "value": {
                      "request_id": "88808a77-06e9-4cb0-85f5-9c052ddfd987",
                      "status": "Expired or Not Found",
                      "message": "No pending email verification found in the last 5 minutes.",
                      "vendor_data": null,
                      "metadata": null,
                      "created_at": "2026-06-12T01:24:59.887904+00:00"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error — DRF's field-error envelope: one array of messages per offending field.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid email": {
                    "summary": "Malformed email address",
                    "value": {
                      "email": [
                        "Enter a valid email address."
                      ]
                    }
                  },
                  "Missing code": {
                    "summary": "`code` not included in the body",
                    "value": {
                      "code": [
                        "This field is required."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Permission denied. Returned when the `x-api-key` header is missing, malformed, revoked, or belongs to another environment — this API never returns `401`. Checks are free, so there is no credit check here.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No `x-api-key` header, or the key is invalid/revoked",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. All POST/PATCH/DELETE endpoints share a budget of 300 write requests per minute per API key. The response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers.",
            "content": {
              "application/json": {
                "examples": {
                  "Write rate limit exceeded": {
                    "summary": "More than 300 write requests in the current minute",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                },
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/workflows/": {
      "get": {
        "summary": "List workflows",
        "description": "List your workflows, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `workflow_id` is the stable group ID and new sessions use the latest `published` version.",
        "operationId": "list_workflows",
        "tags": [
          "Workflows"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            },
            "description": "Page size (number of workflows per page)."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of rows to skip before the first returned workflow."
          }
        ],
        "responses": {
          "200": {
            "description": "One page of workflows ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `WorkflowListItem`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Total number of workflow versions for the application."
                    },
                    "next": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "URL of the previous page, or `null` on the first page."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WorkflowListItem"
                      }
                    }
                  }
                },
                "examples": {
                  "Two workflows": {
                    "summary": "A default KYC workflow and a draft KYC + AML workflow",
                    "value": {
                      "count": 2,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
                          "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111",
                          "workflow_label": "Standard KYC",
                          "workflow_type": "kyc",
                          "is_default": true,
                          "is_archived": false,
                          "is_white_label_enabled": false,
                          "total_price": 0.15,
                          "min_price": 0.15,
                          "max_price": 0.15,
                          "features": "OCR + LIVENESS + FACE_MATCH",
                          "is_simple_workflow": true,
                          "is_editable": false,
                          "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ",
                          "max_retry_attempts": 3,
                          "retry_window_days": 7,
                          "session_expiration_time": 604800,
                          "version": 2,
                          "status": "published",
                          "has_draft": false,
                          "created_by": {
                            "name": "Alex Mateo",
                            "email": "alex@example.com",
                            "type": "user"
                          },
                          "updated_at": "2026-04-12T10:30:00Z"
                        },
                        {
                          "uuid": "b2c3d4e5-6789-01bc-defa-222222222222",
                          "workflow_id": "b2c3d4e5-6789-01bc-defa-222222222222",
                          "workflow_label": "Full Verification + AML",
                          "workflow_type": "kyc",
                          "is_default": false,
                          "is_archived": false,
                          "is_white_label_enabled": false,
                          "total_price": 0.25,
                          "min_price": 0.25,
                          "max_price": 0.25,
                          "features": "OCR + LIVENESS + FACE_MATCH + AML",
                          "is_simple_workflow": true,
                          "is_editable": true,
                          "workflow_url": "https://verify.didit.me/u/Aq3kPOx8RWG3UAz5ZRQGVQ",
                          "max_retry_attempts": 3,
                          "retry_window_days": 7,
                          "session_expiration_time": 604800,
                          "version": 1,
                          "status": "draft",
                          "has_draft": true,
                          "created_by": null,
                          "updated_at": "2026-04-15T18:05:21Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The API key is missing, malformed, expired, or belongs to a different application. Body: `{\"detail\": \"You do not have permission to perform this action.\"}`. Refresh the token via the [Auth API](/auth-api/get-credentials) and retry.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff and honour any `Retry-After` header."
          },
          "500": {
            "description": "Unexpected server error while building the list. The request can be safely retried."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET 'https://verification.didit.me/v3/workflows/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Accept: application/json'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.get(\n    \"https://verification.didit.me/v3/workflows/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor workflow in page[\"results\"]:\n    print(workflow[\"uuid\"], workflow[\"workflow_label\"], workflow[\"status\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((w) => console.log(w.uuid, w.workflow_label, w.status));"
          }
        ]
      },
      "post": {
        "summary": "Create workflow",
        "description": "Create a simple workflow. Send `features` in execution order (ordering rules apply); defaults to `published` v1. The body accepts ONLY the fields listed in `SimpleWorkflowRequest` — any other key (including `workflow_type`) is rejected with a 400. Not idempotent — each call creates a new workflow. Maximum 50 workflows per application.",
        "operationId": "create_workflow",
        "tags": [
          "Workflows"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimpleWorkflowRequest"
              },
              "examples": {
                "Simple KYC": {
                  "summary": "Linear ID, liveness, and face match workflow",
                  "value": {
                    "workflow_label": "Standard KYC",
                    "features": [
                      {
                        "feature": "OCR",
                        "config": {
                          "duplicated_user_action": "review"
                        }
                      },
                      {
                        "feature": "LIVENESS",
                        "config": {
                          "face_liveness_method": "passive"
                        }
                      },
                      {
                        "feature": "FACE_MATCH",
                        "config": {
                          "face_match_score_decline_threshold": 40,
                          "face_match_score_review_threshold": 60
                        }
                      }
                    ]
                  }
                },
                "KYC + AML": {
                  "summary": "Run AML after ID verification",
                  "value": {
                    "workflow_label": "KYC with AML",
                    "features": [
                      {
                        "feature": "OCR"
                      },
                      {
                        "feature": "AML",
                        "config": {
                          "aml_score_review_threshold": 70,
                          "aml_score_approve_threshold": 30
                        }
                      }
                    ]
                  }
                },
                "Questionnaire workflow": {
                  "summary": "Run a questionnaire created with POST /v3/questionnaires/",
                  "value": {
                    "workflow_label": "Source of Funds",
                    "features": [
                      {
                        "feature": "QUESTIONNAIRE",
                        "config": {
                          "questionnaire_uuid": "11111111-2222-3333-4444-555555555555",
                          "review_questionnaire_manually": true
                        }
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Workflow created. The response body is the FULL workflow configuration — the same raw `VerificationSettings` serialization returned by `GET /v3/workflows/{settings_uuid}/` (~190 fields: every feature toggle, threshold, action and the generated `workflow_graph`), not the compact list item. Store the `uuid` (also exposed as `workflow_id`) so you can reference it from later session-creation calls. Note: `features` is a `\" + \"`-joined string and prices are JSON numbers.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Full workflow configuration (abbreviated here — see `GET /v3/workflows/{settings_uuid}/`).",
                  "additionalProperties": true
                },
                "examples": {
                  "Created (abbreviated)": {
                    "summary": "Key fields of the full configuration object",
                    "value": {
                      "uuid": "c3d4e5f6-7890-12cd-ef34-333333333333",
                      "workflow_id": "c3d4e5f6-7890-12cd-ef34-333333333333",
                      "workflow_label": "Standard KYC",
                      "workflow_type": null,
                      "is_default": false,
                      "is_archived": false,
                      "total_price": 0.15,
                      "min_price": 0.15,
                      "max_price": 0.15,
                      "features": "OCR + LIVENESS + FACE_MATCH",
                      "is_simple_workflow": false,
                      "is_editable": false,
                      "workflow_url": "https://verify.didit.me/u/w9TkPOx8RWG3UAz5ZRQGVQ",
                      "workflow_graph": {
                        "start_node": "ocr",
                        "nodes": "... one node per feature plus a final status node ..."
                      },
                      "max_retry_attempts": 3,
                      "retry_window_days": 7,
                      "session_expiration_time": 604800,
                      "version": 1,
                      "status": "published",
                      "published_at": "2026-05-17T10:30:00Z",
                      "created_at": "2026-05-17T10:30:00Z",
                      "updated_at": "2026-05-17T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Common causes: a top-level key outside the accepted whitelist (e.g. `workflow_type`), an unknown `feature` value, a feature placed before its dependency (e.g., `FACE_MATCH` before `OCR`), a passive feature (`AML`, `IP_ANALYSIS`, `DATABASE_VALIDATION`, `KYB_KEY_PEOPLE`) used as the first step, a `QUESTIONNAIRE` feature whose `config.questionnaire_uuid` does not exist for this application, or an out-of-range threshold. Body is a DRF validation error object — keys are field names, values are arrays of human-readable messages.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing feature": {
                    "value": {
                      "features": [
                        "At least one feature is required."
                      ]
                    }
                  },
                  "Unknown top-level field": {
                    "value": {
                      "workflow_type": [
                        "The v3 workflow API only accepts `workflow_label`, `is_default`, `status`, `features`, `is_white_label_enabled`, `is_desktop_allowed`, `max_retry_attempts`, `retry_window_days`, `session_expiration_time`, `face_liveness_max_attempts`, `face_match_max_attempts`."
                      ]
                    }
                  },
                  "Unknown feature": {
                    "value": {
                      "features": [
                        "Invalid feature 'FOO'. Allowed values: AGE_ESTIMATION, AML, DATABASE_VALIDATION, EMAIL_VERIFICATION, FACE_MATCH, IP_ANALYSIS, KYB_DOCUMENTS, KYB_KEY_PEOPLE, KYB_REGISTRY, LIVENESS, NFC, OCR, PHONE_VERIFICATION, PROOF_OF_ADDRESS, QUESTIONNAIRE."
                      ]
                    }
                  },
                  "Ordering": {
                    "value": {
                      "features": [
                        "Feature 'FACE_MATCH' must appear after one of: OCR."
                      ]
                    }
                  },
                  "Questionnaire not found": {
                    "value": {
                      "features": "Questionnaire '11111111-2222-3333-4444-555555555555' was not found for this application. Create the questionnaire first, then use its questionnaire_id."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The API key is missing, malformed, expired, or scoped to a different application. Refresh via the [Auth API](/auth-api/get-credentials) and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body."
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error while creating the workflow. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/workflows/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"workflow_label\": \"Standard KYC\",\n    \"features\": [\n      {\"feature\": \"OCR\"},\n      {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n      {\"feature\": \"FACE_MATCH\", \"config\": {\"face_match_score_decline_threshold\": 40, \"face_match_score_review_threshold\": 60}}\n    ]\n  }'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\npayload = {\n    \"workflow_label\": \"Standard KYC\",\n    \"features\": [\n        {\"feature\": \"OCR\"},\n        {\"feature\": \"LIVENESS\", \"config\": {\"face_liveness_method\": \"passive\"}},\n        {\"feature\": \"FACE_MATCH\"},\n    ],\n}\nresp = requests.post(\n    \"https://verification.didit.me/v3/workflows/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    json=payload,\n    timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(\"Workflow id:\", workflow[\"uuid\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(\"https://verification.didit.me/v3/workflows/\", {\n  method: \"POST\",\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    workflow_label: \"Standard KYC\",\n    features: [\n      { feature: \"OCR\" },\n      { feature: \"LIVENESS\", config: { face_liveness_method: \"passive\" } },\n      { feature: \"FACE_MATCH\" },\n    ],\n  }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst workflow = await res.json();\nconsole.log(\"Workflow id:\", workflow.uuid);"
          }
        ]
      }
    },
    "/v3/workflows/{settings_uuid}/": {
      "get": {
        "summary": "Get workflow",
        "description": "Fetch full configuration for one workflow version by `settings_uuid`: feature toggles, thresholds, allowed documents, graph, retry policy, callbacks, and pricing.",
        "operationId": "get_workflow",
        "tags": [
          "Workflows"
        ],
        "parameters": [
          {
            "name": "settings_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the workflow (the `uuid` value returned by the list endpoint). Each version of a workflow has its own `settings_uuid`."
          }
        ],
        "responses": {
          "200": {
            "description": "Full workflow configuration. The shape is the raw `VerificationSettings` serialization (~190 fields: every feature toggle, threshold, warning action, allowed-documents config, the `workflow_graph`, retry policy, callbacks and pricing). The example below is abbreviated. Notes: `features` is a `\" + \"`-joined string (e.g. `\"OCR + LIVENESS\"`), prices are JSON numbers, and `is_editable` is `true` only for `draft` versions.",
            "content": {
              "application/json": {
                "examples": {
                  "KYC workflow with face match (abbreviated)": {
                    "value": {
                      "uuid": "a1b2c3d4-5678-90ab-cdef-111111111111",
                      "workflow_id": "a1b2c3d4-5678-90ab-cdef-111111111111",
                      "workflow_label": "Standard KYC",
                      "workflow_type": "kyc",
                      "is_default": true,
                      "is_archived": false,
                      "is_liveness_enabled": true,
                      "face_liveness_method": "passive",
                      "face_liveness_score_decline_threshold": 50,
                      "face_liveness_score_review_threshold": 70,
                      "is_face_match_enabled": true,
                      "face_match_score_decline_threshold": 40,
                      "face_match_score_review_threshold": 60,
                      "is_aml_enabled": false,
                      "is_phone_verification_enabled": false,
                      "is_email_verification_enabled": false,
                      "is_database_validation_enabled": false,
                      "is_ip_analysis_enabled": false,
                      "is_nfc_enabled": false,
                      "duplicated_user_action": "no_action",
                      "documents_allowed": {
                        "ESP": {
                          "P": {
                            "sides": 1,
                            "enabled": 1,
                            "subtypes": [
                              "EPASSPORT",
                              "PASSPORT_GENERIC"
                            ],
                            "preferred_characters": "latin",
                            "expiration_check_mode": "strict"
                          }
                        }
                      },
                      "max_retry_attempts": 3,
                      "retry_window_days": 7,
                      "session_expiration_time": 604800,
                      "version": 2,
                      "status": "published",
                      "published_at": "2026-04-12T10:30:00Z",
                      "is_simple_workflow": false,
                      "is_editable": false,
                      "workflow_url": "https://verify.didit.me/u/_yljPOx8RWG3UAz5ZRQGVQ",
                      "total_price": 0.15,
                      "min_price": 0.15,
                      "max_price": 0.15,
                      "features": "OCR + LIVENESS + FACE_MATCH",
                      "created_at": "2025-01-15T10:30:00Z",
                      "updated_at": "2026-04-12T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key is missing, malformed, expired, or scoped to a different application.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No workflow with this `settings_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — Didit deliberately does not distinguish the two cases to avoid leaking the existence of cross-application records.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.get(\n    f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()\nworkflow = resp.json()\nprint(workflow[\"workflow_label\"], workflow[\"status\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label, workflow.status);"
          }
        ]
      },
      "patch": {
        "summary": "Update workflow",
        "description": "Update a workflow. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `uuid`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `workflow_id` (new `uuid`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`). The body accepts the same strict field whitelist as create — unknown keys are rejected with 400.",
        "operationId": "update_workflow",
        "tags": [
          "Workflows"
        ],
        "parameters": [
          {
            "name": "settings_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the workflow to update."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimpleWorkflowUpdateRequest"
              },
              "examples": {
                "Replace features": {
                  "summary": "Send the full desired feature order",
                  "value": {
                    "features": [
                      {
                        "feature": "OCR"
                      },
                      {
                        "feature": "AML"
                      }
                    ]
                  }
                },
                "Rename": {
                  "summary": "Rename a workflow",
                  "value": {
                    "workflow_label": "Premium KYC"
                  }
                },
                "Set as default": {
                  "summary": "Make this workflow the application's default",
                  "value": {
                    "is_default": true
                  }
                },
                "Update config": {
                  "summary": "Change feature configuration",
                  "value": {
                    "features": [
                      {
                        "feature": "OCR"
                      },
                      {
                        "feature": "LIVENESS",
                        "config": {
                          "face_liveness_method": "passive"
                        }
                      }
                    ]
                  }
                },
                "Publish a draft": {
                  "summary": "Promote a draft to published",
                  "value": {
                    "status": "published"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Workflow updated. The body is the FULL configuration of the resulting version (same raw `VerificationSettings` shape as `GET /v3/workflows/{settings_uuid}/`). When the update created a new version (`status: \"published\"` sent on a published row) the returned `uuid` and `version` differ from the ones you patched — use the returned `uuid` for subsequent calls.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Full workflow configuration (see `GET /v3/workflows/{settings_uuid}/`).",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Validation error — a top-level key outside the accepted whitelist, an unknown feature, invalid ordering, out-of-range threshold, referencing a non-existent questionnaire, or `status: \"draft\"` sent for a published version (`\"Cannot revert a published version to draft.\"`). The response body is a DRF validation error keyed by field name."
          },
          "403": {
            "description": "API key missing or invalid.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No workflow with this `settings_uuid` exists for the authenticated application."
          },
          "415": {
            "description": "Unsupported media type. Set `Content-Type: application/json`."
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"workflow_label\": \"Premium KYC\"}'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.patch(\n    f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    json={\"workflow_label\": \"Premium KYC\"},\n    timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"workflow_label\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n  method: \"PATCH\",\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({ workflow_label: \"Premium KYC\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst workflow = await res.json();\nconsole.log(workflow.workflow_label);"
          }
        ]
      },
      "delete": {
        "summary": "Delete workflow",
        "description": "Permanently delete one workflow version (hard delete, no undo). Existing sessions keep their verification results but their workflow reference is cleared; other versions in the same `workflow_id` group are not deleted.",
        "operationId": "delete_workflow",
        "tags": [
          "Workflows"
        ],
        "parameters": [
          {
            "name": "settings_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the workflow to delete."
          }
        ],
        "responses": {
          "204": {
            "description": "Workflow deleted. The response body is empty."
          },
          "403": {
            "description": "API key missing or invalid.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No workflow with this `settings_uuid` exists for the authenticated application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/workflows/a1b2c3d4-5678-90ab-cdef-111111111111/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.delete(\n    f\"https://verification.didit.me/v3/workflows/{settings_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()  # raises on 4xx/5xx; 204 is success"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/workflows/${settingsUuid}/`, {\n  method: \"DELETE\",\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);"
          }
        ]
      }
    },
    "/v3/questionnaires/": {
      "get": {
        "summary": "List questionnaires",
        "description": "List your questionnaires, newest first, in a limit/offset pagination envelope (`{count, next, previous, results}`, default page size 50). Each row is one version; `questionnaire_group_id` is the stable group ID resolved to the latest `published` version.",
        "operationId": "list_questionnaires",
        "tags": [
          "Questionnaires"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            },
            "description": "Page size (number of questionnaires per page)."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0
            },
            "description": "Number of rows to skip before the first returned questionnaire."
          }
        ],
        "responses": {
          "200": {
            "description": "One page of questionnaires ordered by `created_at` descending, wrapped in the pagination envelope. Each item in `results` is a `QuestionnaireListItem`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Total number of questionnaire versions for the application."
                    },
                    "next": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "URL of the previous page, or `null` on the first page."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/QuestionnaireListItem"
                      }
                    }
                  }
                },
                "examples": {
                  "One questionnaire": {
                    "summary": "A simple bilingual questionnaire",
                    "value": {
                      "count": 1,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "uuid": "11111111-2222-3333-4444-555555555555",
                          "title": "KYC Questionnaire",
                          "description": "Additional verification questions",
                          "languages": [
                            "en",
                            "es"
                          ],
                          "default_language": "en",
                          "is_active": true,
                          "is_simple_questionnaire": true,
                          "created_at": "2026-04-23T09:55:00Z",
                          "updated_at": "2026-04-23T10:00:00Z",
                          "question_types": [
                            "SHORT_TEXT",
                            "MULTIPLE_CHOICE",
                            "FILE_UPLOAD"
                          ],
                          "workflow_names": [],
                          "questionnaire_group_id": "11111111-2222-3333-4444-555555555555",
                          "version": 1,
                          "status": "published",
                          "published_at": "2026-04-23T10:00:00Z",
                          "is_editable": false
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error while building the list. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.get(\n    \"https://verification.didit.me/v3/questionnaires/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor q in page[\"results\"]:\n    print(q[\"uuid\"], q[\"title\"], q[\"status\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst page = await res.json();\npage.results.forEach((q) => console.log(q.uuid, q.title, q.status));"
          }
        ]
      },
      "post": {
        "summary": "Create questionnaire",
        "description": "Create a simple linear questionnaire. Send `form_elements` in display order; branching and repeatable groups require the Console. Defaults to `published` v1.",
        "operationId": "create_questionnaire",
        "tags": [
          "Questionnaires"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimpleQuestionnaireRequest"
              },
              "examples": {
                "Simple": {
                  "summary": "Basic questionnaire",
                  "value": {
                    "title": "Customer Onboarding",
                    "description": "Additional information needed for verification",
                    "default_language": "en",
                    "languages": [
                      "en"
                    ],
                    "is_active": true,
                    "form_elements": [
                      {
                        "id": "occupation",
                        "element_type": "short_text",
                        "label": {
                          "en": "What is your occupation?"
                        },
                        "is_required": true,
                        "placeholder": {
                          "en": "Software engineer"
                        }
                      },
                      {
                        "id": "source_of_funds",
                        "element_type": "multiple_choice",
                        "label": {
                          "en": "Source of funds"
                        },
                        "is_required": true,
                        "options": [
                          {
                            "value": "employment",
                            "label": {
                              "en": "Employment"
                            }
                          },
                          {
                            "value": "business",
                            "label": {
                              "en": "Business"
                            }
                          },
                          {
                            "value": "investments",
                            "label": {
                              "en": "Investments"
                            }
                          },
                          {
                            "value": "other",
                            "label": {
                              "en": "Other"
                            }
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Questionnaire created. The response includes the `questionnaire_id` — store it to reference this questionnaire from workflows or future update/delete calls.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuestionnaireDetail"
                },
                "examples": {
                  "Created": {
                    "value": {
                      "questionnaire_id": "11111111-2222-3333-4444-555555555555",
                      "title": "Customer Onboarding",
                      "description": "Additional information needed for verification",
                      "languages": [
                        "en"
                      ],
                      "default_language": "en",
                      "is_active": true,
                      "is_simple_questionnaire": true,
                      "graph": {
                        "start_node": "occupation",
                        "nodes": {
                          "occupation": {
                            "element_type": "SHORT_TEXT",
                            "is_required": true,
                            "title": {
                              "en": "What is your occupation?"
                            },
                            "next": "source_of_funds"
                          },
                          "source_of_funds": {
                            "element_type": "MULTIPLE_CHOICE",
                            "is_required": true,
                            "title": {
                              "en": "Source of funds"
                            },
                            "choices": [
                              {
                                "value": "employment",
                                "label": {
                                  "en": "Employment"
                                }
                              },
                              {
                                "value": "business",
                                "label": {
                                  "en": "Business"
                                }
                              }
                            ],
                            "next": null
                          }
                        }
                      },
                      "sections": [],
                      "questionnaire_group_id": "11111111-2222-3333-4444-555555555555",
                      "version": 1,
                      "status": "published",
                      "published_at": "2026-04-23T10:00:00Z",
                      "is_editable": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Common causes: an unknown `element_type`, a translatable field missing a translation for one of `languages`, `languages` missing `en`, `default_language` not in `languages`, an option without a `label`, a question that has `branches`/`required_if`/`repeatable_config` (not supported by this endpoint), or sending a raw `graph` instead of `form_elements`. The body is a DRF validation error keyed by field name.",
            "content": {
              "application/json": {
                "examples": {
                  "Graph not accepted": {
                    "value": {
                      "graph": "The v3 questionnaire API only accepts simple `form_elements`, not `graph`."
                    }
                  },
                  "Missing label": {
                    "value": {
                      "form_elements": [
                        {
                          "label": [
                            "This field is required."
                          ]
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "415": {
            "description": "Unsupported media type. Set `Content-Type: application/json` and send a JSON body."
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error while creating the questionnaire. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X POST 'https://verification.didit.me/v3/questionnaires/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"title\": \"Customer Onboarding\",\n    \"default_language\": \"en\",\n    \"languages\": [\"en\"],\n    \"form_elements\": [\n      {\n        \"id\": \"occupation\",\n        \"element_type\": \"short_text\",\n        \"label\": {\"en\": \"What is your occupation?\"},\n        \"is_required\": true\n      }\n    ]\n  }'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\npayload = {\n    \"title\": \"Customer Onboarding\",\n    \"default_language\": \"en\",\n    \"languages\": [\"en\"],\n    \"form_elements\": [\n        {\n            \"id\": \"occupation\",\n            \"element_type\": \"short_text\",\n            \"label\": {\"en\": \"What is your occupation?\"},\n            \"is_required\": True,\n        },\n    ],\n}\nresp = requests.post(\n    \"https://verification.didit.me/v3/questionnaires/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    json=payload,\n    timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(\"Questionnaire id:\", questionnaire[\"questionnaire_id\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(\"https://verification.didit.me/v3/questionnaires/\", {\n  method: \"POST\",\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    title: \"Customer Onboarding\",\n    default_language: \"en\",\n    languages: [\"en\"],\n    form_elements: [\n      {\n        id: \"occupation\",\n        element_type: \"short_text\",\n        label: { en: \"What is your occupation?\" },\n        is_required: true,\n      },\n    ],\n  }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconst questionnaire = await res.json();\nconsole.log(\"Questionnaire id:\", questionnaire.questionnaire_id);"
          }
        ]
      }
    },
    "/v3/questionnaires/{questionnaire_uuid}/": {
      "get": {
        "summary": "Get questionnaire",
        "description": "Fetch full questionnaire detail for one version by `questionnaire_uuid`: title, `languages`, canonical `graph`, derived `sections`, and version metadata.",
        "operationId": "get_questionnaire",
        "tags": [
          "Questionnaires"
        ],
        "parameters": [
          {
            "name": "questionnaire_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the questionnaire (the `uuid` value returned by the list endpoint, also returned as `questionnaire_id` on detail endpoints)."
          }
        ],
        "responses": {
          "200": {
            "description": "Full questionnaire detail including the canonical `graph`, the derived `sections` view, version metadata, and `is_editable`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuestionnaireDetail"
                },
                "examples": {
                  "Published questionnaire": {
                    "value": {
                      "questionnaire_id": "11111111-2222-3333-4444-555555555555",
                      "title": "Customer Onboarding",
                      "description": "Additional information needed for verification",
                      "languages": [
                        "en"
                      ],
                      "default_language": "en",
                      "is_active": true,
                      "is_simple_questionnaire": true,
                      "graph": {
                        "start_node": "occupation",
                        "nodes": {
                          "occupation": {
                            "element_type": "SHORT_TEXT",
                            "is_required": true,
                            "title": {
                              "en": "What is your occupation?"
                            },
                            "next": "source_of_funds"
                          },
                          "source_of_funds": {
                            "element_type": "MULTIPLE_CHOICE",
                            "is_required": true,
                            "title": {
                              "en": "Source of funds"
                            },
                            "choices": [
                              {
                                "value": "employment",
                                "label": {
                                  "en": "Employment"
                                }
                              },
                              {
                                "value": "business",
                                "label": {
                                  "en": "Business"
                                }
                              }
                            ],
                            "next": null
                          }
                        }
                      },
                      "sections": [
                        {
                          "title": {},
                          "description": {},
                          "items": [
                            {
                              "uuid": "11111111-2222-3333-4444-666666666666",
                              "value": "occupation",
                              "element_type": "SHORT_TEXT",
                              "is_required": true,
                              "title": {
                                "en": "What is your occupation?"
                              }
                            }
                          ]
                        }
                      ],
                      "questionnaire_group_id": "11111111-2222-3333-4444-555555555555",
                      "version": 1,
                      "status": "published",
                      "published_at": "2026-04-23T10:00:00Z",
                      "is_editable": false
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application. Either the UUID is wrong or it belongs to another application — the API deliberately does not distinguish the two cases.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X GET 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.get(\n    f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()\nquestionnaire = resp.json()\nprint(questionnaire[\"title\"], questionnaire[\"status\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst questionnaire = await res.json();\nconsole.log(questionnaire.title, questionnaire.status);"
          }
        ]
      },
      "patch": {
        "summary": "Update questionnaire",
        "description": "Update a questionnaire. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `questionnaire_id`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: \"published\"` on a **published** version → a NEW published version is created under the same `questionnaire_group_id` (new `questionnaire_id`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: \"draft\"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`\"Cannot revert a published version to draft.\"`).",
        "operationId": "update_questionnaire",
        "tags": [
          "Questionnaires"
        ],
        "parameters": [
          {
            "name": "questionnaire_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the questionnaire to update."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SimpleQuestionnaireUpdateRequest"
              },
              "examples": {
                "Rename": {
                  "summary": "Rename a questionnaire",
                  "value": {
                    "title": "Updated Customer Onboarding"
                  }
                },
                "Replace questions": {
                  "summary": "Replace the full linear question list",
                  "value": {
                    "form_elements": [
                      {
                        "id": "occupation",
                        "element_type": "short_text",
                        "label": {
                          "en": "What is your occupation?"
                        },
                        "is_required": true
                      },
                      {
                        "id": "source_of_funds",
                        "element_type": "multiple_choice",
                        "label": {
                          "en": "Source of funds"
                        },
                        "options": [
                          {
                            "value": "employment",
                            "label": {
                              "en": "Employment"
                            }
                          },
                          {
                            "value": "business",
                            "label": {
                              "en": "Business"
                            }
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Questionnaire updated. The body is the latest state of the resulting version — the in-place updated version, or the newly created published version when you sent `status: \"published\"` on a published row (in that case `questionnaire_id` and `version` differ from the ones you patched). `questionnaire_id` in the response is the per-version UUID; use `questionnaire_group_id` if you need the stable group identifier.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/QuestionnaireDetail"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — unknown `element_type`, missing translation, unsupported branching/conditional fields, `languages` missing `en`, `default_language` not in `languages`, option without a label, sending a raw `graph`, or `status: \"draft\"` on a published version (`\"Cannot revert a published version to draft.\"`). Response body is a DRF validation error keyed by field name."
          },
          "403": {
            "description": "API key missing or invalid.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application."
          },
          "415": {
            "description": "Unsupported media type. Set `Content-Type: application/json`."
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"title\": \"Updated Customer Onboarding\"}'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.patch(\n    f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    json={\"title\": \"Updated Customer Onboarding\"},\n    timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()[\"questionnaire_id\"], resp.json()[\"status\"])"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n  method: \"PATCH\",\n  headers: {\n    'x-api-key': 'YOUR_API_KEY',\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({ title: \"Updated Customer Onboarding\" }),\n});\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst q = await res.json();\nconsole.log(q.questionnaire_id, q.status);"
          }
        ]
      },
      "delete": {
        "summary": "Delete questionnaire",
        "description": "Permanently delete one questionnaire version (hard delete, no undo). Stored questionnaire responses linked to this version are deleted with it, and sessions referencing it lose the link; other versions in the same group are not deleted. Workflows that referenced it keep running but the questionnaire step can no longer load it.",
        "operationId": "delete_questionnaire",
        "tags": [
          "Questionnaires"
        ],
        "parameters": [
          {
            "name": "questionnaire_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Per-version UUID of the questionnaire to delete."
          }
        ],
        "responses": {
          "204": {
            "description": "Questionnaire deleted. The response body is empty."
          },
          "403": {
            "description": "API key missing or invalid.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No questionnaire with this `questionnaire_uuid` exists for the authenticated application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          },
          "500": {
            "description": "Unexpected server error. Safe to retry."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "cURL",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import requests\n\nresp = requests.delete(\n    f\"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\"},\n    timeout=10,\n)\nresp.raise_for_status()  # raises on 4xx/5xx; 204 is success"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const res = await fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`, {\n  method: \"DELETE\",\n  headers: { 'x-api-key': 'YOUR_API_KEY' },\n});\nif (res.status !== 204) throw new Error(`Didit ${res.status}`);"
          }
        ]
      }
    },
    "/v3/users/": {
      "get": {
        "summary": "List users",
        "description": "List [users](/entities/users) Didit assembles from KYC sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus users created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.",
        "operationId": "list_users",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/users/?limit=50' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/users/',\n    headers={'x-api-key': 'YOUR_API_KEY'},\n    params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor user in page['results']:\n    print(user['vendor_data'], user['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const url = new URL('https://verification.didit.me/v3/users/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\n\nconsole.log(page.count, page.results.length);"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of users with verification status, session counts, and feature breakdown.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or `null` on the first page."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/UserListItem"
                      }
                    }
                  }
                },
                "examples": {
                  "Example": {
                    "value": {
                      "count": 2,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418",
                          "vendor_data": "user-abc-123",
                          "display_name": null,
                          "full_name": "John Michael Doe",
                          "date_of_birth": "1990-05-15",
                          "effective_name": "John Michael Doe",
                          "status": "ACTIVE",
                          "portrait_image_url": "https://<media-host>/...",
                          "session_count": 3,
                          "approved_count": 2,
                          "declined_count": 0,
                          "in_review_count": 1,
                          "issuing_states": [
                            "USA"
                          ],
                          "approved_emails": [
                            "john@example.com"
                          ],
                          "approved_phones": [
                            "+14155551234"
                          ],
                          "features": {
                            "ID_VERIFICATION": "Approved",
                            "LIVENESS": "Approved",
                            "FACE_MATCH": "Approved",
                            "AML": "Approved"
                          },
                          "features_list": [
                            {
                              "feature": "ID_VERIFICATION",
                              "status": "Approved"
                            },
                            {
                              "feature": "LIVENESS",
                              "status": "Approved"
                            },
                            {
                              "feature": "FACE_MATCH",
                              "status": "Approved"
                            },
                            {
                              "feature": "AML",
                              "status": "Approved"
                            }
                          ],
                          "last_session_at": "2025-06-15T10:30:00Z",
                          "last_activity_at": "2025-06-15T10:30:00Z",
                          "first_session_at": "2025-06-01T08:00:00Z",
                          "tags": [
                            {
                              "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                              "name": "VIP",
                              "color": "#2567FF"
                            }
                          ],
                          "created_at": "2025-06-01T08:00:00Z"
                        },
                        {
                          "didit_internal_id": "6ed99d53-e8f5-4cf8-9ac1-4a506cb40f6b",
                          "vendor_data": null,
                          "display_name": "Jane S.",
                          "full_name": "Jane Elizabeth Smith",
                          "date_of_birth": "1985-11-22",
                          "effective_name": "Jane S.",
                          "status": "FLAGGED",
                          "portrait_image_url": null,
                          "session_count": 1,
                          "approved_count": 0,
                          "declined_count": 0,
                          "in_review_count": 1,
                          "issuing_states": [],
                          "approved_emails": [],
                          "approved_phones": [],
                          "features": {
                            "ID_VERIFICATION": "In Review"
                          },
                          "features_list": [
                            {
                              "feature": "ID_VERIFICATION",
                              "status": "In Review"
                            }
                          ],
                          "last_session_at": "2025-06-20T14:00:00Z",
                          "last_activity_at": "2025-06-20T14:05:00Z",
                          "first_session_at": "2025-06-20T14:00:00Z",
                          "tags": [],
                          "created_at": "2025-06-20T14:00:00Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot list users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/users/{vendor_data}/": {
      "get": {
        "summary": "Get user",
        "description": "Fetch one [user](/entities/users) by `vendor_data` (exact match). Adds `metadata`, `comments`, and `updated_at` to the list view, and switches `tags` to the detailed tag-link shape. Returns soft-deleted users too.",
        "operationId": "get_user",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the user — a free-form string (NOT a UUID). This is the same value you passed as `vendor_data` when creating the session that first introduced this user, matched exactly as sent.",
            "example": "user-abc-123"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/users/user-abc-123/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/users/user-abc-123/',\n    headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'], user['status'], user['session_count'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const vendorData = encodeURIComponent('user-abc-123');\nconst user = await fetch(\n  `https://verification.didit.me/v3/users/${vendorData}/`,\n  { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\n\nconsole.log(user.didit_internal_id, user.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Full user detail including metadata, comments/activity log, and aggregated session data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetailItem"
                },
                "examples": {
                  "Active user": {
                    "value": {
                      "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418",
                      "vendor_data": "user-abc-123",
                      "display_name": null,
                      "full_name": "John Michael Doe",
                      "date_of_birth": "1990-05-15",
                      "effective_name": "John Michael Doe",
                      "status": "ACTIVE",
                      "metadata": {
                        "tier": "premium",
                        "source": "website"
                      },
                      "portrait_image_url": "https://<media-host>/...",
                      "session_count": 3,
                      "approved_count": 2,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "issuing_states": [
                        "USA"
                      ],
                      "approved_emails": [
                        "john@example.com"
                      ],
                      "approved_phones": [
                        "+14155551234"
                      ],
                      "features": {
                        "ID_VERIFICATION": "Approved",
                        "LIVENESS": "Approved",
                        "FACE_MATCH": "Approved",
                        "AML": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "ID_VERIFICATION",
                          "status": "Approved"
                        },
                        {
                          "feature": "LIVENESS",
                          "status": "Approved"
                        },
                        {
                          "feature": "FACE_MATCH",
                          "status": "Approved"
                        },
                        {
                          "feature": "AML",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2025-06-15T10:30:00Z",
                      "last_activity_at": "2025-06-15T10:30:00Z",
                      "first_session_at": "2025-06-01T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "tag": {
                            "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                            "name": "VIP",
                            "color": "#2567FF",
                            "description": null,
                            "source": "custom",
                            "created_at": "2025-05-01T09:00:00Z",
                            "updated_at": "2025-05-01T09:00:00Z"
                          },
                          "added_by_email": "analyst@acme.com",
                          "added_by_name": "Jane Analyst",
                          "created_at": "2025-06-02T11:00:00Z"
                        }
                      ],
                      "comments": [
                        {
                          "uuid": "c1111111-2222-4333-8444-555555555555",
                          "comment_type": "STATUS_CHANGED",
                          "comment": null,
                          "actor_email": "analyst@acme.com",
                          "actor_name": "Jane Analyst",
                          "previous_status": "FLAGGED",
                          "new_status": "ACTIVE",
                          "previous_value": null,
                          "new_value": null,
                          "changed_fields": [],
                          "metadata": null,
                          "mentioned_emails": [],
                          "created_at": "2025-06-01T10:30:00Z"
                        }
                      ],
                      "created_at": "2025-06-01T08:00:00Z",
                      "updated_at": "2025-06-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot read this application's users. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No user with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "patch": {
        "summary": "Update user",
        "description": "Partial update of a user: `display_name`, `full_name`, `date_of_birth`, `status`, `metadata`, `approved_emails`/`approved_phones`/`issuing_states`. `metadata` fully replaces. **Note:** unlike [Update User Status](#patch-/v3/users/-vendor_data-/update-status/), changing `status` here does NOT record a status-change activity and does NOT sync the system blocklist — use the dedicated update-status endpoint for `BLOCKED`/unblock flows.",
        "operationId": "update_user",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the user (free-form string, NOT a UUID).",
            "example": "user-abc-123"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"display_name\": \"Jane S.\", \"status\": \"FLAGGED\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.patch(\n    'https://verification.didit.me/v3/users/user-abc-123/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'display_name': 'Jane S.', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/', {\n  method: 'PATCH',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ display_name: 'Jane S.', status: 'FLAGGED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "User updated. The full updated user record (same shape as Get User) is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetailItem"
                },
                "examples": {
                  "Updated": {
                    "value": {
                      "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418",
                      "vendor_data": "user-abc-123",
                      "display_name": "Jane S.",
                      "full_name": "Jane Elizabeth Smith",
                      "date_of_birth": "1985-11-22",
                      "effective_name": "Jane S.",
                      "status": "FLAGGED",
                      "metadata": {
                        "tier": "premium"
                      },
                      "portrait_image_url": "https://<media-host>/...",
                      "session_count": 3,
                      "approved_count": 2,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "issuing_states": [
                        "USA"
                      ],
                      "approved_emails": [
                        "john@example.com"
                      ],
                      "approved_phones": [
                        "+14155551234"
                      ],
                      "features": {
                        "ID_VERIFICATION": "Approved",
                        "LIVENESS": "Approved",
                        "FACE_MATCH": "Approved",
                        "AML": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "ID_VERIFICATION",
                          "status": "Approved"
                        },
                        {
                          "feature": "LIVENESS",
                          "status": "Approved"
                        },
                        {
                          "feature": "FACE_MATCH",
                          "status": "Approved"
                        },
                        {
                          "feature": "AML",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2025-06-15T10:30:00Z",
                      "last_activity_at": "2025-06-15T10:30:00Z",
                      "first_session_at": "2025-06-01T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "tag": {
                            "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                            "name": "VIP",
                            "color": "#2567FF",
                            "description": null,
                            "source": "custom",
                            "created_at": "2025-05-01T09:00:00Z",
                            "updated_at": "2025-05-01T09:00:00Z"
                          },
                          "added_by_email": "analyst@acme.com",
                          "added_by_name": "Jane Analyst",
                          "created_at": "2025-06-02T11:00:00Z"
                        }
                      ],
                      "comments": [],
                      "created_at": "2025-06-01T08:00:00Z",
                      "updated_at": "2025-06-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body — typically an unknown `status` or a `date_of_birth` not in `YYYY-MM-DD`.",
            "content": {
              "application/json": {
                "examples": {
                  "Bad status": {
                    "value": {
                      "status": [
                        "\"INVALID\" is not a valid choice."
                      ]
                    }
                  },
                  "Bad date_of_birth": {
                    "value": {
                      "date_of_birth": [
                        "Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No user with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "full_name": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 512,
                    "description": "Full name of the user"
                  },
                  "display_name": {
                    "type": "string",
                    "nullable": true,
                    "description": "Custom display name for this user"
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Date of birth in YYYY-MM-DD format"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "description": "User lifecycle status. Changing it via this generic PATCH does NOT sync the system blocklist or record a status-change activity; prefer the update-status endpoint."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Arbitrary JSON object you attach to the user. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys."
                  },
                  "approved_emails": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Verified email addresses for this user, e.g. `[\"john@example.com\"]`. Fully replaces the stored list."
                  },
                  "approved_phones": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Verified phone numbers for this user. Fully replaces the stored list."
                  },
                  "issuing_states": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`. Fully replaces the stored list."
                  }
                }
              },
              "examples": {
                "Rename": {
                  "summary": "Set display name",
                  "value": {
                    "display_name": "Jane S."
                  }
                },
                "Override status": {
                  "summary": "Flag the user for review",
                  "value": {
                    "status": "FLAGGED"
                  }
                },
                "Add metadata": {
                  "summary": "Replace metadata JSON",
                  "value": {
                    "metadata": {
                      "tier": "premium",
                      "risk_level": "low"
                    }
                  }
                },
                "Fix extracted DOB": {
                  "summary": "Correct extracted profile fields",
                  "value": {
                    "full_name": "Jane Elizabeth Smith",
                    "date_of_birth": "1985-11-22"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/users/delete/": {
      "post": {
        "summary": "Batch delete users",
        "description": "**Hard delete** users — rows are removed permanently (including previously soft-deleted records). For everyday blocking prefer [Update User Status](#patch-/v3/users/-vendor_data-/update-status/) with `BLOCKED`. Unlike the businesses endpoint, this endpoint only accepts `vendor_data_list` or `delete_all` (no `didit_internal_id_list`).",
        "operationId": "batch_delete_users",
        "tags": [
          "Users"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Provide `vendor_data_list` or `delete_all: true`. When `delete_all` is `true` it takes precedence and `vendor_data_list` is ignored.",
                "properties": {
                  "vendor_data_list": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Your own identifiers to delete, matched exactly as sent."
                  },
                  "delete_all": {
                    "type": "boolean",
                    "default": false,
                    "description": "If `true`, deletes every user in the application."
                  }
                }
              },
              "examples": {
                "Specific users": {
                  "summary": "Delete users by vendor_data",
                  "value": {
                    "vendor_data_list": [
                      "user-123",
                      "user-456"
                    ]
                  }
                },
                "All users": {
                  "summary": "Wipe every user in this application",
                  "value": {
                    "delete_all": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/users/delete/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"vendor_data_list\": [\"user-123\", \"user-456\"]}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/users/delete/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'vendor_data_list': ['user-123', 'user-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/users/delete/', {\n  method: 'POST',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ vendor_data_list: ['user-123', 'user-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);"
          }
        ],
        "responses": {
          "200": {
            "description": "Users deleted. Returns the number of rows actually removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "integer",
                      "description": "Number of users deleted (excludes vendor_data values that didn't match anything)."
                    }
                  }
                },
                "examples": {
                  "Deleted some": {
                    "value": {
                      "deleted": 2
                    }
                  },
                  "Nothing matched": {
                    "value": {
                      "deleted": 0
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No selector supplied (`vendor_data_list` missing/empty and `delete_all` not `true`). The error body is a JSON array.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing selector": {
                    "value": [
                      "Provide vendor_data_list or set delete_all=true."
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot delete users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/sessions/delete/": {
      "post": {
        "summary": "Bulk soft-delete KYC sessions by session_number",
        "description": "Bulk soft-delete User Verification (KYC) sessions by their numeric `session_number`, or wipe every KYC session in your application with `delete_all: true`.\n\n`session_number` is the human-friendly incrementing counter shown in the Console session list and returned as `session_number` by the create-session and decision endpoints — it is **not** the `session_id` UUID. To delete a session by UUID — or to delete a Business Verification (KYB) session, which this endpoint never touches — use `DELETE /v3/session/{sessionId}/delete/` instead.\n\nEach deleted session gets exactly the same treatment as the single-session delete endpoint: stamped with a deletion timestamp, removed from all read endpoints, related face/liveness/face-match records soft-deleted, and all stored media moved to a quarantined storage prefix by a background job so previously issued media URLs stop resolving. Database records are retained internally but become unreachable through the API; blocklist entries are **not** removed; no webhook is emitted; there is no undo.\n\n**Selection semantics:**\n- Entries in `session_numbers` that do not match a live KYC session in your application — unknown numbers, already-deleted sessions, or sessions owned by another application — are **silently skipped**. There is no per-item failure report: the endpoint returns `204` whether it deleted all, some, or none of the listed sessions.\n- Validation failures (a non-numeric entry, an empty list, or neither `session_numbers` nor `delete_all` provided) reject the **whole request** with `400` and delete nothing.\n- All deletions run inside a single database transaction.\n- There is no documented cap on the list length; the shared write rate limit (below) is the practical bound.\n\n**Idempotency:** fully idempotent — repeating the same request returns `204` again (already-deleted numbers are skipped).\n\n**`delete_all: true` — destructive:** soft-deletes **every** non-deleted KYC session in the application; `session_numbers` is ignored when set. There is no confirmation step and no undo.\n\n**Authentication:** API key in the `x-api-key` header. Missing or invalid credentials return `403` (`{\"detail\": \"You do not have permission to perform this action.\"}`) — this API never returns `401`.\n\n**Rate limit:** shared write budget of 300 requests/min per API key across all POST/PATCH/DELETE endpoints; exceeding it returns `429`.\n\nThis endpoint accepts API keys only — Business Console user Bearer tokens are rejected with `403` (unlike the single-session delete, which accepts both).",
        "operationId": "batch_delete_sessions",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "session_numbers": {
                    "type": "array",
                    "description": "Numeric `session_number` values of the KYC sessions to soft-delete, as digit-only strings (JSON numbers are also accepted and coerced). Required unless `delete_all` is `true`; an empty list returns `400`. Any non-digit entry rejects the whole request with `400` (unless `delete_all` is `true`, in which case validation is skipped and the entries are ignored). Numbers that do not match a live session in your application are silently skipped.",
                    "items": {
                      "type": "string",
                      "pattern": "^[0-9]+$",
                      "example": "1001"
                    },
                    "minItems": 1,
                    "example": [
                      "1001",
                      "1002",
                      "1003"
                    ]
                  },
                  "delete_all": {
                    "type": "boolean",
                    "description": "When `true`, every non-deleted KYC session in the calling application is soft-deleted and `session_numbers` is ignored. Defaults to `false`. Irreversible — there is no confirmation step.",
                    "default": false,
                    "example": false
                  }
                }
              },
              "examples": {
                "Specific sessions": {
                  "summary": "Delete specific sessions by number",
                  "value": {
                    "session_numbers": [
                      "1001",
                      "1002",
                      "1003"
                    ]
                  }
                },
                "All sessions": {
                  "summary": "Delete every KYC session in the application",
                  "value": {
                    "delete_all": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Request accepted and all matching sessions soft-deleted. Empty body. Returned even when some or all `session_numbers` matched nothing (unknown or already deleted) — there is no per-item report."
          },
          "400": {
            "description": "Validation error — nothing was deleted. Error values are arrays of message strings keyed by field name.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_numbers": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                },
                "examples": {
                  "Missing selector": {
                    "summary": "Neither session_numbers nor delete_all provided",
                    "value": {
                      "session_numbers": [
                        "This field is required unless delete_all is true."
                      ]
                    }
                  },
                  "Empty list": {
                    "summary": "session_numbers is an empty array",
                    "value": {
                      "session_numbers": [
                        "This list may not be empty."
                      ]
                    }
                  },
                  "Non-numeric entry": {
                    "summary": "session_numbers contains a non-digit string",
                    "value": {
                      "session_numbers": [
                        "All session_numbers must be numeric."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing or invalid API key. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Missing or invalid API key": {
                    "summary": "No x-api-key header, or the key is invalid",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Rate limited": {
                    "summary": "Write budget exhausted",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST \\\n  https://verification.didit.me/v3/sessions/delete/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"session_numbers\": [\"1001\", \"1002\", \"1003\"]\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.post(\n    \"https://verification.didit.me/v3/sessions/delete/\",\n    headers={\n        \"x-api-key\": \"YOUR_API_KEY\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\"session_numbers\": [\"1001\", \"1002\", \"1003\"]},\n)\nresponse.raise_for_status()  # 204 on success, even if some numbers matched nothing\n\n# Destructive: delete every KYC session in the application\n# requests.post(\n#     \"https://verification.didit.me/v3/sessions/delete/\",\n#     headers={\"x-api-key\": \"YOUR_API_KEY\"},\n#     json={\"delete_all\": True},\n# )"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/sessions/delete/',\n  {\n    method: 'POST',\n    headers: {\n      'x-api-key': 'YOUR_API_KEY',\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({ session_numbers: ['1001', '1002', '1003'] }),\n  },\n);\nif (response.status !== 204) throw new Error(`HTTP ${response.status}`);"
          }
        ]
      }
    },
    "/v3/sessions/{session_id}/reviews/": {
      "get": {
        "summary": "List session reviews and activity log",
        "description": "Return the audit trail / activity feed for a User Verification (KYC) session, newest first, in a paginated envelope (`count` / `next` / `previous` / `results`, 50 entries per page by default).\n\nFeed entries are written by every actor that touches the session: manual decisions made through `PATCH /v3/session/{session_id}/update-status/` (which records `previous_status` → `new_status`), Console reviewer activity (comments with `@email` mentions, KYC/POA data edits, file uploads/removals, tag changes, detail-page views), AML ongoing-monitoring matches, and notes appended via `POST` on this same path. Reading this feed is the API-side equivalent of the **Activity** panel in the Didit Console session view.\n\nOnly User Verification (KYC) sessions are addressable here; Business Verification (KYB) session IDs return `404`.",
        "operationId": "list_session_reviews",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Page size. Defaults to `50`.",
            "schema": {
              "type": "integer",
              "default": 50,
              "example": 50
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "description": "Number of entries to skip from the start of the (ordered) feed. Defaults to `0`. The `next` / `previous` URLs in the response carry the right values for walking the feed.",
            "schema": {
              "type": "integer",
              "default": 0,
              "example": 0
            }
          },
          {
            "name": "ordering",
            "in": "query",
            "required": false,
            "description": "Sort order. The feed defaults to newest first (`-created_at`); pass `created_at` to read it oldest first. Unknown values are silently ignored (default order is used); other serializer field names are also accepted.",
            "schema": {
              "type": "string",
              "default": "-created_at",
              "example": "created_at"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated activity feed, newest first by default. `count` is the total number of entries; `next` / `previous` are ready-made page URLs (`null` at the ends). A `count` of `0` with empty `results` means no activity has been recorded yet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Total number of feed entries for this session (across all pages).",
                      "example": 3
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of the next page, or `null` on the last page.",
                      "example": null
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "URL of the previous page, or `null` on the first page.",
                      "example": null
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReviewItem"
                      },
                      "description": "Feed entries for this page."
                    }
                  }
                },
                "examples": {
                  "DecisionTrail": {
                    "summary": "Manual approval plus API-added notes",
                    "value": {
                      "count": 3,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4",
                          "activity_type": "STATUS_UPDATED",
                          "actor_display": "API Client",
                          "actor_email": null,
                          "actor_type": "API_KEY",
                          "new_status": "Approved",
                          "previous_status": "In Review",
                          "comment": "Document re-checked manually; address matches.",
                          "mentioned_emails": [],
                          "previous_value": {
                            "status": "In Review"
                          },
                          "new_value": {
                            "status": "Approved"
                          },
                          "changed_fields": [
                            "status"
                          ],
                          "metadata": null,
                          "created_at": "2026-06-12T00:19:31.918735Z"
                        },
                        {
                          "uuid": "35370f58-5098-44ee-8a06-8c9af80b2455",
                          "activity_type": "STATUS_UPDATED",
                          "actor_display": "Unknown",
                          "actor_email": null,
                          "actor_type": "CONSOLE_USER",
                          "new_status": null,
                          "previous_status": null,
                          "comment": "Note without status.",
                          "mentioned_emails": [],
                          "previous_value": null,
                          "new_value": null,
                          "changed_fields": [],
                          "metadata": null,
                          "created_at": "2026-06-12T00:18:44.504157Z"
                        },
                        {
                          "uuid": "00338f82-f1c5-4f57-8614-c49bb258af1a",
                          "activity_type": "STATUS_UPDATED",
                          "actor_display": "Unknown",
                          "actor_email": null,
                          "actor_type": "CONSOLE_USER",
                          "new_status": "In Review",
                          "previous_status": null,
                          "comment": "Flagging for manual document re-check.",
                          "mentioned_emails": [],
                          "previous_value": null,
                          "new_value": null,
                          "changed_fields": [],
                          "metadata": null,
                          "created_at": "2026-06-12T00:18:44.466537Z"
                        }
                      ]
                    }
                  },
                  "PaginatedFirstPage": {
                    "summary": "First page with limit=2 — next holds the follow-up URL",
                    "value": {
                      "count": 4,
                      "next": "https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=2&offset=2",
                      "previous": null,
                      "results": [
                        {
                          "uuid": "de0561bc-5583-4409-9171-1a31a4c8a5f4",
                          "activity_type": "STATUS_UPDATED",
                          "actor_display": "API Client",
                          "actor_email": null,
                          "actor_type": "API_KEY",
                          "new_status": "Approved",
                          "previous_status": "In Review",
                          "comment": "Document re-checked manually; address matches.",
                          "mentioned_emails": [],
                          "previous_value": {
                            "status": "In Review"
                          },
                          "new_value": {
                            "status": "Approved"
                          },
                          "changed_fields": [
                            "status"
                          ],
                          "metadata": null,
                          "created_at": "2026-06-12T00:19:31.918735Z"
                        },
                        {
                          "uuid": "bd4e6227-bdd2-42b2-a726-1d12f785a457",
                          "activity_type": "STATUS_UPDATED",
                          "actor_display": "Unknown",
                          "actor_email": null,
                          "actor_type": "CONSOLE_USER",
                          "new_status": null,
                          "previous_status": null,
                          "comment": null,
                          "mentioned_emails": [],
                          "previous_value": null,
                          "new_value": null,
                          "changed_fields": [],
                          "metadata": null,
                          "created_at": "2026-06-12T00:18:44.540238Z"
                        }
                      ]
                    }
                  },
                  "Empty": {
                    "summary": "Session with no recorded activity yet",
                    "value": {
                      "count": 0,
                      "next": null,
                      "previous": null,
                      "results": []
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "You do not have permission to perform this action."
                    }
                  }
                },
                "examples": {
                  "InvalidKey": {
                    "summary": "Missing or invalid x-api-key header",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Session not found."
                    }
                  }
                },
                "examples": {
                  "NotFound": {
                    "summary": "Unknown, deleted, or cross-application session UUID",
                    "value": {
                      "detail": "Session not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -s 'https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/?limit=50' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import os, requests\n\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nurl = f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\"\n\nwhile url:  # walk every page of the feed\n    resp = requests.get(url, headers={\"x-api-key\": os.environ[\"DIDIT_API_KEY\"]}, timeout=10)\n    resp.raise_for_status()\n    page = resp.json()\n    for entry in page[\"results\"]:\n        print(entry[\"created_at\"], entry[\"activity_type\"], entry[\"actor_display\"], entry.get(\"new_status\"))\n    url = page[\"next\"]"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "const sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n  `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n  { headers: { 'x-api-key': 'YOUR_API_KEY' } },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}`);\nconst { count, results } = await res.json();\nconsole.log(`${count} feed entries`);\nfor (const entry of results) {\n  console.log(entry.created_at, entry.activity_type, entry.actor_display, entry.new_status ?? \"\");\n}"
          }
        ]
      },
      "post": {
        "summary": "Add a review note to a session's audit trail",
        "description": "Append a note to a User Verification (KYC) session's review / activity feed. **This endpoint only records an entry — it never changes the session.** The session's `status` is left untouched, no status-transition validation runs, and no webhook fires. To actually approve, decline, or re-open a session (which also writes a fully-attributed `STATUS_UPDATED` entry to this feed), use `PATCH /v3/session/{session_id}/update-status/` instead.\n\nBoth body fields are optional and an empty JSON object is accepted. `new_status` is stored purely as a label on the entry and accepts any of the ten session status values (case-sensitive). `@email` mentions inside `comment` are stored as plain text only — they trigger no notifications on this endpoint, and the entry's `mentioned_emails` stays empty.\n\nNote on attribution: entries created here carry no actor information. In the feed they show `actor_display: \"Unknown\"`, `actor_type: \"CONSOLE_USER\"`, and `activity_type: \"STATUS_UPDATED\"` (storage defaults), and the `201` body's `user` field is `\"Unknown\"`.",
        "operationId": "create_session_review",
        "tags": [
          "Sessions"
        ],
        "parameters": [
          {
            "name": "Content-Type",
            "in": "header",
            "required": false,
            "description": "Set to `application/json` when sending a JSON body.",
            "schema": {
              "type": "string",
              "example": "application/json"
            }
          },
          {
            "name": "session_id",
            "in": "path",
            "required": true,
            "description": "UUID of the User Verification (KYC) session. Must belong to the same application as the API key — cross-application UUIDs, deleted sessions, and Business Verification (KYB) session IDs all return `404`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "new_status": {
                    "type": "string",
                    "nullable": true,
                    "enum": [
                      "Not Started",
                      "In Progress",
                      "Approved",
                      "Declined",
                      "In Review",
                      "Expired",
                      "Abandoned",
                      "Kyc Expired",
                      "Resubmitted",
                      "Awaiting User"
                    ],
                    "description": "Optional status label to record on the entry. Accepts any session status value, case-sensitive (`\"approved\"` is rejected with `400`). Recording a status here does **not** change the session's actual status — use the update-status endpoint for that.",
                    "example": "In Review"
                  },
                  "comment": {
                    "type": "string",
                    "nullable": true,
                    "description": "Optional free-text note (no enforced length limit). Stored verbatim; `@email` mentions are not processed on this endpoint.",
                    "example": "Flagging for manual document re-check."
                  }
                }
              },
              "examples": {
                "NoteWithStatusLabel": {
                  "summary": "Note carrying a status label (session status unchanged)",
                  "value": {
                    "new_status": "In Review",
                    "comment": "Flagging for manual document re-check."
                  }
                },
                "CommentOnly": {
                  "summary": "Plain note without a status label",
                  "value": {
                    "comment": "Customer emailed an updated utility bill; awaiting upload."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Entry recorded on the session's audit trail. The session itself is unchanged — its `status` keeps its previous value and no webhook is sent. The new entry also appears at the top of `GET …/reviews/` (with `actor_display: \"Unknown\"`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "string",
                      "description": "Display name of the entry's author. Always `\"Unknown\"` for entries created through this endpoint, because it records no actor attribution.",
                      "example": "Unknown"
                    },
                    "new_status": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "Not Started",
                        "In Progress",
                        "Approved",
                        "Declined",
                        "In Review",
                        "Expired",
                        "Abandoned",
                        "Kyc Expired",
                        "Resubmitted",
                        "Awaiting User"
                      ],
                      "description": "The status label recorded on the entry, or `null` if none was sent. The session's actual status is not affected.",
                      "example": "In Review"
                    },
                    "comment": {
                      "type": "string",
                      "nullable": true,
                      "description": "The note recorded on the entry, or `null` if none was sent.",
                      "example": "Flagging for manual document re-check."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the entry was written, ISO 8601 UTC.",
                      "example": "2026-06-12T00:18:44.466537Z"
                    }
                  }
                },
                "examples": {
                  "NoteWithStatusLabel": {
                    "summary": "Note with a status label",
                    "value": {
                      "user": "Unknown",
                      "new_status": "In Review",
                      "comment": "Flagging for manual document re-check.",
                      "created_at": "2026-06-12T00:18:44.466537Z"
                    }
                  },
                  "CommentOnly": {
                    "summary": "Plain note",
                    "value": {
                      "user": "Unknown",
                      "new_status": null,
                      "comment": "Note without status.",
                      "created_at": "2026-06-12T00:18:44.504157Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error, returned as a per-field map of messages. The usual cause is a `new_status` value that is not one of the ten allowed statuses — the check is case-sensitive.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "new_status": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "example": [
                        "\"approved\" is not a valid choice."
                      ]
                    }
                  }
                },
                "examples": {
                  "InvalidStatusChoice": {
                    "summary": "Lowercase / unknown status value",
                    "value": {
                      "new_status": [
                        "\"approved\" is not a valid choice."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, malformed, expired, or revoked `x-api-key`. Authentication is checked before the session lookup, so an invalid key returns `403` even when the session UUID does not exist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "You do not have permission to perform this action."
                    }
                  }
                },
                "examples": {
                  "InvalidKey": {
                    "summary": "Missing or invalid x-api-key header",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No User Verification (KYC) session with this UUID is visible to your application: the UUID does not exist, the session was deleted, or it belongs to a different application. Business Verification (KYB) session IDs also return `404` here — the reviews feed is only addressable for KYC sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string",
                      "example": "Session not found."
                    }
                  }
                },
                "examples": {
                  "NotFound": {
                    "summary": "Unknown, deleted, or cross-application session UUID",
                    "value": {
                      "detail": "Session not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*` response headers. See [Rate limiting](/integration/rate-limiting)."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "# Records an audit-trail note only -- the session's status does NOT change.\ncurl -s -X POST https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"new_status\": \"In Review\",\n    \"comment\": \"Flagging for manual document re-check.\"\n  }'"
          },
          {
            "lang": "Python",
            "label": "Python (requests)",
            "source": "import os, requests\n\n# Records an audit-trail note only -- the session's status does NOT change.\n# To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nsession_id = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\"\nresp = requests.post(\n    f\"https://verification.didit.me/v3/sessions/{session_id}/reviews/\",\n    headers={\n        \"x-api-key\": os.environ[\"DIDIT_API_KEY\"],\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"new_status\": \"In Review\",\n        \"comment\": \"Flagging for manual document re-check.\",\n    },\n    timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())  # {'user': 'Unknown', 'new_status': 'In Review', ...}"
          },
          {
            "lang": "JavaScript",
            "label": "Node.js (fetch)",
            "source": "// Records an audit-trail note only -- the session's status does NOT change.\n// To approve/decline a session use PATCH /v3/session/{session_id}/update-status/.\nconst sessionId = \"8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d\";\nconst res = await fetch(\n  `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,\n  {\n    method: \"POST\",\n    headers: {\n      'x-api-key': 'YOUR_API_KEY',\n      \"Content-Type\": \"application/json\",\n    },\n    body: JSON.stringify({\n      new_status: \"In Review\",\n      comment: \"Flagging for manual document re-check.\",\n    }),\n  },\n);\nif (!res.ok) throw new Error(`Didit ${res.status}: ${await res.text()}`);\nconsole.log(await res.json()); // { user: 'Unknown', new_status: 'In Review', ... }"
          }
        ]
      }
    },
    "/v3/billing/balance/": {
      "get": {
        "summary": "Get credit balance",
        "description": "Get current pre-paid credit balance in USD (organization-wide) plus auto-refill config. Amounts are decimal strings — parse with a decimal library.",
        "operationId": "get_billing_balance",
        "tags": [
          "Billing"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/billing/balance/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\nfrom decimal import Decimal\n\nimport requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/billing/balance/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=10,\n)\nresp.raise_for_status()\ndata = resp.json()\nbalance = Decimal(data['balance'])\nif balance < Decimal('50'):\n    print(f'Low balance: ${balance} - top up before launching new sessions.')\nelse:\n    print(f'Balance OK: ${balance}')"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/billing/balance/', {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Billing balance failed: ${resp.status}`);\nconst { balance, auto_refill_enabled, auto_refill_threshold } = await resp.json();\nconsole.log({ balance, auto_refill_enabled, auto_refill_threshold });"
          }
        ],
        "responses": {
          "200": {
            "description": "Current credit balance plus the organization's auto-refill configuration.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "balance",
                    "auto_refill_enabled"
                  ],
                  "properties": {
                    "balance": {
                      "type": "string",
                      "description": "Current pre-paid credit balance in USD, as a fixed-point decimal string. Decreases as verifications are billed and increases when a top-up settles."
                    },
                    "auto_refill_enabled": {
                      "type": "boolean",
                      "description": "When `true`, Didit will automatically charge the saved Stripe payment method to add credits once the balance crosses `auto_refill_threshold`. Configured from Console → Billing."
                    },
                    "auto_refill_amount": {
                      "type": "string",
                      "nullable": true,
                      "description": "USD amount that will be charged on each auto-refill. `null` when auto-refill is disabled or unset."
                    },
                    "auto_refill_threshold": {
                      "type": "string",
                      "nullable": true,
                      "description": "Trigger threshold in USD. Auto-refill fires when `balance` falls below this value. `null` when auto-refill is disabled or unset."
                    }
                  }
                },
                "examples": {
                  "Healthy balance with auto-refill": {
                    "value": {
                      "balance": "1234.5600",
                      "auto_refill_enabled": true,
                      "auto_refill_amount": "500.00",
                      "auto_refill_threshold": "100.00"
                    }
                  },
                  "Low balance, manual top-up only": {
                    "value": {
                      "balance": "12.0000",
                      "auto_refill_enabled": false,
                      "auto_refill_amount": null,
                      "auto_refill_threshold": null
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot read billing data for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/billing/top-up/": {
      "post": {
        "summary": "Top up credits",
        "description": "Add credits via a hosted Stripe Checkout session (**$50 minimum**); not available on annual plans. Redirect the customer to the returned `checkout_session_url`.",
        "operationId": "billing_top_up",
        "tags": [
          "Billing"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_in_dollars"
                ],
                "properties": {
                  "amount_in_dollars": {
                    "type": "number",
                    "minimum": 50,
                    "description": "Amount to charge, in USD. **Must be at least 50.** Tax is added automatically at checkout based on the customer's billing address."
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL Stripe redirects the customer to after a successful payment. Defaults to `https://console.didit.me`. Append the literal placeholder `{CHECKOUT_SESSION_ID}` if you want Stripe to substitute the session ID."
                  },
                  "cancel_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "URL Stripe redirects the customer to when they abort or close the Checkout window. Defaults to `https://console.didit.me`."
                  }
                }
              },
              "examples": {
                "Minimum top-up": {
                  "value": {
                    "amount_in_dollars": 50
                  }
                },
                "Custom amount with redirects": {
                  "value": {
                    "amount_in_dollars": 500,
                    "success_url": "https://yourapp.com/billing/top-up/success",
                    "cancel_url": "https://yourapp.com/billing/top-up/cancel"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/billing/top-up/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"amount_in_dollars\": 500,\n    \"success_url\": \"https://yourapp.com/billing/success\",\n    \"cancel_url\": \"https://yourapp.com/billing/cancel\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/billing/top-up/',\n    headers={\n        'x-api-key': os.environ['DIDIT_API_KEY'],\n        'Content-Type': 'application/json',\n    },\n    json={\n        'amount_in_dollars': 500,\n        'success_url': 'https://yourapp.com/billing/success',\n        'cancel_url': 'https://yourapp.com/billing/cancel',\n    },\n    timeout=10,\n)\nresp.raise_for_status()\nsession = resp.json()\nprint('Redirect customer to:', session['checkout_session_url'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/billing/top-up/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': process.env.DIDIT_API_KEY,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    amount_in_dollars: 500,\n    success_url: 'https://yourapp.com/billing/success',\n    cancel_url: 'https://yourapp.com/billing/cancel',\n  }),\n});\nif (!resp.ok) throw new Error(`Top-up failed: ${resp.status}`);\nconst { checkout_session_url } = await resp.json();\nwindow.location.assign(checkout_session_url);"
          }
        ],
        "responses": {
          "200": {
            "description": "Stripe Checkout session created. Redirect the user to `checkout_session_url` to complete payment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "checkout_session_id",
                    "checkout_session_url"
                  ],
                  "properties": {
                    "checkout_session_id": {
                      "type": "string",
                      "description": "Stripe Checkout Session identifier (e.g. `cs_test_a1B2c3...`). Useful for server-side reconciliation."
                    },
                    "checkout_session_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Single-use Stripe-hosted payment page URL. Redirect the customer here to complete payment."
                    }
                  }
                },
                "examples": {
                  "Created": {
                    "value": {
                      "checkout_session_id": "cs_test_a1B2c3D4e5F6g7H8i9J0",
                      "checkout_session_url": "https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid payload — the amount is missing, not a number, below the $50 minimum, or the organization is on an annual plan. The error body is a JSON array.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing amount": {
                    "value": [
                      "amount_in_dollars is required."
                    ]
                  },
                  "Not a number": {
                    "value": [
                      "amount_in_dollars must be a number."
                    ]
                  },
                  "Below minimum": {
                    "value": [
                      "Amount must be at least $50."
                    ]
                  },
                  "Annual plan": {
                    "value": [
                      "You have an annual plan. Contact your account manager to top up."
                    ]
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot create top-up sessions for this organization. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/webhook/destinations/": {
      "get": {
        "summary": "List webhook destinations",
        "description": "List all webhook destinations (unpaginated array, oldest first). Each row includes URL, subscribed events, and delivery health metrics. The signing secret is NOT included in the list — fetch a single destination (`GET /v3/webhook/destinations/{destination_uuid}/`) to read `secret_shared_key`.",
        "operationId": "list_webhook_destinations",
        "tags": [
          "Webhook"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/webhook/destinations/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=10,\n)\nresp.raise_for_status()\nfor dest in resp.json():\n    health = 'OK' if dest['error_rate_percentage'] < 5 else 'DEGRADED'\n    print(f\"{dest['label']:30s} {dest['url']:50s} {health}\")"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nconst destinations = await resp.json();\nfor (const d of destinations) {\n  console.log(d.label, d.url, `${d.error_rate_percentage}% errors`);\n}"
          }
        ],
        "responses": {
          "200": {
            "description": "List of webhook destinations (unpaginated, oldest first).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "uuid": {
                        "type": "string",
                        "format": "uuid",
                        "description": "Stable destination identifier; use as `{destination_uuid}` for update/delete."
                      },
                      "label": {
                        "type": "string",
                        "description": "Human-readable name shown in the Console."
                      },
                      "url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Destination URL that receives `POST` requests with the webhook payload."
                      },
                      "enabled": {
                        "type": "boolean",
                        "description": "When `false`, Didit silently skips delivery to this destination."
                      },
                      "webhook_version": {
                        "type": "string",
                        "enum": [
                          "v1",
                          "v2",
                          "v3"
                        ],
                        "description": "Payload schema version sent to this destination. `v3` is the current/recommended format."
                      },
                      "subscribed_events": {
                        "$ref": "#/components/schemas/WebhookSubscribedEvents"
                      },
                      "subscribed_events_count": {
                        "type": "integer",
                        "description": "Cached length of `subscribed_events`, useful for UI rendering."
                      },
                      "deliveries_count": {
                        "type": "integer",
                        "description": "Total delivery attempts (successful + failed) recorded for this destination."
                      },
                      "failed_deliveries_count": {
                        "type": "integer",
                        "description": "Delivery attempts whose target responded with HTTP ≥ 400 (or did not respond)."
                      },
                      "average_response_time_ms": {
                        "type": "integer",
                        "nullable": true,
                        "description": "Average target response time in milliseconds, rounded to an integer. `null` if no deliveries yet."
                      },
                      "error_rate_percentage": {
                        "type": "integer",
                        "description": "`round(failed_deliveries_count / deliveries_count * 100)`; `0` when there are no deliveries."
                      },
                      "last_delivery_at": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true,
                        "description": "Timestamp of the most recent delivery attempt (success or failure). `null` if never delivered."
                      },
                      "created_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "updated_at": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  }
                },
                "examples": {
                  "Two destinations": {
                    "value": [
                      {
                        "uuid": "11111111-2222-3333-4444-555555555555",
                        "label": "Production session webhooks",
                        "url": "https://yourapp.com/webhooks/didit",
                        "enabled": true,
                        "webhook_version": "v3",
                        "subscribed_events": [
                          "status.updated",
                          "data.updated"
                        ],
                        "subscribed_events_count": 2,
                        "deliveries_count": 12453,
                        "failed_deliveries_count": 14,
                        "average_response_time_ms": 187,
                        "error_rate_percentage": 0,
                        "last_delivery_at": "2026-05-17T09:21:18Z",
                        "created_at": "2026-01-04T08:00:00Z",
                        "updated_at": "2026-04-22T14:09:11Z"
                      },
                      {
                        "uuid": "22222222-3333-4444-5555-666666666666",
                        "label": "Staging mirror",
                        "url": "https://staging.yourapp.com/webhooks/didit",
                        "enabled": false,
                        "webhook_version": "v3",
                        "subscribed_events": [
                          "status.updated"
                        ],
                        "subscribed_events_count": 1,
                        "deliveries_count": 0,
                        "failed_deliveries_count": 0,
                        "average_response_time_ms": null,
                        "error_rate_percentage": 0,
                        "last_delivery_at": null,
                        "created_at": "2026-03-12T10:15:02Z",
                        "updated_at": "2026-03-12T10:15:02Z"
                      }
                    ]
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Create webhook destination",
        "description": "Create a webhook destination. The response includes the auto-generated `secret_shared_key` HMAC signing secret (also readable later via `GET /v3/webhook/destinations/{destination_uuid}/`). Always send `subscribed_events` with at least one event (no wildcards): an explicit empty list is rejected with 400, and omitting the field creates a destination subscribed to nothing, which never receives a webhook. The `(application, url)` pair must be unique among non-deleted destinations.",
        "operationId": "create_webhook_destination",
        "tags": [
          "Webhook"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/webhook/destinations/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"label\": \"Production session webhooks\",\n    \"url\": \"https://yourapp.com/webhooks/didit\",\n    \"webhook_version\": \"v3\",\n    \"subscribed_events\": [\"status.updated\", \"data.updated\"]\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/webhook/destinations/',\n    headers={\n        'x-api-key': os.environ['DIDIT_API_KEY'],\n        'Content-Type': 'application/json',\n    },\n    json={\n        'label': 'Production session webhooks',\n        'url': 'https://yourapp.com/webhooks/didit',\n        'webhook_version': 'v3',\n        'subscribed_events': ['status.updated', 'data.updated'],\n    },\n    timeout=10,\n)\nresp.raise_for_status()\ndestination = resp.json()\n# Store secret_shared_key in your secret manager.\nprint('Destination UUID:', destination['uuid'])\nprint('Signing secret:', destination['secret_shared_key'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/webhook/destinations/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': process.env.DIDIT_API_KEY,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    label: 'Production session webhooks',\n    url: 'https://yourapp.com/webhooks/didit',\n    webhook_version: 'v3',\n    subscribed_events: ['status.updated', 'data.updated'],\n  }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst destination = await resp.json();\nconsole.log('Store this secret securely:', destination.secret_shared_key);"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label",
                  "url"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Human-readable name displayed in Console → Webhooks. Helps distinguish destinations when you have several (e.g. \"Prod\", \"Staging mirror\")."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS URL that will receive `POST` requests. Must be unique per application among non-deleted destinations. Use a stable, hardened endpoint — Didit retries failed deliveries on a backoff schedule."
                  },
                  "enabled": {
                    "type": "boolean",
                    "default": true,
                    "description": "When `false`, the destination is saved but Didit will not push any webhooks to it. Useful for staging configs you want to keep parked."
                  },
                  "webhook_version": {
                    "type": "string",
                    "enum": [
                      "v1",
                      "v2",
                      "v3"
                    ],
                    "description": "Payload schema version Didit will send. `v3` is the current/recommended format; older versions are kept for backward compatibility with legacy integrations."
                  },
                  "subscribed_events": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/WebhookSubscribedEvents"
                      }
                    ],
                    "description": "Events to deliver. Effectively required: an explicit `[]` is rejected with 400, and omitting the field creates a destination with no subscriptions that receives nothing."
                  }
                }
              },
              "examples": {
                "session_updates": {
                  "summary": "Session status and data updates",
                  "value": {
                    "label": "Production session webhooks",
                    "url": "https://yourapp.com/webhooks/didit",
                    "webhook_version": "v3",
                    "subscribed_events": [
                      "status.updated",
                      "data.updated"
                    ]
                  }
                },
                "transaction_updates": {
                  "summary": "Transaction monitoring updates",
                  "value": {
                    "label": "Transaction monitoring webhooks",
                    "url": "https://yourapp.com/webhooks/didit-transactions",
                    "webhook_version": "v3",
                    "subscribed_events": [
                      "transaction.created",
                      "transaction.status.updated"
                    ]
                  }
                },
                "entity_updates": {
                  "summary": "User and Business entity updates",
                  "value": {
                    "label": "Entity lifecycle webhooks",
                    "url": "https://yourapp.com/webhooks/didit-entities",
                    "webhook_version": "v3",
                    "subscribed_events": [
                      "user.status.updated",
                      "user.data.updated",
                      "business.status.updated",
                      "business.data.updated",
                      "activity.created"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Destination created. `secret_shared_key` is the HMAC signing secret — store it now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Stable destination identifier. Use as `{destination_uuid}` in subsequent calls."
                    },
                    "label": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "webhook_version": {
                      "type": "string",
                      "enum": [
                        "v1",
                        "v2",
                        "v3"
                      ]
                    },
                    "secret_shared_key": {
                      "type": "string",
                      "description": "Auto-generated HMAC signing secret bound to this destination (43-character URL-safe token). Use it to verify the signature headers on incoming webhooks. Always returned in full to API-key callers; Console users need the `read:webhooks` permission, otherwise they see an empty string."
                    },
                    "subscribed_events": {
                      "$ref": "#/components/schemas/WebhookSubscribedEvents"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "summary": {
                      "type": "object",
                      "description": "Delivery health summary (all zero/null on a brand-new destination).",
                      "properties": {
                        "total_deliveries": {
                          "type": "integer"
                        },
                        "failed_deliveries": {
                          "type": "integer"
                        },
                        "error_rate_percentage": {
                          "type": "integer"
                        },
                        "min_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "avg_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "max_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "last_delivery_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Created": {
                    "value": {
                      "uuid": "11111111-2222-3333-4444-555555555555",
                      "label": "Production session webhooks",
                      "url": "https://yourapp.com/webhooks/didit",
                      "enabled": true,
                      "webhook_version": "v3",
                      "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ",
                      "subscribed_events": [
                        "status.updated",
                        "data.updated"
                      ],
                      "created_at": "2026-05-17T10:00:00Z",
                      "updated_at": "2026-05-17T10:00:00Z",
                      "summary": {
                        "total_deliveries": 0,
                        "failed_deliveries": 0,
                        "error_rate_percentage": 0,
                        "min_response_time_ms": null,
                        "avg_response_time_ms": null,
                        "max_response_time_ms": null,
                        "last_delivery_at": null
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed — usually a duplicate URL, missing/invalid `subscribed_events`, or unknown event name.",
            "content": {
              "application/json": {
                "examples": {
                  "Duplicate URL": {
                    "value": {
                      "url": [
                        "This webhook destination already exists for the application."
                      ]
                    }
                  },
                  "Empty events list": {
                    "value": {
                      "subscribed_events": [
                        "At least one subscribed event is required."
                      ]
                    }
                  },
                  "Unknown event": {
                    "value": {
                      "subscribed_events": [
                        "Unsupported webhook events: foo.bar"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/webhook/destinations/{destination_uuid}/": {
      "get": {
        "summary": "Get webhook destination",
        "description": "Retrieve one webhook destination with its `secret_shared_key` signing secret and delivery health summary. API-key callers always receive the secret in full; Console users without `read:webhooks` see an empty string.",
        "operationId": "get_webhook_destination",
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "destination_uuid",
            "in": "path",
            "required": true,
            "description": "UUID of the destination (the `uuid` returned from create / list).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "11111111-2222-3333-4444-555555555555"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.get(\n    f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json())"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst destination = await resp.json();\nconsole.log(destination);"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook destination details.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "label": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "webhook_version": {
                      "type": "string",
                      "enum": [
                        "v1",
                        "v2",
                        "v3"
                      ]
                    },
                    "secret_shared_key": {
                      "type": "string",
                      "description": "HMAC signing secret (43-character URL-safe token). Always returned in full to API-key callers; Console users need `read:webhooks`, otherwise an empty string."
                    },
                    "subscribed_events": {
                      "$ref": "#/components/schemas/WebhookSubscribedEvents"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "summary": {
                      "type": "object",
                      "description": "Delivery health metrics aggregated over the destination's lifetime.",
                      "properties": {
                        "total_deliveries": {
                          "type": "integer"
                        },
                        "failed_deliveries": {
                          "type": "integer"
                        },
                        "error_rate_percentage": {
                          "type": "integer"
                        },
                        "min_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "avg_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "max_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "last_delivery_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Healthy destination": {
                    "value": {
                      "uuid": "11111111-2222-3333-4444-555555555555",
                      "label": "Production session webhooks",
                      "url": "https://yourapp.com/webhooks/didit",
                      "enabled": true,
                      "webhook_version": "v3",
                      "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ",
                      "subscribed_events": [
                        "status.updated",
                        "data.updated"
                      ],
                      "created_at": "2026-01-04T08:00:00Z",
                      "updated_at": "2026-04-22T14:09:11Z",
                      "summary": {
                        "total_deliveries": 12453,
                        "failed_deliveries": 14,
                        "error_rate_percentage": 0,
                        "min_response_time_ms": 41,
                        "avg_response_time_ms": 187,
                        "max_response_time_ms": 2103,
                        "last_delivery_at": "2026-05-17T09:21:18Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No destination with this UUID exists, or it belongs to a different application, or it has been deleted.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Webhook destination not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "patch": {
        "summary": "Update webhook destination",
        "description": "Update a webhook destination. **`subscribed_events` is REPLACED wholesale, not merged** — fetch first, append, resend. Secret is not rotated.",
        "operationId": "update_webhook_destination",
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "destination_uuid",
            "in": "path",
            "required": true,
            "description": "UUID of the destination to update.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "11111111-2222-3333-4444-555555555555"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "description": "New human-readable name."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "New target URL. Must be unique within the application."
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "Set to `false` to pause deliveries to this destination without deleting it."
                  },
                  "webhook_version": {
                    "type": "string",
                    "enum": [
                      "v1",
                      "v2",
                      "v3"
                    ],
                    "description": "New payload schema version for future deliveries."
                  },
                  "subscribed_events": {
                    "$ref": "#/components/schemas/WebhookSubscribedEvents"
                  }
                }
              },
              "examples": {
                "Pause destination": {
                  "value": {
                    "enabled": false
                  }
                },
                "Add transaction events": {
                  "value": {
                    "subscribed_events": [
                      "status.updated",
                      "data.updated",
                      "transaction.created",
                      "transaction.status.updated"
                    ]
                  }
                },
                "Move target URL": {
                  "value": {
                    "label": "Production session webhooks v2",
                    "url": "https://api.yourapp.com/webhooks/didit/v2"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"subscribed_events\": [\n      \"status.updated\",\n      \"data.updated\",\n      \"transaction.created\"\n    ]\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.patch(\n    f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n    headers={\n        'x-api-key': os.environ['DIDIT_API_KEY'],\n        'Content-Type': 'application/json',\n    },\n    json={'enabled': False},\n    timeout=10,\n)\nresp.raise_for_status()\nprint('Paused destination:', resp.json()['label'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n  method: 'PATCH',\n  headers: {\n    'x-api-key': process.env.DIDIT_API_KEY,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({ enabled: false }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconsole.log(await resp.json());"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated webhook destination. Body has the same shape as `GET /v3/webhook/destinations/{destination_uuid}/`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "label": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "enabled": {
                      "type": "boolean"
                    },
                    "webhook_version": {
                      "type": "string",
                      "enum": [
                        "v1",
                        "v2",
                        "v3"
                      ]
                    },
                    "secret_shared_key": {
                      "type": "string"
                    },
                    "subscribed_events": {
                      "$ref": "#/components/schemas/WebhookSubscribedEvents"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "total_deliveries": {
                          "type": "integer"
                        },
                        "failed_deliveries": {
                          "type": "integer"
                        },
                        "error_rate_percentage": {
                          "type": "integer"
                        },
                        "min_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "avg_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "max_response_time_ms": {
                          "type": "integer",
                          "nullable": true
                        },
                        "last_delivery_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Paused": {
                    "value": {
                      "uuid": "11111111-2222-3333-4444-555555555555",
                      "label": "Production session webhooks",
                      "url": "https://yourapp.com/webhooks/didit",
                      "enabled": false,
                      "webhook_version": "v3",
                      "secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ",
                      "subscribed_events": [
                        "status.updated",
                        "data.updated"
                      ],
                      "created_at": "2026-01-04T08:00:00Z",
                      "updated_at": "2026-05-17T11:02:55Z",
                      "summary": {
                        "total_deliveries": 12453,
                        "failed_deliveries": 14,
                        "error_rate_percentage": 0,
                        "min_response_time_ms": 41,
                        "avg_response_time_ms": 187,
                        "max_response_time_ms": 2103,
                        "last_delivery_at": "2026-05-17T09:21:18Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (duplicate URL, empty/unknown events list, malformed body).",
            "content": {
              "application/json": {
                "examples": {
                  "Duplicate URL": {
                    "value": {
                      "url": [
                        "This webhook destination already exists for the application."
                      ]
                    }
                  },
                  "Empty events": {
                    "value": {
                      "subscribed_events": [
                        "At least one subscribed event is required."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Destination with this UUID does not exist (or is deleted, or belongs to another application).",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Webhook destination not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "delete": {
        "summary": "Delete webhook destination",
        "description": "Soft-delete a webhook destination. Future events stop; historical delivery records are retained for audit. Calling again returns 404.",
        "operationId": "delete_webhook_destination",
        "tags": [
          "Webhook"
        ],
        "parameters": [
          {
            "name": "destination_uuid",
            "in": "path",
            "required": true,
            "description": "UUID of the destination to delete.",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "11111111-2222-3333-4444-555555555555"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/webhook/destinations/11111111-2222-3333-4444-555555555555/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nuuid = '11111111-2222-3333-4444-555555555555'\nresp = requests.delete(\n    f'https://verification.didit.me/v3/webhook/destinations/{uuid}/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=10,\n)\nresp.raise_for_status()\nassert resp.status_code == 204"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const uuid = '11111111-2222-3333-4444-555555555555';\nconst resp = await fetch(`https://verification.didit.me/v3/webhook/destinations/${uuid}/`, {\n  method: 'DELETE',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (resp.status !== 204) throw new Error(`Delete failed: ${resp.status}`);"
          }
        ],
        "responses": {
          "204": {
            "description": "Destination deleted. No response body."
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No destination with this UUID exists for the application (already deleted or never existed).",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Webhook destination not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/wallet-screening/": {
      "post": {
        "summary": "Screen a wallet address",
        "description": "Run on-demand AML screening for a single crypto wallet/address without creating a transaction. Resolves your application's configured blockchain analytics provider (Merkle Science or Crystal), screens the address synchronously, and returns the normalised risk result. Nothing is written to the transactions table. One AML monitoring usage is billed per successful screening (sandbox applications are not billed).",
        "operationId": "screenWallet",
        "tags": [
          "Transactions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "wallet_address",
                  "blockchain"
                ],
                "properties": {
                  "wallet_address": {
                    "type": "string",
                    "description": "The crypto address to screen. Must match the format of the selected blockchain (e.g. a `0x`-prefixed 40-hex-char address for EVM chains). Alias: `address`.",
                    "example": "0x28c6c06298d514db089934071355e5743bf21d60"
                  },
                  "blockchain": {
                    "type": "string",
                    "description": "Asset / chain identifier. One of: BTC, ETH, LTC, XRP, BCH, DOGE, TRX, SOL, MATIC, BNB, USDT, USDC. Alias: `currency`.",
                    "enum": [
                      "BTC",
                      "ETH",
                      "LTC",
                      "XRP",
                      "BCH",
                      "DOGE",
                      "TRX",
                      "SOL",
                      "MATIC",
                      "BNB",
                      "USDT",
                      "USDC"
                    ],
                    "example": "ETH"
                  },
                  "direction": {
                    "type": "string",
                    "description": "Optional. Whether the address is being screened as an inbound (deposit) or outbound (withdrawal) counterparty. Accepts `inbound`, `outbound`, `deposit`, or `withdrawal`. Defaults to a neutral pre-transfer screen when omitted.",
                    "enum": [
                      "inbound",
                      "outbound",
                      "deposit",
                      "withdrawal"
                    ]
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/wallet-screening/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"wallet_address\": \"0x28c6c06298d514db089934071355e5743bf21d60\",\n    \"blockchain\": \"ETH\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/wallet-screening/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    json={\n        'wallet_address': '0x28c6c06298d514db089934071355e5743bf21d60',\n        'blockchain': 'ETH',\n    },\n    timeout=20,\n)\nresp.raise_for_status()\nresult = resp.json()\nprint(result['risk_score'], result['severity'], result['sanctions_hit'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const result = await fetch('https://verification.didit.me/v3/wallet-screening/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': process.env.DIDIT_API_KEY,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    wallet_address: '0x28c6c06298d514db089934071355e5743bf21d60',\n    blockchain: 'ETH',\n  }),\n}).then((r) => r.json());\nconsole.log(result.risk_score, result.severity, result.sanctions_hit);"
          }
        ],
        "responses": {
          "200": {
            "description": "The normalised wallet screening result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "string",
                      "description": "Provider that performed the screening (e.g. `merklescience`, `crystal`)."
                    },
                    "screening_type": {
                      "type": "string",
                      "enum": [
                        "WALLET_SCREENING"
                      ],
                      "description": "Always `WALLET_SCREENING` for this endpoint."
                    },
                    "risk_score": {
                      "type": "integer",
                      "description": "Normalised 0-100 risk score. Higher means greater exposure to risky entities."
                    },
                    "severity": {
                      "type": "string",
                      "enum": [
                        "UNKNOWN",
                        "LOW",
                        "MEDIUM",
                        "HIGH",
                        "CRITICAL"
                      ],
                      "description": "Severity bucket derived from `risk_score`."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "SCREENED",
                        "PENDING",
                        "ERROR"
                      ],
                      "description": "Screening outcome status."
                    },
                    "summary": {
                      "type": "string",
                      "description": "Human-readable summary of the screening result."
                    },
                    "wallet_address": {
                      "type": "string",
                      "description": "The screened address, echoed back."
                    },
                    "blockchain": {
                      "type": "string",
                      "description": "The blockchain that was screened."
                    },
                    "sanctions_hit": {
                      "type": "boolean",
                      "description": "True if the address has direct or indirect sanctions exposure."
                    },
                    "dominant_risk_category": {
                      "type": "string",
                      "nullable": true,
                      "description": "Highest-weighted high-risk category, or `null` when none is dominant (e.g. `sanctioned`, `mixer`, `stolen_funds`)."
                    },
                    "source_of_funds": {
                      "type": "array",
                      "description": "Where the address received funds from, attributed by entity. Each entry is an exposure breakdown.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "category": {
                            "type": "string",
                            "description": "Normalised risk category, e.g. `exchange_licensed`, `dex`, `defi`, `payment_processor`, `mixer`, `darknet_market`, `sanctioned`, `stolen_funds`, `high_risk_exchange`."
                          },
                          "entity_name": {
                            "type": "string",
                            "description": "Name of the attributed entity (e.g. `Binance.com`, `Tornado Cash`)."
                          },
                          "entity_type": {
                            "type": "string",
                            "description": "Provider entity type (e.g. `Exchange`, `Mixer`, `DeFi`, `Sanctions`, `Theft`)."
                          },
                          "entity_subtype": {
                            "type": "string",
                            "description": "More granular subtype (e.g. `Mandatory KYC and AML`, `OFAC SDN`, `Yield Aggregator`)."
                          },
                          "exposure_direction": {
                            "type": "string",
                            "enum": [
                              "incoming",
                              "outgoing",
                              "connected"
                            ],
                            "description": "Whether the funds came from (`incoming`) or went to (`outgoing`) this entity."
                          },
                          "exposure_type": {
                            "type": "string",
                            "enum": [
                              "direct",
                              "indirect"
                            ],
                            "description": "`direct` (1 hop) or `indirect` (multi-hop) exposure."
                          },
                          "is_direct": {
                            "type": "boolean",
                            "description": "True when the exposure is direct (0 hops)."
                          },
                          "amount_usd": {
                            "type": "number",
                            "description": "USD value attributed to this entity."
                          },
                          "percentage": {
                            "type": "number",
                            "description": "Share of the direction's total exposure, 0-100."
                          },
                          "hops": {
                            "type": "integer",
                            "description": "Number of hops to the entity (0 for direct exposure)."
                          },
                          "risk_level": {
                            "type": "string",
                            "enum": [
                              "UNKNOWN",
                              "LOW",
                              "MEDIUM",
                              "HIGH",
                              "CRITICAL"
                            ],
                            "description": "Risk level of the entity."
                          },
                          "country": {
                            "type": "string",
                            "description": "Primary ISO 3166-1 alpha-2 country of the entity, when known."
                          }
                        }
                      }
                    },
                    "destination_of_funds": {
                      "type": "array",
                      "description": "Where the address sent funds to, attributed by entity. Same item shape as `source_of_funds` with `exposure_direction` = `outgoing`.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "category": {
                            "type": "string"
                          },
                          "entity_name": {
                            "type": "string"
                          },
                          "entity_type": {
                            "type": "string"
                          },
                          "entity_subtype": {
                            "type": "string"
                          },
                          "exposure_direction": {
                            "type": "string",
                            "enum": [
                              "incoming",
                              "outgoing",
                              "connected"
                            ]
                          },
                          "exposure_type": {
                            "type": "string",
                            "enum": [
                              "direct",
                              "indirect"
                            ]
                          },
                          "is_direct": {
                            "type": "boolean"
                          },
                          "amount_usd": {
                            "type": "number"
                          },
                          "percentage": {
                            "type": "number"
                          },
                          "hops": {
                            "type": "integer"
                          },
                          "risk_level": {
                            "type": "string",
                            "enum": [
                              "UNKNOWN",
                              "LOW",
                              "MEDIUM",
                              "HIGH",
                              "CRITICAL"
                            ]
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "counterparty_connections": {
                      "type": "array",
                      "description": "Direct and indirect counterparty entities with received/sent amounts and risk levels.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "entity_name": {
                            "type": "string",
                            "description": "Counterparty entity name."
                          },
                          "entity_type": {
                            "type": "string",
                            "description": "Counterparty entity type."
                          },
                          "entity_subtype": {
                            "type": "string",
                            "description": "Counterparty entity subtype."
                          },
                          "risk_level": {
                            "type": "string",
                            "enum": [
                              "UNKNOWN",
                              "LOW",
                              "MEDIUM",
                              "HIGH",
                              "CRITICAL"
                            ]
                          },
                          "categories": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Risk categories attributed to the counterparty."
                          },
                          "received_usd": {
                            "type": "number",
                            "description": "USD received from the counterparty."
                          },
                          "sent_usd": {
                            "type": "number",
                            "description": "USD sent to the counterparty."
                          },
                          "received_hops": {
                            "type": "integer"
                          },
                          "sent_hops": {
                            "type": "integer"
                          },
                          "percentage": {
                            "type": "number"
                          },
                          "is_direct": {
                            "type": "boolean"
                          },
                          "country": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "provider": "merklescience",
                  "screening_type": "WALLET_SCREENING",
                  "risk_score": 72,
                  "severity": "HIGH",
                  "status": "SCREENED",
                  "summary": "Merkle Science risk: High (4/5). Dominant risk: mixer",
                  "wallet_address": "0x28c6c06298d514db089934071355e5743bf21d60",
                  "blockchain": "ETH",
                  "sanctions_hit": true,
                  "dominant_risk_category": "mixer",
                  "source_of_funds": [
                    {
                      "category": "exchange_licensed",
                      "entity_name": "Binance.com",
                      "entity_type": "Exchange",
                      "entity_subtype": "Mandatory KYC and AML",
                      "exposure_direction": "incoming",
                      "exposure_type": "direct",
                      "is_direct": true,
                      "amount_usd": 18420.55,
                      "percentage": 61.4,
                      "hops": 0,
                      "risk_level": "LOW",
                      "country": "SC"
                    },
                    {
                      "category": "mixer",
                      "entity_name": "Tornado Cash",
                      "entity_type": "Mixer",
                      "entity_subtype": "OFAC SDN",
                      "exposure_direction": "incoming",
                      "exposure_type": "indirect",
                      "is_direct": false,
                      "amount_usd": 11580.2,
                      "percentage": 38.6,
                      "hops": 2,
                      "risk_level": "CRITICAL",
                      "country": "RU"
                    }
                  ],
                  "destination_of_funds": [
                    {
                      "category": "exchange_licensed",
                      "entity_name": "Coinbase",
                      "entity_type": "Exchange",
                      "entity_subtype": "Mandatory KYC and AML",
                      "exposure_direction": "outgoing",
                      "exposure_type": "direct",
                      "is_direct": true,
                      "amount_usd": 30000.75,
                      "percentage": 100,
                      "hops": 0,
                      "risk_level": "LOW",
                      "country": "US"
                    }
                  ],
                  "counterparty_connections": [
                    {
                      "entity_name": "Tornado Cash",
                      "entity_type": "Mixer",
                      "entity_subtype": "OFAC SDN",
                      "risk_level": "CRITICAL",
                      "categories": [
                        "mixer"
                      ],
                      "received_usd": 11580.2,
                      "sent_usd": 0,
                      "received_hops": 2,
                      "sent_hops": 0,
                      "percentage": 38.6,
                      "is_direct": false,
                      "country": "RU"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error — malformed wallet address, missing `wallet_address`, or unsupported `blockchain`."
          },
          "401": {
            "description": "Missing or invalid `x-api-key`."
          },
          "409": {
            "description": "No AML provider is configured for the application, or the configured provider does not support wallet screening yet. Configure transaction monitoring first."
          },
          "502": {
            "description": "The AML provider could not complete the screening. Retry later."
          }
        }
      }
    },
    "/v3/transactions/": {
      "get": {
        "summary": "List transactions",
        "description": "Paginated list of monitored transactions, newest first (`txn_date` desc). `count` is capped at 100; use `total_transactions` for the true row count. This endpoint does not support filtering or search parameters.",
        "operationId": "listTransactions",
        "tags": [
          "Transactions"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/transactions/?limit=50' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/transactions/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    params={'limit': 50},\n    timeout=10,\n)\nresp.raise_for_status()\npage = resp.json()\nfor tx in page['results']:\n    print(\n        tx['transaction_number'],\n        tx['status'],\n        tx['severity'],\n        f\"{tx['amount']} {tx['currency']}\",\n        tx.get('subject_name') or tx.get('subject_vendor_data'),\n    )"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const url = new URL('https://verification.didit.me/v3/transactions/');\nurl.searchParams.set('limit', '50');\nconst page = await fetch(url, {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nfor (const tx of page.results) {\n  console.log(tx.transaction_number, tx.status, tx.severity, tx.amount, tx.currency);\n}"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of transaction summaries, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Capped count of matching transactions (exact up to 100, capped thereafter). Use `total_transactions` for the true count when paginating."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or `null` on the first page."
                    },
                    "total_transactions": {
                      "type": "integer",
                      "description": "Exact total count of (non-deleted) transactions for the application. Present on paginated responses."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "uuid": {
                            "type": "string",
                            "format": "uuid",
                            "description": "Stable Didit transaction identifier. Use as `{transaction_id}` in detail/notes/related calls."
                          },
                          "transaction_number": {
                            "type": "integer",
                            "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)."
                          },
                          "txn_id": {
                            "type": "string",
                            "description": "Customer-provided identifier from the create call (max 128 chars). Unique per application."
                          },
                          "transaction_type": {
                            "type": "string",
                            "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case."
                          },
                          "action_type": {
                            "type": "string",
                            "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`)."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "APPROVED",
                              "IN_REVIEW",
                              "DECLINED",
                              "AWAITING_USER"
                            ],
                            "description": "Current monitoring verdict."
                          },
                          "direction": {
                            "type": "string",
                            "enum": [
                              "INBOUND",
                              "OUTBOUND"
                            ],
                            "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted."
                          },
                          "amount": {
                            "type": "string",
                            "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places."
                          },
                          "currency": {
                            "type": "string",
                            "description": "Currency code of `amount` (e.g. `EUR`, `USD`, `BTC`)."
                          },
                          "preferred_currency_amount": {
                            "type": "string",
                            "nullable": true,
                            "description": "`amount` converted to the application's preferred currency, when available."
                          },
                          "preferred_currency_code": {
                            "type": "string",
                            "nullable": true,
                            "description": "Preferred currency code used for `preferred_currency_amount`."
                          },
                          "payment_details": {
                            "type": "string",
                            "nullable": true,
                            "description": "Free-text payment reference or memo from the submission."
                          },
                          "score": {
                            "type": "integer",
                            "description": "Risk score (typically 0–100). Higher = riskier."
                          },
                          "severity": {
                            "type": "string",
                            "nullable": true,
                            "enum": [
                              "UNKNOWN",
                              "LOW",
                              "MEDIUM",
                              "HIGH",
                              "CRITICAL"
                            ],
                            "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it."
                          },
                          "subject_name": {
                            "type": "string",
                            "nullable": true,
                            "description": "Full name of the `APPLICANT` party."
                          },
                          "subject_vendor_data": {
                            "type": "string",
                            "nullable": true,
                            "description": "Your internal user/business identifier for the applicant."
                          },
                          "subject_country": {
                            "type": "string",
                            "nullable": true,
                            "description": "Country code of the applicant, from the submitted `address.country` (typically ISO 3166-1 alpha-3)."
                          },
                          "counterparty_name": {
                            "type": "string",
                            "nullable": true,
                            "description": "Full name of the `COUNTERPARTY` party, if any."
                          },
                          "counterparty_vendor_data": {
                            "type": "string",
                            "nullable": true,
                            "description": "Your internal identifier for the counterparty."
                          },
                          "counterparty_country": {
                            "type": "string",
                            "nullable": true,
                            "description": "Country code of the counterparty, from the submitted `address.country`."
                          },
                          "tags": {
                            "type": "array",
                            "description": "Tags attached to the transaction (manually or by rules).",
                            "items": {
                              "type": "object",
                              "properties": {
                                "uuid": {
                                  "type": "string",
                                  "format": "uuid"
                                },
                                "name": {
                                  "type": "string"
                                },
                                "color": {
                                  "type": "string"
                                },
                                "description": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "source": {
                                  "type": "string",
                                  "enum": [
                                    "didit",
                                    "custom"
                                  ],
                                  "description": "Whether the tag is a Didit preset or a custom tag."
                                }
                              }
                            }
                          },
                          "txn_date": {
                            "type": "string",
                            "format": "date-time",
                            "description": "When the transaction occurred (from `transaction_at` at create time)."
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "When Didit recorded the submission."
                          },
                          "decision_reason_code": {
                            "type": "string",
                            "nullable": true,
                            "description": "Machine-readable reason for the current `status` (e.g. `WALLET_HIGH_RISK`)."
                          },
                          "decision_reason_label": {
                            "type": "string",
                            "nullable": true,
                            "description": "Human-readable label for `decision_reason_code`."
                          },
                          "assigned_to_name": {
                            "type": "string",
                            "nullable": true,
                            "description": "Display name of the Console reviewer assigned to this transaction."
                          }
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Single transaction": {
                    "value": {
                      "count": 1,
                      "next": null,
                      "previous": null,
                      "total_transactions": 1,
                      "results": [
                        {
                          "uuid": "abcdef12-3456-4890-abcd-ef1234567890",
                          "transaction_number": 4123,
                          "txn_id": "your-txn-2026-05-17-001",
                          "transaction_type": "finance",
                          "action_type": "withdrawal",
                          "status": "IN_REVIEW",
                          "direction": "OUTBOUND",
                          "amount": "1500",
                          "currency": "EUR",
                          "preferred_currency_amount": "1620.45",
                          "preferred_currency_code": "USD",
                          "payment_details": "Withdrawal to crypto wallet",
                          "txn_date": "2026-05-17T08:42:00Z",
                          "created_at": "2026-05-17T08:42:11Z",
                          "score": 78,
                          "severity": "HIGH",
                          "decision_reason_code": "WALLET_HIGH_RISK",
                          "decision_reason_label": "Destination wallet flagged as high risk",
                          "tags": [
                            {
                              "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                              "name": "Manual review",
                              "color": "#F59E0B",
                              "description": null,
                              "source": "custom"
                            }
                          ],
                          "subject_name": "Maria Garcia",
                          "subject_vendor_data": "user-12345",
                          "counterparty_name": null,
                          "counterparty_vendor_data": null,
                          "counterparty_country": null,
                          "subject_country": null,
                          "assigned_to_name": "Alex Reviewer"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot list transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "post": {
        "summary": "Create transaction",
        "description": "Submit a transaction for synchronous AML/risk monitoring. The transaction is created `APPROVED`, then your rules (and crypto screening, when applicable) run inline and may flip the returned `status` to `IN_REVIEW` or `DECLINED`. **`transaction_id` is your idempotency key** (max 128 chars, unique per app); reusing it returns 400. A `transaction.created` webhook is dispatched on success (sandbox applications are not billed and do not emit webhooks).",
        "operationId": "createTransaction",
        "tags": [
          "Transactions"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/transactions/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"transaction_id\": \"your-txn-2026-05-17-001\",\n    \"transaction_category\": \"finance\",\n    \"transaction_details\": {\n      \"direction\": \"outbound\",\n      \"amount\": 1500,\n      \"currency\": \"EUR\",\n      \"action_type\": \"withdrawal\"\n    },\n    \"subject\": {\n      \"entity_type\": \"individual\",\n      \"vendor_data\": \"user-12345\",\n      \"full_name\": \"Maria Garcia\"\n    }\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/transactions/',\n    headers={\n        'x-api-key': os.environ['DIDIT_API_KEY'],\n        'Content-Type': 'application/json',\n    },\n    json={\n        'transaction_id': 'your-txn-2026-05-17-001',\n        'transaction_category': 'finance',\n        'transaction_details': {\n            'direction': 'outbound',\n            'amount': 1500,\n            'currency': 'EUR',\n            'action_type': 'withdrawal',\n        },\n        'subject': {\n            'entity_type': 'individual',\n            'vendor_data': 'user-12345',\n            'full_name': 'Maria Garcia',\n        },\n    },\n    timeout=15,\n)\nresp.raise_for_status()\ntx = resp.json()\nprint(tx['txn_id'], '->', tx['status'], tx['severity'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/transactions/', {\n  method: 'POST',\n  headers: {\n    'x-api-key': process.env.DIDIT_API_KEY,\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    transaction_id: 'your-txn-2026-05-17-001',\n    transaction_category: 'finance',\n    transaction_details: {\n      direction: 'outbound',\n      amount: 1500,\n      currency: 'EUR',\n      action_type: 'withdrawal',\n    },\n    subject: {\n      entity_type: 'individual',\n      vendor_data: 'user-12345',\n      full_name: 'Maria Garcia',\n    },\n  }),\n});\nif (!resp.ok) throw new Error(await resp.text());\nconst tx = await resp.json();\nconsole.log(tx.txn_id, tx.status, tx.severity);"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "transaction_id": {
                    "type": "string",
                    "description": "Your unique identifier for this transaction (max 128 characters).",
                    "example": "your-txn-2026-05-17-001"
                  },
                  "transaction_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the transaction occurred. Defaults to now if omitted."
                  },
                  "time_zone": {
                    "type": "string",
                    "description": "IANA time zone identifier, e.g. `Europe/Madrid`."
                  },
                  "transaction_category": {
                    "type": "string",
                    "description": "Category of the transaction. CamelCase aliases (`travelRule`, `userPlatformEvent`, `gamblingBet`, …) are also accepted; the stored/echoed value is camelCase for multi-word categories.",
                    "example": "finance",
                    "enum": [
                      "finance",
                      "kyc",
                      "travel_rule",
                      "user_event",
                      "audit_trail_event",
                      "gambling_bet",
                      "gambling_limit_change",
                      "gambling_bonus_change",
                      "travelRule"
                    ]
                  },
                  "transaction_details": {
                    "type": "object",
                    "description": "Core financial details of the transaction.",
                    "required": [
                      "direction",
                      "amount",
                      "currency"
                    ],
                    "properties": {
                      "direction": {
                        "type": "string",
                        "description": "Direction of the transaction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).",
                        "example": "outbound",
                        "enum": [
                          "inbound",
                          "outbound",
                          "in",
                          "out"
                        ]
                      },
                      "amount": {
                        "type": "number",
                        "description": "Transaction amount (up to 18 decimal places).",
                        "example": 1500
                      },
                      "currency": {
                        "type": "string",
                        "description": "Currency code, e.g. `EUR`, `USD`, `BTC`.",
                        "example": "EUR"
                      },
                      "currency_kind": {
                        "type": "string",
                        "description": "Whether the currency is `fiat` or `crypto`."
                      },
                      "amount_in_default_currency": {
                        "type": "number",
                        "nullable": true,
                        "description": "Pre-converted amount in the default currency. If omitted, automatic FX conversion is attempted."
                      },
                      "default_currency": {
                        "type": "string",
                        "description": "The default currency code for the converted amount."
                      },
                      "payment_details": {
                        "type": "string",
                        "nullable": true,
                        "description": "Free-text payment reference or memo."
                      },
                      "payment_reference_id": {
                        "type": "string",
                        "nullable": true,
                        "description": "External payment system reference. For crypto AML transaction-hash screening, send the blockchain transaction hash and also provide `subject.payment_method.account_id` with the related address."
                      },
                      "action_type": {
                        "type": "string",
                        "nullable": true,
                        "description": "Sub-type of the transaction, e.g. `deposit`, `withdrawal`, `transfer`."
                      }
                    }
                  },
                  "subject": {
                    "type": "object",
                    "description": "The subject (applicant) of the transaction.",
                    "required": [
                      "vendor_data",
                      "full_name"
                    ],
                    "properties": {
                      "entity_type": {
                        "type": "string",
                        "description": "Entity type: `individual` or `company`."
                      },
                      "vendor_data": {
                        "type": "string",
                        "description": "Your internal user/business identifier.",
                        "example": "user-12345"
                      },
                      "full_name": {
                        "type": "string",
                        "description": "Full name of the subject.",
                        "example": "Maria Garcia"
                      },
                      "first_name": {
                        "type": "string",
                        "description": "First name of the subject."
                      },
                      "last_name": {
                        "type": "string",
                        "description": "Last name of the subject."
                      },
                      "date_of_birth": {
                        "type": "string",
                        "format": "date",
                        "description": "Date of birth (YYYY-MM-DD)."
                      },
                      "address": {
                        "type": "object",
                        "description": "Address object.",
                        "properties": {
                          "country": {
                            "type": "string",
                            "description": "Country code (ISO 3166-1 alpha-3 recommended). Copied to the party's `country_code`."
                          },
                          "town": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "street": {
                            "type": "string"
                          },
                          "post_code": {
                            "type": "string"
                          }
                        }
                      },
                      "institution_details": {
                        "type": "object",
                        "description": "Institution details (for institutional subjects).",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "code": {
                            "type": "string"
                          },
                          "address": {
                            "type": "object"
                          }
                        }
                      },
                      "device_context": {
                        "type": "object",
                        "description": "Device context captured at transaction time."
                      },
                      "payment_method": {
                        "type": "object",
                        "description": "Payment method used by the subject.",
                        "properties": {
                          "method_type": {
                            "type": "string",
                            "description": "Type of payment method: `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`."
                          },
                          "account_id": {
                            "type": "string",
                            "description": "Account identifier (e.g. IBAN, wallet address). For crypto screening, this should contain the wallet address for this participant. Inbound transaction-hash screening requires the service/customer deposit address on the subject. Outbound screening uses the destination wallet on the counterparty."
                          },
                          "issuing_country": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-3 country code of the issuer."
                          }
                        }
                      }
                    }
                  },
                  "counterparty": {
                    "type": "object",
                    "description": "The counterparty of the transaction (optional).",
                    "properties": {
                      "entity_type": {
                        "type": "string",
                        "description": "Entity type: `individual` or `company`."
                      },
                      "vendor_data": {
                        "type": "string",
                        "description": "Your internal identifier for the counterparty."
                      },
                      "full_name": {
                        "type": "string",
                        "description": "Full name of the counterparty."
                      },
                      "first_name": {
                        "type": "string"
                      },
                      "last_name": {
                        "type": "string"
                      },
                      "date_of_birth": {
                        "type": "string",
                        "format": "date"
                      },
                      "address": {
                        "type": "object",
                        "description": "Address object with `country` (ISO 3166-1 alpha-3)."
                      },
                      "institution_details": {
                        "type": "object"
                      },
                      "device_context": {
                        "type": "object"
                      },
                      "payment_method": {
                        "type": "object",
                        "description": "Payment method used by the counterparty. On travelRule transactions, `method_type: \"unhosted_wallet\"` declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in `UNCONFIRMED_OWNERSHIP`, and (when `auto_wallet_verification` is enabled) a wallet-ownership widget session is auto-minted and returned in the transaction's `action_required` block.",
                        "properties": {
                          "method_type": {
                            "type": "string",
                            "description": "Type of payment method: `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`. Use `unhosted_wallet` to declare a self-hosted wallet for the Travel Rule."
                          },
                          "account_id": {
                            "type": "string",
                            "description": "Account identifier (e.g. IBAN or wallet address)."
                          },
                          "issuing_country": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "custom_properties": {
                    "type": "object",
                    "description": "Arbitrary key-value pairs attached to the transaction. Each key can be referenced in rule conditions with the field path `custom_values.<key>`.",
                    "additionalProperties": true,
                    "example": {
                      "merchant_id": "test",
                      "bank_name": "test_2"
                    }
                  },
                  "travel_rule_details": {
                    "type": "object",
                    "description": "Travel Rule compliance metadata.",
                    "required": [
                      "status"
                    ],
                    "properties": {
                      "status": {
                        "type": "string"
                      },
                      "protocol": {
                        "type": "string"
                      },
                      "required": {
                        "type": "boolean"
                      },
                      "obligations_count": {
                        "type": "integer"
                      },
                      "originator_data": {
                        "type": "object"
                      },
                      "beneficiary_data": {
                        "type": "object"
                      },
                      "metadata": {
                        "type": "object"
                      }
                    }
                  },
                  "network_snapshot": {
                    "type": "object",
                    "description": "Pre-computed network graph snapshot.",
                    "properties": {
                      "nodes": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      "edges": {
                        "type": "array",
                        "items": {
                          "type": "object"
                        }
                      },
                      "metrics": {
                        "type": "object"
                      }
                    }
                  },
                  "include_crypto_screening": {
                    "type": "boolean",
                    "nullable": true,
                    "description": "Per-transaction override for crypto blockchain analytics screening. When `true`, crypto screening is performed regardless of the application default. When `false`, crypto screening is skipped even if enabled in the application settings. When omitted or `null`, the application-level default configured in the Console is used. Crypto screening requires `transaction_details.direction`, `currency_kind: \"crypto\"`, a chain/currency, and the wallet address selected by the direction: inbound pre-transfer screening uses `counterparty.payment_method.account_id`, inbound transaction-hash screening uses `subject.payment_method.account_id`, and outbound screening uses `counterparty.payment_method.account_id`. When `transaction_details.payment_reference_id` contains a blockchain transaction hash, Didit performs transaction screening and returns transaction summary enrichment when available."
                  }
                },
                "required": [
                  "transaction_id",
                  "transaction_category",
                  "transaction_details",
                  "subject"
                ]
              },
              "examples": {
                "Minimal fiat withdrawal": {
                  "summary": "Smallest acceptable payload for a fiat outbound transfer",
                  "value": {
                    "transaction_id": "your-txn-2026-05-17-001",
                    "transaction_category": "finance",
                    "transaction_details": {
                      "direction": "outbound",
                      "amount": 1500,
                      "currency": "EUR",
                      "action_type": "withdrawal"
                    },
                    "subject": {
                      "entity_type": "individual",
                      "vendor_data": "user-12345",
                      "full_name": "Maria Garcia"
                    }
                  }
                },
                "Crypto outbound with screening": {
                  "summary": "Outbound crypto withdrawal to an unhosted wallet, opt-in screening",
                  "value": {
                    "transaction_id": "your-txn-2026-05-17-002",
                    "transaction_category": "finance",
                    "include_crypto_screening": true,
                    "transaction_details": {
                      "direction": "outbound",
                      "amount": 0.5,
                      "currency": "BTC",
                      "currency_kind": "crypto",
                      "amount_in_default_currency": 32500,
                      "default_currency": "USD",
                      "action_type": "withdrawal"
                    },
                    "subject": {
                      "entity_type": "individual",
                      "vendor_data": "user-12345",
                      "full_name": "Maria Garcia",
                      "payment_method": {
                        "method_type": "crypto_wallet",
                        "account_id": "bc1qexampleexchangewalletaddressxyz",
                        "issuing_country": "ESP"
                      }
                    },
                    "counterparty": {
                      "entity_type": "individual",
                      "payment_method": {
                        "method_type": "unhosted_wallet",
                        "account_id": "bc1qexampleexternalwalletaddressabc"
                      }
                    }
                  }
                },
                "Travel Rule transfer": {
                  "summary": "Outbound crypto transfer with Travel Rule originator/beneficiary data",
                  "value": {
                    "transaction_id": "your-txn-2026-05-17-003",
                    "transaction_category": "travel_rule",
                    "transaction_details": {
                      "direction": "outbound",
                      "amount": 25000,
                      "currency": "USDT",
                      "currency_kind": "crypto",
                      "payment_reference_id": "0xabcdef1234567890",
                      "action_type": "transfer"
                    },
                    "subject": {
                      "entity_type": "individual",
                      "vendor_data": "user-12345",
                      "full_name": "Maria Garcia",
                      "date_of_birth": "1992-03-14",
                      "address": {
                        "country": "ESP",
                        "town": "Madrid",
                        "street": "Calle Mayor 1",
                        "post_code": "28013"
                      }
                    },
                    "counterparty": {
                      "entity_type": "individual",
                      "full_name": "John Beneficiary",
                      "institution_details": {
                        "name": "Beneficiary VASP, Inc.",
                        "code": "BVASP-US"
                      }
                    },
                    "travel_rule_details": {
                      "status": "PENDING",
                      "protocol": "IVMS101",
                      "required": true,
                      "originator_data": {
                        "name": "Maria Garcia"
                      },
                      "beneficiary_data": {
                        "name": "John Beneficiary"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Transaction submitted and monitored. The body is the full transaction detail (same shape as `GET /v3/transactions/{transaction_id}/`), reflecting any rule outcomes already applied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDetail"
                },
                "examples": {
                  "Approved with no rule hits": {
                    "value": {
                      "uuid": "abcdef12-3456-4890-abcd-ef1234567890",
                      "transaction_number": 4123,
                      "txn_id": "your-txn-2026-05-17-001",
                      "txn_date": "2026-05-17T08:42:00Z",
                      "zone_id": "Europe/Madrid",
                      "transaction_type": "finance",
                      "action_type": "withdrawal",
                      "direction": "OUTBOUND",
                      "status": "APPROVED",
                      "amount": "1500",
                      "currency": "EUR",
                      "currency_type": "fiat",
                      "amount_in_default_currency": "1620.45",
                      "default_currency_code": "USD",
                      "preferred_currency_amount": "1620.45",
                      "preferred_currency_code": "USD",
                      "payment_details": "Withdrawal to crypto wallet",
                      "payment_txn_id": null,
                      "score": 0,
                      "severity": null,
                      "decision_reason_code": null,
                      "decision_reason_label": null,
                      "vendor_data": "user-12345",
                      "metadata": {
                        "subject": {
                          "entity_type": "individual",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "address": {},
                          "institution_details": {},
                          "device_context": {}
                        },
                        "counterparty": {}
                      },
                      "props": {},
                      "tags": [],
                      "parties": [
                        {
                          "uuid": "b1111111-2222-4333-8444-555555555555",
                          "role": "APPLICANT",
                          "entity_type": "individual",
                          "kind": "USER",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "first_name": null,
                          "last_name": null,
                          "country_code": null,
                          "dob": null,
                          "address": {},
                          "institution_info": {},
                          "device": {},
                          "external_party_snapshot": null
                        }
                      ],
                      "payment_methods": [],
                      "activities": [
                        {
                          "uuid": "d4e5f6a7-8901-4bcd-9ef0-123456789abc",
                          "activity_type": "TRANSACTION_CREATED",
                          "source": "TRANSACTION",
                          "status": "APPROVED",
                          "title": "withdrawal",
                          "description": "Transaction recorded.",
                          "metadata": {},
                          "occurred_at": "2026-05-17T08:42:11Z"
                        }
                      ],
                      "alerts": [],
                      "rule_runs": [],
                      "provider_results": [],
                      "travel_rule": null,
                      "network_snapshot": null,
                      "remediation": null,
                      "cost_breakdown": null
                    }
                  },
                  "Flagged for review": {
                    "value": {
                      "uuid": "abcdef12-3456-4890-abcd-ef1234567891",
                      "transaction_number": 4124,
                      "txn_id": "your-txn-2026-05-17-002",
                      "txn_date": "2026-05-17T08:42:00Z",
                      "zone_id": "Europe/Madrid",
                      "transaction_type": "finance",
                      "action_type": "withdrawal",
                      "direction": "OUTBOUND",
                      "status": "IN_REVIEW",
                      "amount": "0.5",
                      "currency": "BTC",
                      "currency_type": "crypto",
                      "amount_in_default_currency": "32500",
                      "default_currency_code": "USD",
                      "preferred_currency_amount": "32500",
                      "preferred_currency_code": "USD",
                      "payment_details": "Withdrawal to crypto wallet",
                      "payment_txn_id": null,
                      "score": 78,
                      "severity": "HIGH",
                      "decision_reason_code": "WALLET_HIGH_RISK",
                      "decision_reason_label": "Destination wallet flagged as high risk",
                      "vendor_data": "user-12345",
                      "metadata": {
                        "subject": {
                          "entity_type": "individual",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "address": {},
                          "institution_details": {},
                          "device_context": {}
                        },
                        "counterparty": {}
                      },
                      "props": {
                        "order_id": "ord-9988"
                      },
                      "tags": [],
                      "parties": [
                        {
                          "uuid": "b1111111-2222-4333-8444-555555555555",
                          "role": "APPLICANT",
                          "entity_type": "individual",
                          "kind": "USER",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "first_name": null,
                          "last_name": null,
                          "country_code": null,
                          "dob": null,
                          "address": {},
                          "institution_info": {},
                          "device": {},
                          "external_party_snapshot": null
                        }
                      ],
                      "payment_methods": [],
                      "activities": [],
                      "alerts": [
                        {
                          "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef",
                          "title": "Destination wallet flagged as high risk",
                          "description": "Provider risk score exceeded the configured threshold.",
                          "severity": "HIGH",
                          "status": "OPEN",
                          "source": "RULE",
                          "metadata": {},
                          "created_at": "2026-05-17T08:42:12Z",
                          "due_at": null
                        }
                      ],
                      "rule_runs": [
                        {
                          "uuid": "e5f6a7b8-9012-4cde-af01-23456789abcd",
                          "rule": "f6a7b8c9-0123-4def-b012-3456789abcde",
                          "rule_title": "High-risk wallet screening",
                          "rule_description": null,
                          "rule_category": "crypto",
                          "matched": true,
                          "is_test": false,
                          "mode": "ACTIVE",
                          "severity": "HIGH",
                          "score_delta": 78,
                          "status_target": "IN_REVIEW",
                          "action_summary": {
                            "add_score": 78,
                            "change_status": "IN_REVIEW"
                          },
                          "metadata": {},
                          "created_at": "2026-05-17T08:42:12Z"
                        }
                      ],
                      "provider_results": [],
                      "travel_rule": null,
                      "network_snapshot": null,
                      "remediation": null,
                      "cost_breakdown": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (missing required fields, duplicate `transaction_id`, unknown enum value, invalid currency, etc.).",
            "content": {
              "application/json": {
                "examples": {
                  "Missing required field": {
                    "value": {
                      "transaction_details": {
                        "amount": [
                          "This field is required."
                        ]
                      }
                    }
                  },
                  "Duplicate transaction_id": {
                    "value": {
                      "transaction_id": [
                        "A transaction with this transaction_id already exists."
                      ]
                    }
                  },
                  "Invalid category": {
                    "value": {
                      "non_field_errors": [
                        "Unsupported transaction category."
                      ]
                    }
                  },
                  "Missing subject identity": {
                    "value": {
                      "subject": [
                        "subject.vendor_data and subject.full_name are required."
                      ]
                    }
                  },
                  "Blocklisted participant": {
                    "value": {
                      "vendor_data": [
                        "The individual 'user-12345' is in the blocklist and cannot participate in transactions."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot submit transactions for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/transactions/{transaction_id}/": {
      "get": {
        "summary": "Get transaction",
        "description": "Retrieve the full monitoring record for one transaction: parties, score/severity, rule runs, alerts, Travel Rule outcome, remediation session.",
        "operationId": "getTransaction",
        "tags": [
          "Transactions"
        ],
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "description": "Didit-stable transaction UUID (the `uuid` returned from create / list, **not** your `txn_id`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "abcdef12-3456-7890-abcd-ef1234567890"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/transactions/abcdef12-3456-7890-abcd-ef1234567890/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nuuid = 'abcdef12-3456-7890-abcd-ef1234567890'\nresp = requests.get(\n    f'https://verification.didit.me/v3/transactions/{uuid}/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=10,\n)\nresp.raise_for_status()\ntx = resp.json()\nfor alert in tx['alerts']:\n    print(alert.get('code'), alert.get('severity'))"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const uuid = 'abcdef12-3456-7890-abcd-ef1234567890';\nconst resp = await fetch(`https://verification.didit.me/v3/transactions/${uuid}/`, {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n});\nif (!resp.ok) throw new Error(`Lookup failed: ${resp.status}`);\nconst tx = await resp.json();\nconsole.log(tx.status, tx.severity, tx.alerts.length, 'alerts');"
          }
        ],
        "responses": {
          "200": {
            "description": "Full transaction detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDetail"
                },
                "examples": {
                  "In review with an alert": {
                    "value": {
                      "uuid": "abcdef12-3456-4890-abcd-ef1234567890",
                      "transaction_number": 4123,
                      "txn_id": "your-txn-2026-05-17-001",
                      "txn_date": "2026-05-17T08:42:00Z",
                      "zone_id": "Europe/Madrid",
                      "transaction_type": "finance",
                      "action_type": "withdrawal",
                      "direction": "OUTBOUND",
                      "status": "IN_REVIEW",
                      "amount": "1500",
                      "currency": "EUR",
                      "currency_type": "fiat",
                      "amount_in_default_currency": "1620.45",
                      "default_currency_code": "USD",
                      "preferred_currency_amount": "1620.45",
                      "preferred_currency_code": "USD",
                      "payment_details": "Withdrawal to crypto wallet",
                      "payment_txn_id": null,
                      "score": 78,
                      "severity": "HIGH",
                      "decision_reason_code": "WALLET_HIGH_RISK",
                      "decision_reason_label": "Destination wallet flagged as high risk",
                      "vendor_data": "user-12345",
                      "metadata": {
                        "subject": {
                          "entity_type": "individual",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "address": {},
                          "institution_details": {},
                          "device_context": {}
                        },
                        "counterparty": {}
                      },
                      "props": {
                        "order_id": "ord-9988"
                      },
                      "tags": [],
                      "parties": [
                        {
                          "uuid": "b1111111-2222-4333-8444-555555555555",
                          "role": "APPLICANT",
                          "entity_type": "individual",
                          "kind": "USER",
                          "vendor_data": "user-12345",
                          "full_name": "Maria Garcia",
                          "first_name": null,
                          "last_name": null,
                          "country_code": null,
                          "dob": null,
                          "address": {},
                          "institution_info": {},
                          "device": {},
                          "external_party_snapshot": null
                        }
                      ],
                      "payment_methods": [],
                      "activities": [],
                      "alerts": [
                        {
                          "uuid": "a7b8c9d0-1234-4abc-9def-567890abcdef",
                          "title": "Destination wallet flagged as high risk",
                          "description": "Provider risk score exceeded the configured threshold.",
                          "severity": "HIGH",
                          "status": "OPEN",
                          "source": "RULE",
                          "metadata": {},
                          "created_at": "2026-05-17T08:42:12Z",
                          "due_at": null
                        }
                      ],
                      "rule_runs": [],
                      "provider_results": [],
                      "travel_rule": null,
                      "network_snapshot": null,
                      "remediation": null,
                      "cost_breakdown": null
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot read this application's transactions. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No transaction with this UUID exists for the application (deleted, never created, belongs to another application, or the path segment is not a valid UUID).",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Throttled": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/transactions/sdk-token/": {
      "post": {
        "summary": "Mint an SDK transaction token",
        "description": "Mint a short-lived scoped token your app passes to the Didit SDKs so they can submit transactions directly from the end-user's device via `POST /v1/transactions/`. The token is bound to one `vendor_data` (your user id): every transaction submitted with it has its subject identity enforced server-side from that binding, so a tampered client cannot submit on behalf of another user. Mint tokens from your backend - never ship the API key itself in an app. camelCase aliases (`vendorData`, `ttlSeconds`, `maxUses`) are also accepted.",
        "operationId": "createTransactionSdkToken",
        "tags": [
          "Transactions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vendor_data": {
                    "type": "string",
                    "description": "Your internal identifier for the end user this token is scoped to. Enforced as the subject identity of every transaction submitted with the token.",
                    "example": "user-042"
                  },
                  "ttl_seconds": {
                    "type": "integer",
                    "default": 900,
                    "maximum": 86400,
                    "description": "Token lifetime in seconds. Defaults to 900 (15 minutes); maximum 86400 (24 hours)."
                  },
                  "max_uses": {
                    "type": "integer",
                    "nullable": true,
                    "description": "Maximum number of successful submissions allowed with this token. Omit or null for unlimited uses within the TTL."
                  }
                },
                "required": [
                  "vendor_data"
                ]
              },
              "example": {
                "vendor_data": "user-042",
                "ttl_seconds": 900,
                "max_uses": 3
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/transactions/sdk-token/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"vendor_data\": \"user-042\", \"ttl_seconds\": 900, \"max_uses\": 3}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/transactions/sdk-token/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    json={'vendor_data': 'user-042', 'ttl_seconds': 900, 'max_uses': 3},\n    timeout=10,\n)\nresp.raise_for_status()\ntoken = resp.json()\nprint(token['sdk_token'], token['expires_at'])"
          }
        ],
        "responses": {
          "201": {
            "description": "The minted token. Hand `sdk_token` to your app; it authenticates the device-facing `/v1/transactions/` endpoints via the `X-Transaction-Token` header until `expires_at`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sdk_token": {
                      "type": "string",
                      "description": "The scoped transaction token to pass to the SDK."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the token stops being accepted."
                    }
                  }
                },
                "example": {
                  "sdk_token": "q7Zt...urlsafe",
                  "expires_at": "2026-07-07T12:15:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Validation failed: missing `vendor_data`, or `ttl_seconds` outside the allowed range.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing vendor_data": {
                    "value": {
                      "vendor_data": [
                        "This field is required."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid API Key": {
                    "value": {
                      "detail": "Invalid API Key."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/": {
      "post": {
        "summary": "Submit a transaction from an SDK",
        "description": "Device-facing endpoint the Didit SDKs call to submit a transaction directly from the end-user's device. Authentication is a scoped token from `POST /v3/transactions/sdk-token/` sent in the `X-Transaction-Token` header instead of an API key, and the endpoint is CORS-enabled so browsers can call it cross-origin. The payload uses the camelCase wire aliases shown below (the SDK `submitTransaction` methods build it for you). Three server-side guarantees: the application is always taken from the token; the subject identity (`externalUserId`) is always enforced from the token's `vendor_data`, so spoofed payload values are ignored; and device intelligence (the SDK device fingerprint plus server-derived IP and user agent) is attached to the subject automatically.",
        "operationId": "submitTransactionFromSdk",
        "tags": [
          "Transactions"
        ],
        "security": [
          {
            "TransactionTokenAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "txnId": {
                    "type": "string",
                    "description": "Your unique identifier for this transaction (maps to `transaction_id`, max 128 characters).",
                    "example": "wd-2026-07-07-0042"
                  },
                  "txnDate": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the transaction occurred (maps to `transaction_at`). Defaults to now if omitted."
                  },
                  "zoneId": {
                    "type": "string",
                    "description": "IANA time zone identifier (maps to `time_zone`), e.g. `Europe/Madrid`."
                  },
                  "type": {
                    "type": "string",
                    "description": "Transaction category (maps to `transaction_category`), e.g. `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`.",
                    "example": "travelRule"
                  },
                  "info": {
                    "type": "object",
                    "description": "Core financial details of the transaction (maps to `transaction_details`).",
                    "required": [
                      "direction",
                      "amount",
                      "currency"
                    ],
                    "properties": {
                      "direction": {
                        "type": "string",
                        "description": "Direction relative to the subject. Case-insensitive; `in`/`out` are accepted as aliases. Echoed back uppercase (`INBOUND`/`OUTBOUND`).",
                        "example": "out"
                      },
                      "amount": {
                        "type": "number",
                        "description": "Transaction amount (up to 18 decimal places). A decimal string is also accepted.",
                        "example": 0.25
                      },
                      "currency": {
                        "type": "string",
                        "description": "Currency code, e.g. `EUR`, `USD`, `ETH`.",
                        "example": "ETH"
                      },
                      "currencyType": {
                        "type": "string",
                        "description": "Whether the currency is `fiat` or `crypto` (maps to `currency_kind`)."
                      },
                      "amountInDefaultCurrency": {
                        "type": "number",
                        "nullable": true,
                        "description": "Pre-converted amount in the default currency (maps to `amount_in_default_currency`). If omitted, automatic FX conversion is attempted."
                      },
                      "defaultCurrencyCode": {
                        "type": "string",
                        "description": "The default currency code for the converted amount (maps to `default_currency`)."
                      },
                      "paymentDetails": {
                        "type": "string",
                        "nullable": true,
                        "description": "Free-text payment reference or memo (maps to `payment_details`)."
                      },
                      "paymentTxnId": {
                        "type": "string",
                        "nullable": true,
                        "description": "External payment reference (maps to `payment_reference_id`); the on-chain transaction hash for crypto transactions."
                      },
                      "type": {
                        "type": "string",
                        "nullable": true,
                        "description": "Sub-type of the transaction (maps to `action_type`), e.g. `deposit`, `withdrawal`, `transfer`."
                      },
                      "cryptoParams": {
                        "type": "object",
                        "description": "Crypto-specific parameters, e.g. `{\"crypto_chain\": \"ETH\"}`."
                      }
                    }
                  },
                  "subject": {
                    "type": "object",
                    "description": "The subject (applicant) of the transaction. `externalUserId` is ignored here: the subject identity is always enforced from the token's `vendor_data`.",
                    "required": [
                      "fullName"
                    ],
                    "properties": {
                      "type": {
                        "type": "string",
                        "description": "Entity type (maps to `entity_type`): `individual` (default) or `company`."
                      },
                      "externalUserId": {
                        "type": "string",
                        "description": "Ignored on the subject - enforced server-side from the token's `vendor_data`."
                      },
                      "fullName": {
                        "type": "string",
                        "example": "Jane Doe"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "dob": {
                        "type": "string",
                        "format": "date",
                        "description": "Date of birth (YYYY-MM-DD; maps to `date_of_birth`)."
                      },
                      "address": {
                        "type": "object",
                        "description": "Address object with `country` (ISO 3166-1 alpha-3), `town`, `state`, `street`, `post_code`."
                      },
                      "institutionInfo": {
                        "type": "object",
                        "description": "Institution details for institutional participants (maps to `institution_details`)."
                      },
                      "device": {
                        "type": "object",
                        "description": "Optional explicit device context. Merged with the automatically captured device intelligence; server-derived fields win on conflict."
                      },
                      "paymentMethod": {
                        "type": "object",
                        "description": "Payment method used by the subject (maps to `payment_method`).",
                        "properties": {
                          "type": {
                            "type": "string",
                            "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`."
                          },
                          "accountId": {
                            "type": "string",
                            "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address."
                          },
                          "issuingCountry": {
                            "type": "string",
                            "description": "ISO 3166-1 alpha-3 country code of the issuer (maps to `issuing_country`)."
                          }
                        }
                      }
                    }
                  },
                  "counterparty": {
                    "type": "object",
                    "description": "The counterparty of the transaction (optional). Same shape as `subject`; `externalUserId` is meaningful here.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "description": "Entity type (maps to `entity_type`): `individual` or `company`."
                      },
                      "externalUserId": {
                        "type": "string",
                        "description": "Your internal identifier for the counterparty (maps to `vendor_data`)."
                      },
                      "fullName": {
                        "type": "string"
                      },
                      "firstName": {
                        "type": "string"
                      },
                      "lastName": {
                        "type": "string"
                      },
                      "dob": {
                        "type": "string",
                        "format": "date"
                      },
                      "address": {
                        "type": "object"
                      },
                      "institutionInfo": {
                        "type": "object"
                      },
                      "device": {
                        "type": "object"
                      },
                      "paymentMethod": {
                        "type": "object",
                        "description": "Payment method used by the counterparty (maps to `payment_method`). On travelRule transactions, `type: \"unhosted_wallet\"` declares the counterparty wallet as self-hosted: the transfer skips counterparty-VASP routing, is created directly in `UNCONFIRMED_OWNERSHIP`, and (when `auto_wallet_verification` is enabled) a wallet-ownership widget session is auto-minted and returned in `action_required` so the SDK can launch it.",
                        "properties": {
                          "type": {
                            "type": "string",
                            "description": "Payment method kind (maps to `method_type`), e.g. `bank_card`, `bank_account`, `crypto_wallet`, `ewallet`, `unhosted_wallet`, `other`. Use `unhosted_wallet` to declare a self-hosted wallet for the Travel Rule."
                          },
                          "accountId": {
                            "type": "string",
                            "description": "Account identifier (maps to `account_id`): IBAN, card fingerprint, or wallet address."
                          },
                          "issuingCountry": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  },
                  "props": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Custom key-value pairs (maps to `custom_properties`). Each key can be referenced in rule conditions as `custom_values.<key>`."
                  },
                  "travelRule": {
                    "type": "object",
                    "description": "Travel Rule details (maps to `travel_rule_details`). When Travel Rule is enabled for the application, Didit ignores any `status` you send and drives the exchange itself.",
                    "required": [
                      "status"
                    ],
                    "properties": {
                      "status": {
                        "type": "string"
                      },
                      "protocol": {
                        "type": "string"
                      },
                      "required": {
                        "type": "boolean"
                      },
                      "obligationsCount": {
                        "type": "integer"
                      },
                      "originatorData": {
                        "type": "object"
                      },
                      "beneficiaryData": {
                        "type": "object"
                      },
                      "metadata": {
                        "type": "object"
                      }
                    }
                  },
                  "includeCryptoScreening": {
                    "type": "boolean",
                    "nullable": true,
                    "description": "Per-transaction override for crypto blockchain analytics screening (maps to `include_crypto_screening`)."
                  },
                  "fingerprint_v2": {
                    "type": "object",
                    "description": "The `didit-fp-v2` device fingerprint payload. Injected automatically by the SDKs; only relevant when calling the wire API directly."
                  }
                },
                "required": [
                  "txnId",
                  "type",
                  "info",
                  "subject"
                ]
              },
              "example": {
                "txnId": "wd-2026-07-07-0042",
                "type": "travelRule",
                "info": {
                  "direction": "out",
                  "amount": "0.25",
                  "currency": "ETH",
                  "currencyType": "crypto",
                  "cryptoParams": {
                    "crypto_chain": "ETH"
                  }
                },
                "subject": {
                  "fullName": "Jane Doe"
                },
                "counterparty": {
                  "fullName": "Ana Diaz",
                  "paymentMethod": {
                    "type": "unhosted_wallet",
                    "accountId": "0xBeneficiaryWallet01"
                  }
                },
                "travelRule": {
                  "status": "AWAITING_COUNTERPARTY",
                  "beneficiaryData": {
                    "wallet_address": "0xBeneficiaryWallet01",
                    "name": "Ana Diaz"
                  }
                },
                "includeCryptoScreening": true
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v1/transactions/ \\\n  -H 'X-Transaction-Token: SDK_TOKEN' -H 'Content-Type: application/json' \\\n  -d '{\"txnId\": \"wd-2026-07-07-0042\", \"type\": \"travelRule\", \"info\": {\"direction\": \"out\", \"amount\": \"0.25\", \"currency\": \"ETH\", \"currencyType\": \"crypto\"}, \"subject\": {\"fullName\": \"Jane Doe\"}, \"counterparty\": {\"fullName\": \"Ana Diaz\", \"paymentMethod\": {\"type\": \"unhosted_wallet\", \"accountId\": \"0xBeneficiaryWallet01\"}}, \"travelRule\": {\"status\": \"AWAITING_COUNTERPARTY\", \"beneficiaryData\": {\"wallet_address\": \"0xBeneficiaryWallet01\", \"name\": \"Ana Diaz\"}}, \"includeCryptoScreening\": true}'"
          },
          {
            "lang": "javascript",
            "label": "Web SDK",
            "source": "import { DiditSdk } from '@didit-protocol/sdk-web';\n\nconst result = await DiditSdk.shared.submitTransaction({\n  transactionToken: sdkToken,\n  transaction: {\n    txnId: 'wd-2026-07-07-0042',\n    category: 'travelRule',\n    details: { direction: 'out', amount: '0.25', currency: 'ETH', currencyType: 'crypto' },\n    subject: { fullName: 'Jane Doe' },\n    counterparty: { fullName: 'Ana Diaz', paymentMethod: { type: 'unhosted_wallet', accountId: '0xBeneficiaryWallet01' } },\n    travelRule: { status: 'AWAITING_COUNTERPARTY', beneficiaryData: { wallet_address: '0xBeneficiaryWallet01', name: 'Ana Diaz' } },\n    includeCryptoScreening: true\n  },\n  onActionCompleted: (refreshed) => console.log(refreshed.status)\n});"
          }
        ],
        "responses": {
          "201": {
            "description": "Transaction submitted and monitored. Same detail shape as `GET /v3/transactions/{transaction_id}/`, including the `action_required` block when the end user must complete a follow-up action (the SDKs auto-launch it by default).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDetail"
                },
                "examples": {
                  "Wallet ownership required": {
                    "value": {
                      "uuid": "22222222-3333-4444-5555-666666666666",
                      "txn_id": "wd-2026-07-07-0042",
                      "transaction_type": "travelRule",
                      "direction": "OUTBOUND",
                      "status": "AWAITING_USER",
                      "amount": "0.25",
                      "currency": "ETH",
                      "travel_rule": {
                        "uuid": "33333333-4444-5555-6666-777777777777",
                        "status": "ON_HOLD",
                        "required": true
                      },
                      "action_required": {
                        "type": "wallet_ownership",
                        "url": "https://verification.didit.me/wallet-ownership/3s9x...urlsafe",
                        "widget_session_id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb",
                        "expires_at": "2026-07-07T13:00:00Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failed (missing required fields, duplicate `txnId`, unknown category).",
            "content": {
              "application/json": {
                "examples": {
                  "Missing fields": {
                    "value": {
                      "info": {
                        "currency": [
                          "This field is required."
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired. Expired-token responses always contain the word `expired` in the detail, which is how the SDKs distinguish `expired_token` from `invalid_token` errors.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "Invalid transaction token."
                    }
                  },
                  "Expired token": {
                    "value": {
                      "detail": "Transaction token expired."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{transaction_id}/": {
      "get": {
        "summary": "Get a transaction from an SDK",
        "description": "Device-facing read endpoint, authenticated with the same `X-Transaction-Token` used to submit. The SDKs poll it after an auto-launched action flow completes or its window closes, so the final state never depends on a single notification. Returns the same transaction detail as the submit response, including the current `action_required` block (`null` once the action is resolved).",
        "operationId": "getTransactionFromSdk",
        "tags": [
          "Transactions"
        ],
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "description": "Didit-stable transaction UUID (the `uuid` from the submit response, **not** your `txnId`).",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "22222222-3333-4444-5555-666666666666"
          }
        ],
        "security": [
          {
            "TransactionTokenAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v1/transactions/22222222-3333-4444-5555-666666666666/' \\\n  -H 'X-Transaction-Token: SDK_TOKEN'"
          }
        ],
        "responses": {
          "200": {
            "description": "The full transaction detail, including `action_required` (`null` once the pending action is resolved).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionDetail"
                }
              }
            }
          },
          "401": {
            "description": "The transaction token was rejected: missing, unknown, revoked, over its `max_uses`, or expired.",
            "content": {
              "application/json": {
                "examples": {
                  "Expired token": {
                    "value": {
                      "detail": "Transaction token expired."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No transaction with this UUID exists for the token's application, or the path segment is not a valid UUID.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/businesses/": {
      "get": {
        "summary": "List businesses",
        "description": "List [businesses](/entities/businesses) Didit assembles from KYB sessions sharing the same `vendor_data` (free-form string, NOT a UUID), plus businesses created via the API. Paginated, ordered by `last_session_at` desc. This endpoint does not support filtering or search parameters — fetch pages and filter client-side.",
        "operationId": "list_businesses",
        "tags": [
          "Businesses"
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size. Defaults to 50 when omitted. There is no enforced maximum, but keep pages small for latency."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset of the first record to return. Prefer following the `next` URL from the previous page over computing this by hand."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/businesses/?limit=50' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/businesses/',\n    headers={'x-api-key': 'YOUR_API_KEY'},\n    params={'limit': 50},\n)\nresp.raise_for_status()\npage = resp.json()\nfor biz in page['results']:\n    print(biz['vendor_data'], biz['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const url = new URL('https://verification.didit.me/v3/businesses/');\nurl.searchParams.set('limit', '50');\n\nconst page = await fetch(url, {\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY },\n}).then((r) => r.json());\nconsole.log(page.count, page.results.length);"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of businesses with status, session counts, and feature breakdown.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows — exact up to 100, capped at 100 beyond that for performance. Do not use it to detect the last page; rely on `next` being `null` instead."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or `null` on the last page."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or `null` on the first page."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BusinessListItem"
                      }
                    }
                  }
                },
                "examples": {
                  "Example": {
                    "value": {
                      "count": 1,
                      "next": null,
                      "previous": null,
                      "results": [
                        {
                          "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
                          "vendor_data": "company-123",
                          "display_name": null,
                          "legal_name": "Acme Trading Ltd",
                          "registration_number": "12345678",
                          "country_code": "GB",
                          "effective_name": "Acme Trading Ltd",
                          "status": "ACTIVE",
                          "session_count": 2,
                          "approved_count": 1,
                          "declined_count": 0,
                          "in_review_count": 1,
                          "features": {
                            "KYB": "Approved"
                          },
                          "features_list": [
                            {
                              "feature": "KYB",
                              "status": "Approved"
                            }
                          ],
                          "last_session_at": "2026-03-15T10:30:00Z",
                          "last_activity_at": "2026-03-15T10:30:00Z",
                          "first_session_at": "2026-01-10T08:00:00Z",
                          "tags": [
                            {
                              "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                              "name": "VIP",
                              "color": "#2567FF"
                            }
                          ],
                          "created_at": "2026-01-10T08:00:00Z"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot list businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/customization/": {
      "get": {
        "summary": "Get branding customization",
        "description": "Returns the white-label branding for the application resolved from the API key: colors, fonts, border radii, logo URLs and UI/UX options. Custom domain and white-label email settings are managed only in the Business Console and are not part of this response.",
        "operationId": "get_customization",
        "tags": [
          "Customization"
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/customization/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/customization/',\n    headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbranding = resp.json()\nprint(branding['font_family'], branding['color_primary'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const branding = await fetch(\n  'https://verification.didit.me/v3/customization/',\n  { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(branding.font_family, branding.color_primary);"
          }
        ],
        "responses": {
          "200": {
            "description": "Current branding customization for the application.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandingCustomization"
                },
                "examples": {
                  "Default branding": {
                    "value": {
                      "color_primary": "#000000",
                      "color_secondary": "#9DA1A1",
                      "color_background": "#FFFFFF",
                      "color_panel": "#2567FF",
                      "color_panel_10": "#1A1A1A",
                      "color_on_panel_1": "#9DA1A1",
                      "color_on_panel_2": "#F4F4F6",
                      "color_on_background": "#000000",
                      "color_button_1": "#2567FF",
                      "color_button_2": "#F8F8F8",
                      "color_button_text_1": "#FFFFFF",
                      "color_button_text_2": "#000000",
                      "color_pill_text": "#111827",
                      "border_radius_panel": 10,
                      "border_radius_buttons": 12,
                      "font_family": "Inter",
                      "font_weight": "regular",
                      "font_url": "https://fonts.googleapis.com/css2?family=Inter",
                      "logo_square": null,
                      "logo_rectangular": null,
                      "favicon": null,
                      "app_public_name": "Acme Verify",
                      "privacy_policy_url": "https://acme.com/privacy",
                      "skip_welcome_screen": false,
                      "disable_login_with_didit": false,
                      "hide_progress_bar": false,
                      "callback_seconds": 3
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "patch": {
        "summary": "Update branding customization",
        "description": "Partial update of the application's white-label branding. Send `application/json` to change colors, fonts, radii and UI options, or `multipart/form-data` to upload logo images. Only the fields you include are changed. Custom domain and white-label email cannot be set through this endpoint — use the Business Console for those.",
        "operationId": "update_customization",
        "tags": [
          "Customization"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrandingCustomizationUpdate"
              },
              "examples": {
                "Update colors and font": {
                  "summary": "Change branding colors and font",
                  "value": {
                    "color_primary": "#123456",
                    "color_button_1": "#2567FF",
                    "font_family": "AR One Sans",
                    "border_radius_buttons": 16
                  }
                },
                "Update UI options": {
                  "summary": "Change public name and flow options",
                  "value": {
                    "app_public_name": "Acme Verify",
                    "privacy_policy_url": "https://acme.com/privacy",
                    "skip_welcome_screen": true,
                    "hide_progress_bar": true,
                    "callback_seconds": 5
                  }
                },
                "Remove a logo": {
                  "summary": "Delete the current rectangular logo",
                  "value": {
                    "delete_logo_rectangular": true
                  }
                }
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "description": "Use multipart/form-data only to upload logo images. Branding fields above may also be sent as form fields in the same request.",
                "properties": {
                  "image_square": {
                    "type": "string",
                    "format": "binary",
                    "description": "Square logo (1:1 aspect ratio). Max **1 MB**. Allowed: `png`, `jpg`, `jpeg`, `webp`."
                  },
                  "image_rectangular": {
                    "type": "string",
                    "format": "binary",
                    "description": "Rectangular logo (2:1 aspect ratio). Max **1 MB**. Allowed: `png`, `jpg`, `jpeg`, `webp`."
                  },
                  "image_favicon": {
                    "type": "string",
                    "format": "binary",
                    "description": "Favicon image. Max **500 KB**."
                  }
                }
              },
              "example": {
                "image_rectangular": "(binary PNG logo, 2:1)",
                "image_favicon": "(binary favicon)"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"color_primary\": \"#123456\", \"font_family\": \"AR One Sans\"}'"
          },
          {
            "lang": "curl",
            "label": "curl (logo upload)",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/customization/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'image_rectangular=@logo.png' \\\n  -F 'image_favicon=@favicon.ico'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.patch(\n    'https://verification.didit.me/v3/customization/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'color_primary': '#123456', 'font_family': 'AR One Sans'},\n)\nresp.raise_for_status()\nprint(resp.json()['color_primary'])\n\n# Upload a logo (multipart):\nwith open('logo.png', 'rb') as logo:\n    requests.patch(\n        'https://verification.didit.me/v3/customization/',\n        headers={'x-api-key': 'YOUR_API_KEY'},\n        files={'image_rectangular': logo},\n    )"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/customization/', {\n  method: 'PATCH',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ color_primary: '#123456', font_family: 'AR One Sans' }),\n});\nconst branding = await resp.json();\nconsole.log(branding.color_primary);"
          }
        ],
        "responses": {
          "200": {
            "description": "Branding updated. The FULL branding object is returned (all fields, not just the ones you changed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrandingCustomization"
                },
                "examples": {
                  "Updated": {
                    "value": {
                      "color_primary": "#123456",
                      "color_secondary": "#9DA1A1",
                      "color_background": "#FFFFFF",
                      "color_panel": "#2567FF",
                      "color_panel_10": "#1A1A1A",
                      "color_on_panel_1": "#9DA1A1",
                      "color_on_panel_2": "#F4F4F6",
                      "color_on_background": "#000000",
                      "color_button_1": "#2567FF",
                      "color_button_2": "#F8F8F8",
                      "color_button_text_1": "#FFFFFF",
                      "color_button_text_2": "#000000",
                      "color_pill_text": "#111827",
                      "border_radius_panel": 10,
                      "border_radius_buttons": 16,
                      "font_family": "AR One Sans",
                      "font_weight": "400",
                      "font_url": "https://fonts.googleapis.com/css2?family=AR+One+Sans",
                      "logo_square": null,
                      "logo_rectangular": "https://cdn.didit.me/white_label/logo_rectangular/...",
                      "favicon": null,
                      "app_public_name": "Acme Verify",
                      "privacy_policy_url": "https://acme.com/privacy",
                      "skip_welcome_screen": true,
                      "disable_login_with_didit": false,
                      "hide_progress_bar": false,
                      "callback_seconds": 3
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body — e.g. malformed hex color, unknown font family or weight, a logo failing the size/aspect-ratio check, or a Business-Console-only field (`domain`, `from_email`, `verify_domain`, `force_remove_domain`, `verify_email_domain`, `force_remove_email_domain`, and their read-only companions). Also returned when setting `disable_login_with_didit` to `false` while a custom domain is configured.",
            "content": {
              "application/json": {
                "examples": {
                  "Bad color": {
                    "value": {
                      "color_primary": [
                        "Invalid color format. Use '#RRGGBB' or '#RRGGBBAA' format."
                      ]
                    }
                  },
                  "Bad logo": {
                    "value": {
                      "image_rectangular": [
                        "Image aspect ratio must be 2:1. Got 1024x1024 image dimensions"
                      ]
                    }
                  },
                  "Console-only field": {
                    "value": {
                      "domain": [
                        "Manage this field in the Business Console."
                      ]
                    }
                  },
                  "Bad font weight": {
                    "value": {
                      "font_weight": [
                        "Font weight 950 is not available for Inter. Available weights are: 100, 200, 300, 400, 500, 600, 700, 800, 900"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "API key missing, malformed, expired, or scoped to a different application. Didit returns 403 (never 401) for all authentication failures on this endpoint.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid token": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Retry with exponential backoff."
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/businesses/{vendor_data}/": {
      "get": {
        "summary": "Get business",
        "description": "Fetch one [business](/entities/businesses) by `vendor_data` (exact match). Adds `metadata` and `updated_at` to the list view. Soft-deleted businesses return 404.",
        "operationId": "get_business",
        "tags": [
          "Businesses"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the business — a free-form string (NOT a UUID), matched exactly as sent.",
            "example": "company-123"
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/businesses/company-123/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/businesses/company-123/',\n    headers={'x-api-key': 'YOUR_API_KEY'},\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'], biz['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const vendorData = encodeURIComponent('company-123');\nconst biz = await fetch(\n  `https://verification.didit.me/v3/businesses/${vendorData}/`,\n  { headers: { 'x-api-key': process.env.DIDIT_API_KEY } },\n).then((r) => r.json());\nconsole.log(biz.didit_internal_id, biz.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Full business detail including metadata and aggregated session data.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BusinessDetailItem"
                },
                "examples": {
                  "Active business": {
                    "value": {
                      "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
                      "vendor_data": "company-123",
                      "display_name": null,
                      "legal_name": "Acme Trading Ltd",
                      "registration_number": "12345678",
                      "country_code": "GB",
                      "effective_name": "Acme Trading Ltd",
                      "status": "ACTIVE",
                      "session_count": 2,
                      "approved_count": 1,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "features": {
                        "KYB": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "KYB",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2026-03-15T10:30:00Z",
                      "last_activity_at": "2026-03-15T10:30:00Z",
                      "first_session_at": "2026-01-10T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "name": "VIP",
                          "color": "#2567FF"
                        }
                      ],
                      "created_at": "2026-01-10T08:00:00Z",
                      "metadata": {
                        "industry": "fintech"
                      },
                      "updated_at": "2026-03-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot read this application's businesses. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      },
      "patch": {
        "summary": "Update business",
        "description": "Partial update of a business: `display_name`, `legal_name`, `registration_number`, `country_code`, `status`, `metadata`. `metadata` fully replaces. Setting `status` to `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry.",
        "operationId": "update_business",
        "tags": [
          "Businesses"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the business (free-form string, NOT a UUID).",
            "example": "company-123"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Partial-update body. Provide only the fields you want to change.",
                "properties": {
                  "display_name": {
                    "type": "string",
                    "nullable": true,
                    "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)."
                  },
                  "legal_name": {
                    "type": "string",
                    "nullable": true,
                    "description": "Official legal name from registry or manual entry."
                  },
                  "registration_number": {
                    "type": "string",
                    "nullable": true,
                    "description": "Company registration or incorporation number."
                  },
                  "country_code": {
                    "type": "string",
                    "nullable": true,
                    "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "description": "Business lifecycle status. Setting `BLOCKED` automatically adds the `vendor_data` to the system blocklist; flipping away from `BLOCKED` removes that blocklist entry."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Arbitrary JSON object you attach to the business. **Fully replaces** the existing metadata on update — merge client-side if you only want to add keys."
                  }
                }
              },
              "examples": {
                "Update name": {
                  "summary": "Update display name",
                  "value": {
                    "display_name": "Acme Corp"
                  }
                },
                "Flag for review": {
                  "summary": "Flip status",
                  "value": {
                    "status": "FLAGGED"
                  }
                },
                "Add metadata": {
                  "summary": "Replace metadata JSON",
                  "value": {
                    "metadata": {
                      "industry": "fintech",
                      "risk_level": "low"
                    }
                  }
                },
                "Fix registry data": {
                  "summary": "Correct extracted fields",
                  "value": {
                    "legal_name": "Acme Trading International Ltd",
                    "registration_number": "12345678",
                    "country_code": "GB"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"display_name\": \"Acme Corp\", \"status\": \"FLAGGED\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.patch(\n    'https://verification.didit.me/v3/businesses/company-123/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'display_name': 'Acme Corp', 'status': 'FLAGGED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/', {\n  method: 'PATCH',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ display_name: 'Acme Corp', status: 'FLAGGED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Business updated. The full updated business record is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BusinessDetailItem"
                },
                "examples": {
                  "Updated": {
                    "value": {
                      "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
                      "vendor_data": "company-123",
                      "display_name": "Acme Corp",
                      "legal_name": "Acme Trading Ltd",
                      "registration_number": "12345678",
                      "country_code": "GB",
                      "effective_name": "Acme Corp",
                      "status": "FLAGGED",
                      "session_count": 2,
                      "approved_count": 1,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "features": {
                        "KYB": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "KYB",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2026-03-15T10:30:00Z",
                      "last_activity_at": "2026-03-15T10:30:00Z",
                      "first_session_at": "2026-01-10T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "name": "VIP",
                          "color": "#2567FF"
                        }
                      ],
                      "created_at": "2026-01-10T08:00:00Z",
                      "metadata": {
                        "industry": "fintech",
                        "risk_level": "low"
                      },
                      "updated_at": "2026-03-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body — typically an unknown `status` or malformed `metadata`.",
            "content": {
              "application/json": {
                "examples": {
                  "Bad status": {
                    "value": {
                      "status": [
                        "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED"
                      ]
                    }
                  },
                  "Bad metadata": {
                    "value": {
                      "metadata": [
                        "Metadata must be a JSON object."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/businesses/{vendor_data}/update-status/": {
      "patch": {
        "summary": "Update business status",
        "description": "Flip only the lifecycle `status` of a business — VendorBusinessStatusChoices (`ACTIVE`/`FLAGGED`/`BLOCKED`, NOT session statuses). `BLOCKED` adds `vendor_data` to the system blocklist.",
        "operationId": "update_business_status",
        "tags": [
          "Businesses"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the business (free-form string, NOT a UUID).",
            "example": "company-123"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "description": "New lifecycle status. `BLOCKED` also adds the `vendor_data` to the system blocklist."
                  }
                }
              },
              "examples": {
                "Flag": {
                  "summary": "Flag the business for review",
                  "value": {
                    "status": "FLAGGED"
                  }
                },
                "Block": {
                  "summary": "Block the business (adds to system blocklist)",
                  "value": {
                    "status": "BLOCKED"
                  }
                },
                "Reactivate": {
                  "summary": "Reactivate the business",
                  "value": {
                    "status": "ACTIVE"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/businesses/company-123/update-status/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"status\": \"BLOCKED\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.patch(\n    'https://verification.didit.me/v3/businesses/company-123/update-status/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/company-123/update-status/', {\n  method: 'PATCH',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst biz = await resp.json();\nconsole.log(biz.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "Business status updated. Full business record returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BusinessDetailItem"
                },
                "examples": {
                  "Blocked": {
                    "value": {
                      "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
                      "vendor_data": "company-123",
                      "display_name": null,
                      "legal_name": "Acme Trading Ltd",
                      "registration_number": "12345678",
                      "country_code": "GB",
                      "effective_name": "Acme Trading Ltd",
                      "status": "BLOCKED",
                      "session_count": 2,
                      "approved_count": 1,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "features": {
                        "KYB": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "KYB",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2026-03-15T10:30:00Z",
                      "last_activity_at": "2026-03-15T10:30:00Z",
                      "first_session_at": "2026-01-10T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "name": "VIP",
                          "color": "#2567FF"
                        }
                      ],
                      "created_at": "2026-01-10T08:00:00Z",
                      "metadata": {
                        "industry": "fintech"
                      },
                      "updated_at": "2026-03-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid `status`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing status": {
                    "value": {
                      "status": [
                        "This field is required."
                      ]
                    }
                  },
                  "Bad status": {
                    "value": {
                      "status": [
                        "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot update businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No (non-deleted) business with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/businesses/delete/": {
      "post": {
        "summary": "Batch delete businesses",
        "description": "**Hard delete** businesses via `qs.delete()` — permanent. For blocking prefer [Update Business Status](#patch-/v3/businesses/-vendor_data-/update-status/) with `BLOCKED`.",
        "operationId": "batch_delete_businesses",
        "tags": [
          "Businesses"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Provide `vendor_data_list`, `didit_internal_id_list`, or `delete_all: true`. Precedence when several are sent: `delete_all` > `vendor_data_list` > `didit_internal_id_list` (only the highest-precedence selector is used).",
                "properties": {
                  "vendor_data_list": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Your own identifiers to delete, matched exactly as sent."
                  },
                  "didit_internal_id_list": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "description": "Didit's internal UUIDs (`didit_internal_id`) to delete."
                  },
                  "delete_all": {
                    "type": "boolean",
                    "default": false,
                    "description": "If `true`, deletes every business in the application. Cannot be combined with the list selectors."
                  }
                }
              },
              "examples": {
                "Specific businesses": {
                  "summary": "Delete by vendor_data",
                  "value": {
                    "vendor_data_list": [
                      "company-123",
                      "company-456"
                    ]
                  }
                },
                "Specific internal IDs": {
                  "summary": "Delete by didit_internal_id",
                  "value": {
                    "didit_internal_id_list": [
                      "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                      "b2c3d4e5-f6a7-8901-bcde-f12345678901"
                    ]
                  }
                },
                "All businesses": {
                  "summary": "Wipe every business in this application",
                  "value": {
                    "delete_all": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/businesses/delete/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"vendor_data_list\": [\"company-123\", \"company-456\"]}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/businesses/delete/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'vendor_data_list': ['company-123', 'company-456']},\n)\nresp.raise_for_status()\nprint('deleted', resp.json()['deleted'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/delete/', {\n  method: 'POST',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ vendor_data_list: ['company-123', 'company-456'] }),\n});\nconst body = await resp.json();\nconsole.log('deleted', body.deleted);"
          }
        ],
        "responses": {
          "200": {
            "description": "Businesses deleted. Returns the number of rows actually removed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "integer",
                      "description": "Number of businesses deleted (excludes vendor_data values that didn't match anything)."
                    }
                  }
                },
                "examples": {
                  "Deleted some": {
                    "value": {
                      "deleted": 2
                    }
                  },
                  "Nothing matched": {
                    "value": {
                      "deleted": 0
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No selector supplied (all of `vendor_data_list`, `didit_internal_id_list`, and `delete_all` missing or empty). The error body is a JSON array.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing selector": {
                    "value": [
                      "Provide vendor_data_list, didit_internal_id_list, or set delete_all=true."
                    ]
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot delete businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/users/{vendor_data}/update-status/": {
      "patch": {
        "summary": "Update user status",
        "description": "Flip only the lifecycle `status` of a user — `ACTIVE`/`FLAGGED`/`BLOCKED` (NOT session statuses). `BLOCKED` adds the `vendor_data` to the system blocklist; moving away from `BLOCKED` removes that entry. Each change is recorded as a `STATUS_CHANGED` activity on the user.",
        "operationId": "update_user_status",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "name": "vendor_data",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Your unique identifier for the user."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "status"
                ],
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "description": "New lifecycle status. `BLOCKED` also adds the `vendor_data` to the system blocklist."
                  }
                }
              },
              "examples": {
                "Activate": {
                  "summary": "Reactivate the user",
                  "value": {
                    "status": "ACTIVE"
                  }
                },
                "Flag": {
                  "summary": "Flag the user for review",
                  "value": {
                    "status": "FLAGGED"
                  }
                },
                "Block": {
                  "summary": "Block the user (adds to system blocklist)",
                  "value": {
                    "status": "BLOCKED"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/users/user-abc-123/update-status/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"status\": \"BLOCKED\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.patch(\n    'https://verification.didit.me/v3/users/user-abc-123/update-status/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'status': 'BLOCKED'},\n)\nresp.raise_for_status()\nprint(resp.json()['status'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/users/user-abc-123/update-status/', {\n  method: 'PATCH',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ status: 'BLOCKED' }),\n});\nconst user = await resp.json();\nconsole.log(user.status);"
          }
        ],
        "responses": {
          "200": {
            "description": "User status updated. Full user record returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetailItem"
                },
                "examples": {
                  "Blocked": {
                    "value": {
                      "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418",
                      "vendor_data": "user-abc-123",
                      "display_name": null,
                      "full_name": "John Michael Doe",
                      "date_of_birth": "1990-05-15",
                      "effective_name": "John Michael Doe",
                      "status": "BLOCKED",
                      "metadata": {
                        "tier": "premium",
                        "source": "website"
                      },
                      "portrait_image_url": "https://<media-host>/...",
                      "session_count": 3,
                      "approved_count": 2,
                      "declined_count": 0,
                      "in_review_count": 1,
                      "issuing_states": [
                        "USA"
                      ],
                      "approved_emails": [
                        "john@example.com"
                      ],
                      "approved_phones": [
                        "+14155551234"
                      ],
                      "features": {
                        "ID_VERIFICATION": "Approved",
                        "LIVENESS": "Approved",
                        "FACE_MATCH": "Approved",
                        "AML": "Approved"
                      },
                      "features_list": [
                        {
                          "feature": "ID_VERIFICATION",
                          "status": "Approved"
                        },
                        {
                          "feature": "LIVENESS",
                          "status": "Approved"
                        },
                        {
                          "feature": "FACE_MATCH",
                          "status": "Approved"
                        },
                        {
                          "feature": "AML",
                          "status": "Approved"
                        }
                      ],
                      "last_session_at": "2025-06-15T10:30:00Z",
                      "last_activity_at": "2025-06-15T10:30:00Z",
                      "first_session_at": "2025-06-01T08:00:00Z",
                      "tags": [
                        {
                          "uuid": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
                          "tag": {
                            "uuid": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                            "name": "VIP",
                            "color": "#2567FF",
                            "description": null,
                            "source": "custom",
                            "created_at": "2025-05-01T09:00:00Z",
                            "updated_at": "2025-05-01T09:00:00Z"
                          },
                          "added_by_email": "analyst@acme.com",
                          "added_by_name": "Jane Analyst",
                          "created_at": "2025-06-02T11:00:00Z"
                        }
                      ],
                      "comments": [
                        {
                          "uuid": "c1111111-2222-4333-8444-555555555555",
                          "comment_type": "STATUS_CHANGED",
                          "comment": null,
                          "actor_email": null,
                          "actor_name": null,
                          "previous_status": "ACTIVE",
                          "new_status": "BLOCKED",
                          "previous_value": null,
                          "new_value": null,
                          "changed_fields": [],
                          "metadata": null,
                          "mentioned_emails": [],
                          "created_at": "2025-06-15T10:30:00Z"
                        }
                      ],
                      "created_at": "2025-06-01T08:00:00Z",
                      "updated_at": "2025-06-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid `status`.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing status": {
                    "value": {
                      "status": [
                        "This field is required."
                      ]
                    }
                  },
                  "Bad status": {
                    "value": {
                      "status": [
                        "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot update users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No (non-deleted) user with the supplied `vendor_data` exists for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/businesses/create/": {
      "post": {
        "summary": "Create business",
        "description": "Pre-create a [business](/entities/businesses) *without* a KYB session. All fields are optional, but you should always send `vendor_data` — without it the record cannot be linked to sessions or transactions. When provided, `vendor_data` must be unique among non-deleted businesses (exact match; conflicts return 400). Not idempotent.",
        "operationId": "create_business",
        "tags": [
          "Businesses"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "vendor_data": {
                    "type": "string",
                    "nullable": true,
                    "description": "Your unique identifier for this business (free-form string, NOT a UUID). Optional but strongly recommended. When provided, must be unique among non-deleted businesses for the application; matched exactly as sent."
                  },
                  "display_name": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 255,
                    "description": "Friendly display name shown in the console (takes precedence over `legal_name` for UI display)."
                  },
                  "legal_name": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 255,
                    "description": "Official legal name of the company."
                  },
                  "registration_number": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 100,
                    "description": "Company registration or incorporation number."
                  },
                  "country_code": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 10,
                    "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "default": "ACTIVE",
                    "description": "Initial lifecycle status. Defaults to `ACTIVE`."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Arbitrary JSON object you attach to the business. Defaults to `{}`."
                  }
                }
              },
              "examples": {
                "Basic": {
                  "summary": "Recommended minimum",
                  "value": {
                    "vendor_data": "company-456",
                    "legal_name": "New Corp Ltd",
                    "country_code": "US"
                  }
                },
                "Full": {
                  "summary": "All fields",
                  "value": {
                    "vendor_data": "company-789",
                    "display_name": "New Corp",
                    "legal_name": "New Corp International Ltd",
                    "registration_number": "98765432",
                    "country_code": "DE",
                    "status": "ACTIVE",
                    "metadata": {
                      "industry": "fintech",
                      "tier": "enterprise"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/businesses/create/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"vendor_data\": \"company-456\", \"legal_name\": \"New Corp Ltd\", \"country_code\": \"US\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/businesses/create/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={\n        'vendor_data': 'company-456',\n        'legal_name': 'New Corp Ltd',\n        'country_code': 'US',\n    },\n)\nresp.raise_for_status()\nbiz = resp.json()\nprint(biz['didit_internal_id'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/businesses/create/', {\n  method: 'POST',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    vendor_data: 'company-456',\n    legal_name: 'New Corp Ltd',\n    country_code: 'US',\n  }),\n});\nconst biz = await resp.json();\nconsole.log(biz.didit_internal_id);"
          }
        ],
        "responses": {
          "201": {
            "description": "Business created. Full business record returned (same shape as Get Business).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BusinessDetailItem"
                },
                "examples": {
                  "Created": {
                    "value": {
                      "didit_internal_id": "a1b2c3d4-e5f6-4890-abcd-ef1234567890",
                      "vendor_data": "company-456",
                      "display_name": null,
                      "legal_name": "New Corp Ltd",
                      "registration_number": null,
                      "country_code": "US",
                      "effective_name": "New Corp Ltd",
                      "status": "ACTIVE",
                      "session_count": 0,
                      "approved_count": 0,
                      "declined_count": 0,
                      "in_review_count": 0,
                      "features": {},
                      "features_list": [],
                      "last_session_at": null,
                      "last_activity_at": "2026-03-15T10:30:00Z",
                      "first_session_at": null,
                      "tags": [],
                      "created_at": "2026-03-15T10:30:00Z",
                      "metadata": {},
                      "updated_at": "2026-03-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body — typically a duplicate `vendor_data`, an unknown `status`, or malformed `metadata`.",
            "content": {
              "application/json": {
                "examples": {
                  "Duplicate": {
                    "value": {
                      "vendor_data": [
                        "A business with this vendor_data already exists."
                      ]
                    }
                  },
                  "Bad status": {
                    "value": {
                      "status": [
                        "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED"
                      ]
                    }
                  },
                  "Bad metadata": {
                    "value": {
                      "metadata": [
                        "Metadata must be a JSON object."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing, invalid, or revoked API key, or the key cannot create businesses for this application. This endpoint returns `403` (never `401`) for authentication failures, including requests with no credentials at all.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/users/create/": {
      "post": {
        "summary": "Create user",
        "description": "Pre-create a [user](/entities/users) by `vendor_data` *without* a verification session. `vendor_data` must be unique among non-deleted users for the application (exact match; conflicts return 400). Not idempotent.",
        "operationId": "create_user",
        "tags": [
          "Users"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "vendor_data"
                ],
                "properties": {
                  "vendor_data": {
                    "type": "string",
                    "description": "Your unique identifier for this user (free-form string, NOT a UUID). Must be unique among non-deleted users for the application; matched exactly as sent."
                  },
                  "full_name": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 512,
                    "description": "Full legal name of the user."
                  },
                  "display_name": {
                    "type": "string",
                    "nullable": true,
                    "description": "Friendly display name shown in the console (takes precedence over `full_name` for UI display).",
                    "maxLength": 255
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Date of birth in `YYYY-MM-DD` format."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "ACTIVE",
                      "FLAGGED",
                      "BLOCKED"
                    ],
                    "default": "ACTIVE",
                    "description": "Initial lifecycle status. Defaults to `ACTIVE`."
                  },
                  "metadata": {
                    "type": "object",
                    "nullable": true,
                    "description": "Arbitrary JSON object you attach to the user. Defaults to `{}`."
                  },
                  "approved_emails": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Pre-trusted email addresses for this user, e.g. `[\"john@example.com\"]`."
                  },
                  "approved_phones": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Pre-trusted phone numbers for this user, e.g. `[\"+14155551234\"]`."
                  },
                  "issuing_states": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Pre-recorded issuing countries (ISO 3166-1 alpha-3), e.g. `[\"USA\"]`."
                  }
                }
              },
              "examples": {
                "Basic": {
                  "summary": "Create with basic info",
                  "value": {
                    "vendor_data": "user-abc-123",
                    "full_name": "John Doe"
                  }
                },
                "Full": {
                  "summary": "All fields",
                  "value": {
                    "vendor_data": "user-abc-456",
                    "full_name": "Jane Smith",
                    "display_name": "Jane S.",
                    "date_of_birth": "1990-05-15",
                    "status": "ACTIVE",
                    "metadata": {
                      "tier": "premium",
                      "source": "api"
                    },
                    "approved_emails": [
                      "jane@example.com"
                    ]
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/users/create/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"vendor_data\": \"user-abc-123\", \"full_name\": \"John Doe\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/users/create/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={'vendor_data': 'user-abc-123', 'full_name': 'John Doe'},\n)\nresp.raise_for_status()\nuser = resp.json()\nprint(user['didit_internal_id'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/users/create/', {\n  method: 'POST',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ vendor_data: 'user-abc-123', full_name: 'John Doe' }),\n});\nconst user = await resp.json();\nconsole.log(user.didit_internal_id);"
          }
        ],
        "responses": {
          "201": {
            "description": "User created. Full user record returned (same shape as Get User).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserDetailItem"
                },
                "examples": {
                  "Created": {
                    "value": {
                      "didit_internal_id": "f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418",
                      "vendor_data": "user-abc-123",
                      "display_name": null,
                      "full_name": "John Doe",
                      "date_of_birth": null,
                      "effective_name": "John Doe",
                      "status": "ACTIVE",
                      "metadata": {},
                      "portrait_image_url": null,
                      "session_count": 0,
                      "approved_count": 0,
                      "declined_count": 0,
                      "in_review_count": 0,
                      "issuing_states": [],
                      "approved_emails": [],
                      "approved_phones": [],
                      "features": {},
                      "features_list": [],
                      "last_session_at": null,
                      "last_activity_at": "2025-06-15T10:30:00Z",
                      "first_session_at": null,
                      "tags": [],
                      "comments": [],
                      "created_at": "2025-06-15T10:30:00Z",
                      "updated_at": "2025-06-15T10:30:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid body — typically a duplicate `vendor_data`, a missing required field, or a malformed value.",
            "content": {
              "application/json": {
                "examples": {
                  "Duplicate": {
                    "value": {
                      "vendor_data": [
                        "A user with this vendor_data already exists."
                      ]
                    }
                  },
                  "Missing vendor_data": {
                    "value": {
                      "vendor_data": [
                        "This field is required."
                      ]
                    }
                  },
                  "Bad metadata": {
                    "value": {
                      "metadata": [
                        "Metadata must be a JSON object."
                      ]
                    }
                  },
                  "Bad status": {
                    "value": {
                      "status": [
                        "Invalid status. Valid options are: ACTIVE, FLAGGED, BLOCKED"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credentials supplied. Requests to `/v3/users/*` paths without an `x-api-key` header (or `Authorization: Bearer` token) are rejected by the authentication middleware before reaching the API.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing credentials": {
                    "value": {
                      "detail": "You must be authenticated with a valid access token to access this endpoint."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Invalid or revoked API key, or the key cannot create users for this application. Note: on this endpoint an *invalid* key returns `403` (not `401`).",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry after the interval indicated in `Retry-After`.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/vendor-users/by-id/{didit_internal_id}/faces/upload/": {
      "post": {
        "summary": "Upload user face",
        "description": "Attach a trusted imported face image to an existing User profile. Use this after creating or retrieving a User by `vendor_data` and reading its `didit_internal_id`. The uploaded face is stored on the User profile and indexed for duplicate detection and face search. It is not added to a face blocklist.",
        "operationId": "upload_user_face",
        "tags": [
          "Users"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID that owns the application."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID that owns the User profile."
          },
          {
            "name": "didit_internal_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Didit's internal User UUID, returned as `didit_internal_id` by `GET /v3/users/{vendor_data}/` and `POST /v3/users/create/`."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "image"
                ],
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "byte",
                    "description": "Raw base64-encoded JPG or PNG face image. Do not include a `data:image/...;base64,` prefix. Max decoded size: 2 MB."
                  },
                  "comment": {
                    "type": "string",
                    "description": "Optional operator-visible note for the uploaded face.",
                    "default": ""
                  }
                }
              },
              "examples": {
                "Imported face": {
                  "summary": "Attach a face imported from a previous provider",
                  "value": {
                    "image": "/9j/4AAQSkZJRgABAQEA...",
                    "comment": "Imported from previous KYC provider"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"image\": \"/9j/4AAQSkZJRgABAQEA...\", \"comment\": \"Imported from previous KYC provider\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/',\n    headers={'x-api-key': 'YOUR_API_KEY', 'Content-Type': 'application/json'},\n    json={\n        'image': '/9j/4AAQSkZJRgABAQEA...',\n        'comment': 'Imported from previous KYC provider',\n    },\n)\nresp.raise_for_status()\nface = resp.json()\nprint(face['uuid'], face['image_url'])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const resp = await fetch('https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/vendor-users/by-id/f4e5e1f2-94a9-4f86-8c16-2b7d9b4db418/faces/upload/', {\n  method: 'POST',\n  headers: { 'x-api-key': process.env.DIDIT_API_KEY, 'Content-Type': 'application/json' },\n  body: JSON.stringify({\n    image: '/9j/4AAQSkZJRgABAQEA...',\n    comment: 'Imported from previous KYC provider',\n  }),\n});\nconst face = await resp.json();\nconsole.log(face.uuid, face.image_url);"
          }
        ],
        "responses": {
          "201": {
            "description": "Face uploaded and attached to the User profile.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "uuid",
                    "image_url",
                    "comment",
                    "uploaded_by",
                    "created_at"
                  ],
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid",
                      "description": "UUID of the uploaded User face record."
                    },
                    "image_url": {
                      "type": "string",
                      "format": "uri",
                      "nullable": true,
                      "description": "Signed URL for the uploaded face image."
                    },
                    "comment": {
                      "type": "string",
                      "nullable": true,
                      "description": "Comment supplied when the face was uploaded."
                    },
                    "uploaded_by": {
                      "type": "string",
                      "nullable": true,
                      "description": "Email or actor identifier of the uploader when available."
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "Upload timestamp."
                    }
                  }
                },
                "examples": {
                  "Uploaded": {
                    "value": {
                      "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
                      "image_url": "https://signed-url.example/face.jpg",
                      "comment": "Imported from previous KYC provider",
                      "uploaded_by": "api",
                      "created_at": "2026-05-18T12:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request body or image. Common causes: invalid base64, invalid image, no detectable face, image over 2 MB, missing embedding, or the User already has 5 uploaded faces.",
            "content": {
              "application/json": {
                "examples": {
                  "No face": {
                    "value": {
                      "image": "No face detected in the image. Please upload a clear photo with a visible face."
                    }
                  },
                  "Too many faces": {
                    "value": {
                      "detail": "Maximum of 5 faces per user reached."
                    }
                  },
                  "Too large": {
                    "value": {
                      "image": "Image exceeds maximum size of 2 MB."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "examples": {
                  "Missing key": {
                    "value": {
                      "detail": "Authentication credentials were not provided."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "The credential is valid but cannot write User profiles for this application.",
            "content": {
              "application/json": {
                "examples": {
                  "Forbidden": {
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No User exists for that `didit_internal_id` in the application.",
            "content": {
              "application/json": {
                "examples": {
                  "Not found": {
                    "value": {
                      "detail": "Not found."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded; back off and retry.",
            "content": {
              "application/json": {
                "examples": {
                  "Rate limited": {
                    "value": {
                      "detail": "Request was throttled. Expected available in 30 seconds."
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKeyAuth": []
          }
        ]
      }
    },
    "/v3/session/{sessionId}/update-data/": {
      "patch": {
        "summary": "Update KYC data extracted from the ID document on a verification session",
        "description": "Correct the identity fields that were extracted (OCR / NFC) from the user's ID document — misread names, dates, document numbers, and so on — after the verification has finished. Typical callers are a compliance analyst working from your own back office, or an automated reconciliation job. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry an ID-verification (KYC) record: sessions whose workflow never ran an ID-verification step return `404` (`\"The session does not have a KYC.\"`). For graph workflows with multiple ID-verification steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent KYC record is patched.\n\nTwo derived fields are recomputed on every call: `full_name` (from `first_name` + `last_name`) and `issuing_state_name` (from `issuing_state`). If the free-text `address` changes, the server may also re-geocode it and refresh `parsed_address` (skipped for sandbox sessions). `extra_fields` and `parsed_address` are merged key-by-key rather than replaced — see the per-field descriptions for the `null`-to-delete semantics.\n\nSubmitting at least one valid field fires the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) — even when the submitted values equal the stored ones. When values actually change, a `KYC_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome. An empty JSON body (`{}`) is accepted and simply returns the current KYC data without firing the webhook.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.",
        "operationId": "patch_v3_session_update_data",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "description": "UUID of the User Verification (KYC) session whose extracted document data you are correcting. Business (KYB) session IDs are not valid here — for company data use [Update KYB Company Data](#patch-/v3/session/-sessionId-/kyb/-companyUuid-/update-data/).",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "name": "node_id",
            "in": "query",
            "required": false,
            "description": "Workflow graph node whose KYC record to patch, for graph workflows with multiple ID-verification steps (also accepted as a `node_id` body field). When omitted, the most recent KYC record on the session is patched. If no KYC exists for the given node, the call returns `404` (`\"The session does not have a KYC for node '<node_id>'.\"`).",
            "schema": {
              "type": "string",
              "example": "feature_ocr"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "All fields optional — send only what you want to change.",
                "properties": {
                  "document_type": {
                    "type": "string",
                    "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.",
                    "enum": [
                      "P",
                      "DL",
                      "ID",
                      "RP",
                      "SSC",
                      "HIC",
                      "WP",
                      "TC",
                      "VISA",
                      "PSC",
                      "BC",
                      "OTHER"
                    ],
                    "nullable": true,
                    "example": "P"
                  },
                  "document_subtype": {
                    "type": "string",
                    "maxLength": 128,
                    "nullable": true,
                    "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants."
                  },
                  "document_number": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Document number as printed on the ID.",
                    "example": "PAB123456"
                  },
                  "personal_number": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).",
                    "example": "99999999R"
                  },
                  "date_of_birth": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.",
                    "example": "1980-01-01"
                  },
                  "date_of_issue": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Issue date of the document in `YYYY-MM-DD`."
                  },
                  "expiration_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Expiration date of the document in `YYYY-MM-DD`."
                  },
                  "issuing_state": {
                    "type": "string",
                    "nullable": true,
                    "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.",
                    "example": "ESP"
                  },
                  "first_name": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.",
                    "example": "Carmen"
                  },
                  "last_name": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Family name(s). Also feeds the recomputed `full_name`.",
                    "example": "Espanola Espanola"
                  },
                  "gender": {
                    "type": "string",
                    "enum": [
                      "M",
                      "F",
                      "U"
                    ],
                    "nullable": true,
                    "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`."
                  },
                  "address": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it synchronously before responding — the 200 body already reflects the refreshed `parsed_address`. The re-geocode also requires `issuing_state` to be present on the KYC record."
                  },
                  "place_of_birth": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Place of birth as printed on the document."
                  },
                  "nationality": {
                    "type": "string",
                    "maxLength": 32,
                    "nullable": true,
                    "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one."
                  },
                  "marital_status": {
                    "type": "string",
                    "enum": [
                      "SINGLE",
                      "MARRIED",
                      "DIVORCED",
                      "WIDOWED",
                      "UNKNOWN"
                    ],
                    "description": "Marital status where the document carries it. Any other value returns `400`."
                  },
                  "extra_fields": {
                    "type": "object",
                    "additionalProperties": true,
                    "nullable": true,
                    "description": "Per-key overrides for document-specific extra fields (e.g. `blood_group`, `dl_categories`, `mrz_string`). Keys are merged into the existing override map: send a key with a value to set it, or with `null` / `\"\"` to remove that key from the merged view. Send the whole field as `null` to clear all previously applied overrides. Must be a JSON object, otherwise `400`. The response always returns the merged view (OCR-extracted values with your overrides applied)."
                  },
                  "parsed_address": {
                    "type": "object",
                    "nullable": true,
                    "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` and is returned in the response — you cannot set it directly.",
                    "properties": {
                      "address_type": {
                        "type": "string",
                        "nullable": true,
                        "description": "Free-form address classification (e.g. `home`)."
                      },
                      "city": {
                        "type": "string",
                        "nullable": true,
                        "example": "Madrid"
                      },
                      "region": {
                        "type": "string",
                        "nullable": true,
                        "description": "State / province / autonomous community."
                      },
                      "street_1": {
                        "type": "string",
                        "nullable": true,
                        "example": "Avda de Madrid 34"
                      },
                      "street_2": {
                        "type": "string",
                        "nullable": true
                      },
                      "postal_code": {
                        "type": "string",
                        "nullable": true,
                        "example": "28013"
                      },
                      "country": {
                        "type": "string",
                        "nullable": true,
                        "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.",
                        "example": "ES"
                      }
                    }
                  }
                }
              },
              "examples": {
                "Correct name and DOB": {
                  "summary": "Fix OCR-misread name, date of birth, and structured address",
                  "value": {
                    "first_name": "Carmen",
                    "last_name": "Espanola Espanola",
                    "date_of_birth": "1980-01-01",
                    "issuing_state": "ESP",
                    "marital_status": "MARRIED",
                    "extra_fields": {
                      "blood_group": "O+"
                    },
                    "parsed_address": {
                      "street_1": "Avda de Madrid 34",
                      "city": "Madrid",
                      "postal_code": "28013",
                      "country": "ES"
                    }
                  }
                },
                "Remove an extra field": {
                  "summary": "Delete one extra-field key, keep the rest",
                  "value": {
                    "extra_fields": {
                      "blood_group": null
                    }
                  }
                },
                "Long-form document type": {
                  "summary": "Long-form alias is normalized to the short code (here P)",
                  "value": {
                    "document_type": "Passport"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "KYC data updated. Returns the **full current KYC record** (every updatable field with its stored value, plus the merged `extra_fields` and the current `parsed_address`) — not just the fields you submitted. The `data.updated` webhook fires when at least one valid field was in the payload.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "document_type": {
                      "type": "string",
                      "description": "Document type code. Accepts the short codes listed in the enum, plus six long-form aliases normalized server-side before validation: `Passport` → `P`, `Identity Card` → `ID`, `Driver's License` → `DL`, `Residence Permit` → `RP`, `Health Insurance Card` → `HIC`, `Tax Card` → `TC`. Any other value (including `PASSPORT` in caps) returns `400`.",
                      "enum": [
                        "P",
                        "DL",
                        "ID",
                        "RP",
                        "SSC",
                        "HIC",
                        "WP",
                        "TC",
                        "VISA",
                        "PSC",
                        "BC",
                        "OTHER"
                      ],
                      "nullable": true,
                      "example": "P"
                    },
                    "document_subtype": {
                      "type": "string",
                      "maxLength": 128,
                      "nullable": true,
                      "description": "Document subtype from the document registry, including the region prefix when the registry distinguishes regional variants."
                    },
                    "document_number": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Document number as printed on the ID.",
                      "example": "PAB123456"
                    },
                    "personal_number": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Personal / national identification number where the document carries one (e.g. Spanish DNI on a passport).",
                      "example": "99999999R"
                    },
                    "date_of_birth": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "Date of birth in `YYYY-MM-DD`. Any other format returns `400`.",
                      "example": "1980-01-01"
                    },
                    "date_of_issue": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "Issue date of the document in `YYYY-MM-DD`."
                    },
                    "expiration_date": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "Expiration date of the document in `YYYY-MM-DD`."
                    },
                    "issuing_state": {
                      "type": "string",
                      "nullable": true,
                      "description": "Issuing country as an ISO 3166-1 alpha-3 code. Case-insensitive (`esp` is stored as `ESP`); the human-readable `issuing_state_name` shown in [Get Decision](#get-/v3/session/-sessionId-/decision/) is recomputed from this value. Invalid codes return `400`.",
                      "example": "ESP"
                    },
                    "first_name": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Given name(s). The stored `full_name` is recomputed as `first_name + ' ' + last_name` on every call to this endpoint.",
                      "example": "Carmen"
                    },
                    "last_name": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Family name(s). Also feeds the recomputed `full_name`.",
                      "example": "Espanola Espanola"
                    },
                    "gender": {
                      "type": "string",
                      "enum": [
                        "M",
                        "F",
                        "U"
                      ],
                      "nullable": true,
                      "description": "Sex as printed on the document: `M` (male), `F` (female), or `U` (unknown). Any other value — including `X` — returns `400`."
                    },
                    "address": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `parsed_address` with the geocoder result."
                    },
                    "place_of_birth": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Place of birth as printed on the document."
                    },
                    "nationality": {
                      "type": "string",
                      "maxLength": 32,
                      "nullable": true,
                      "description": "Nationality as printed on the document. Free text up to 32 characters — typically an ISO 3166-1 alpha-3 code (e.g. `ESP`), but not validated as one."
                    },
                    "marital_status": {
                      "type": "string",
                      "enum": [
                        "SINGLE",
                        "MARRIED",
                        "DIVORCED",
                        "WIDOWED",
                        "UNKNOWN"
                      ],
                      "description": "Marital status where the document carries it. Any other value returns `400`."
                    },
                    "extra_fields": {
                      "type": "object",
                      "additionalProperties": true,
                      "nullable": true,
                      "description": "Merged view of the document's extra fields: the OCR-extracted values with all operator overrides applied. Keys removed via a `null` override are absent from this map."
                    },
                    "parsed_address": {
                      "type": "object",
                      "nullable": true,
                      "description": "Current structured address (with internal bookkeeping keys stripped), or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys.",
                      "properties": {
                        "address_type": {
                          "type": "string",
                          "nullable": true,
                          "description": "Free-form address classification (e.g. `home`)."
                        },
                        "city": {
                          "type": "string",
                          "nullable": true,
                          "example": "Madrid"
                        },
                        "region": {
                          "type": "string",
                          "nullable": true,
                          "description": "State / province / autonomous community."
                        },
                        "street_1": {
                          "type": "string",
                          "nullable": true,
                          "example": "Avda de Madrid 34"
                        },
                        "street_2": {
                          "type": "string",
                          "nullable": true
                        },
                        "postal_code": {
                          "type": "string",
                          "nullable": true,
                          "example": "28013"
                        },
                        "country": {
                          "type": "string",
                          "nullable": true,
                          "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.",
                          "example": "ES"
                        },
                        "formatted_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `parsed_address` edit.",
                          "example": "Avda de Madrid 34, Madrid, 28013, ES"
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "Updated KYC": {
                    "summary": "Full KYC record after the correction",
                    "value": {
                      "document_type": "P",
                      "document_subtype": null,
                      "document_number": "PAB123456",
                      "personal_number": "99999999R",
                      "date_of_birth": "1980-01-01",
                      "date_of_issue": "2021-02-09",
                      "expiration_date": "2031-02-08",
                      "issuing_state": "ESP",
                      "first_name": "Carmen",
                      "last_name": "Espanola Espanola",
                      "gender": "F",
                      "address": "AVDA DE MADRID 34, MADRID, MADRID 28013",
                      "place_of_birth": "MADRID",
                      "nationality": "ESP",
                      "marital_status": "MARRIED",
                      "extra_fields": {
                        "mrz_string": "P<ESPESPANOLA<<CARMEN<<<<<",
                        "blood_group": "O+"
                      },
                      "parsed_address": {
                        "city": "Madrid",
                        "street_1": "Avda de Madrid 34",
                        "postal_code": "28013",
                        "country": "ES",
                        "formatted_address": "Avda de Madrid 34, Madrid, 28013, ES"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field errors arrive as `{\"<field>\": [\"<message>\"]}`; errors on nested `parsed_address` / `extra_fields` keys use an object value: `{\"parsed_address\": {\"<key>\": \"<message>\"}}`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid gender": {
                    "summary": "gender must be M, F, or U",
                    "value": {
                      "gender": [
                        "\"X\" is not a valid choice."
                      ]
                    }
                  },
                  "Invalid document type": {
                    "summary": "Not a short code or long-form alias",
                    "value": {
                      "document_type": [
                        "\"PASSPORT\" is not a valid choice."
                      ]
                    }
                  },
                  "Invalid issuing state": {
                    "summary": "issuing_state is not ISO 3166-1 alpha-3",
                    "value": {
                      "issuing_state": [
                        "Invalid ISO 3166-1 alpha-3 country code for issuing_state."
                      ]
                    }
                  },
                  "Invalid marital status": {
                    "summary": "marital_status not in the enum",
                    "value": {
                      "marital_status": [
                        "\"ENGAGED\" is not a valid choice."
                      ]
                    }
                  },
                  "Bad date format": {
                    "summary": "Dates must be YYYY-MM-DD",
                    "value": {
                      "date_of_birth": [
                        "Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
                      ]
                    }
                  },
                  "Unsupported parsed_address key": {
                    "summary": "Only the seven documented keys are allowed",
                    "value": {
                      "parsed_address": {
                        "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country"
                      }
                    }
                  },
                  "Invalid parsed_address country": {
                    "summary": "Nested country must be alpha-2, not alpha-3",
                    "value": {
                      "parsed_address": {
                        "country": "Invalid ISO 3166-1 alpha-2 country code."
                      }
                    }
                  },
                  "extra_fields not an object": {
                    "summary": "extra_fields must be a JSON object",
                    "value": {
                      "extra_fields": [
                        "This field must be a JSON object."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "Credential lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown session, session in a non-editable state, or no KYC record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.",
            "content": {
              "application/json": {
                "examples": {
                  "Unknown session": {
                    "summary": "No session with this ID in your application (or it was deleted)",
                    "value": {
                      "detail": "Not found."
                    }
                  },
                  "Wrong session state": {
                    "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired",
                    "value": {
                      "detail": "No Session matches the given query."
                    }
                  },
                  "No KYC record": {
                    "summary": "The workflow never ran an ID-verification step",
                    "value": {
                      "detail": "The session does not have a KYC."
                    }
                  },
                  "No KYC for node": {
                    "summary": "node_id given but no KYC exists for it",
                    "value": {
                      "detail": "The session does not have a KYC for node 'feature_missing'."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Dedicated rate limit exceeded — 10 KYC data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Rate limited": {
                    "summary": "KYC update budget exhausted",
                    "value": {
                      "detail": "Session KYC data update rate limit exceeded. You can make up to 10 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"first_name\": \"Carmen\",\n    \"last_name\": \"Espanola Espanola\",\n    \"date_of_birth\": \"1980-01-01\",\n    \"issuing_state\": \"ESP\",\n    \"parsed_address\": {\n      \"street_1\": \"Avda de Madrid 34\",\n      \"city\": \"Madrid\",\n      \"postal_code\": \"28013\",\n      \"country\": \"ES\"\n    }\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/\",\n    headers={\n        \"x-api-key\": \"YOUR_API_KEY\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"first_name\": \"Carmen\",\n        \"last_name\": \"Espanola Espanola\",\n        \"date_of_birth\": \"1980-01-01\",\n        \"issuing_state\": \"ESP\",\n        \"extra_fields\": {\"blood_group\": \"O+\"},\n        \"parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n    },\n)\nresponse.raise_for_status()\nkyc = response.json()  # full current KYC record\nprint(kyc[\"first_name\"], kyc[\"parsed_address\"][\"formatted_address\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-data/',\n  {\n    method: 'PATCH',\n    headers: {\n      'x-api-key': process.env.DIDIT_API_KEY,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      first_name: 'Carmen',\n      last_name: 'Espanola Espanola',\n      date_of_birth: '1980-01-01',\n      issuing_state: 'ESP',\n      parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst kyc = await response.json(); // full current KYC record"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/features/{nodeId}/update-status/": {
      "patch": {
        "summary": "Update an individual feature (step) status",
        "description": "Set the status of a single verification feature (step) within a session **without changing the overall session decision**. Target the feature by its workflow graph `nodeId` (use `default` for non-graph workflows where the step has no node).\n\nWorks for both User Verification (KYC) and Business Verification (KYB) sessions and every step type — ID/OCR, Liveness, Face Match, NFC, AML, Proof of Address, Phone, Email, Database Validation, Questionnaire, and the KYB registry / documents / key-people checks.\n\n`new_status` must be one of `Approved`, `Declined`, or `In Review`, and must differ from the feature's current status. A successful call fires a `data.updated` webhook and appends an activity entry (with actor attribution) to the session's review trail. This endpoint is **not** available for standalone API sessions (`session_type = API`).\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.",
        "operationId": "patch_v3_session_update_feature_status",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "in": "path",
            "name": "nodeId",
            "required": true,
            "description": "Workflow graph node ID of the feature to update (for example `feature_ocr_1`). Use the literal value `default` for non-graph (single-step) workflows where the feature has no node ID.",
            "schema": {
              "type": "string",
              "example": "feature_ocr_1"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "new_status"
                ],
                "properties": {
                  "new_status": {
                    "type": "string",
                    "description": "Target status for this feature. Must differ from the current value.",
                    "enum": [
                      "Approved",
                      "Declined",
                      "In Review"
                    ],
                    "example": "Approved"
                  },
                  "comment": {
                    "type": "string",
                    "description": "Optional free-text note recorded on the session's review trail for this feature change.",
                    "example": "Document manually verified by compliance."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Feature status updated. Returns the targeted feature, its node ID, and the new status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "node_id": {
                      "type": "string",
                      "example": "feature_ocr_1"
                    },
                    "feature_type": {
                      "type": "string",
                      "description": "Model name of the updated feature (e.g. `KYC`, `Face`, `FaceMatch`, `AML`, `POA`).",
                      "example": "KYC"
                    },
                    "new_status": {
                      "type": "string",
                      "example": "Approved"
                    }
                  }
                },
                "example": {
                  "session_id": "11111111-2222-3333-4444-555555555555",
                  "node_id": "feature_ocr_1",
                  "feature_type": "KYC",
                  "new_status": "Approved"
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Returned when `new_status` is invalid or unchanged, or when the session is a standalone API session.",
            "content": {
              "application/json": {
                "examples": {
                  "Same Status": {
                    "summary": "new_status equals the current status",
                    "value": {
                      "new_status": "The new status is the same as the current status."
                    }
                  },
                  "API Session": {
                    "summary": "Feature status cannot be changed on standalone API sessions",
                    "value": {
                      "detail": "Cannot change feature status for API sessions."
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "API key lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The session was not found, or it has no feature with the given `nodeId`.",
            "content": {
              "application/json": {
                "examples": {
                  "Session Not Found": {
                    "summary": "Unknown session_id",
                    "value": {
                      "detail": "Session not found."
                    }
                  },
                  "Feature Not Found": {
                    "summary": "No feature for this node_id",
                    "value": {
                      "detail": "No feature found with node_id 'feature_ocr_1' in this session."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"new_status\": \"Approved\",\n    \"comment\": \"Document manually verified by compliance.\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/features/feature_ocr_1/update-status/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n    json={\"new_status\": \"Approved\", \"comment\": \"Document manually verified by compliance.\"},\n)\nprint(response.json())"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/update-aml-hit-status/": {
      "patch": {
        "summary": "Update an AML hit review status",
        "description": "Set the review status of a **single AML hit** (a potential sanction / PEP / watchlist / adverse-media match) returned by AML screening, without changing the overall session decision. Identify the hit by its `hit_id` (the `id` of an entry in the AML check's `hits` array — see the session decision).\n\n`review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`. Reviewer decisions are preserved across ongoing-monitoring updates, so a hit you mark `False Positive` stays that way when the provider re-screens.\n\nA successful call fires a `data.updated` webhook and appends an activity entry (with actor attribution) to the session's review trail. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.",
        "operationId": "patch_v3_session_update_aml_hit_status",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "in": "query",
            "name": "node_id",
            "required": false,
            "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.",
            "schema": {
              "type": "string",
              "example": "feature_aml_1"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hit_id",
                  "review_status"
                ],
                "properties": {
                  "hit_id": {
                    "type": "string",
                    "description": "The `id` of the hit in the AML check's `hits` array.",
                    "example": "abc123"
                  },
                  "review_status": {
                    "type": "string",
                    "description": "The new review status for the hit.",
                    "enum": [
                      "Unreviewed",
                      "Confirmed Match",
                      "False Positive",
                      "Inconclusive"
                    ],
                    "example": "False Positive"
                  },
                  "node_id": {
                    "type": "string",
                    "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.",
                    "example": "feature_aml_1"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hit review status updated.",
            "content": {
              "application/json": {
                "example": {
                  "message": "Hit status updated successfully",
                  "hit_id": "abc123",
                  "review_status": "False Positive"
                }
              }
            }
          },
          "400": {
            "description": "Validation error — `hit_id` empty or `review_status` not one of the allowed values.",
            "content": {
              "application/json": {
                "example": {
                  "review_status": [
                    "\"Maybe\" is not a valid choice."
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "API key lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The session was not found, the session has no AML check (optionally for the given `node_id`), or no hit matches `hit_id`.",
            "content": {
              "application/json": {
                "examples": {
                  "No AML Check": {
                    "summary": "Session has no AML check",
                    "value": {
                      "detail": "The session does not have an AML check."
                    }
                  },
                  "Hit Not Found": {
                    "summary": "Unknown hit_id",
                    "value": {
                      "detail": "Hit with ID 'abc123' not found in the AML check."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"hit_id\": \"abc123\",\n    \"review_status\": \"False Positive\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-aml-hit-status/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n    json={\"hit_id\": \"abc123\", \"review_status\": \"False Positive\"},\n)\nprint(response.json())"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/bulk-update-aml-hit-status/": {
      "patch": {
        "summary": "Bulk update AML hit review statuses",
        "description": "Update the review status of **multiple AML hits** in one call. Provide `hit_updates`, a list of `{ hit_id, review_status }` pairs. `review_status` is one of `Unreviewed`, `Confirmed Match`, `False Positive`, or `Inconclusive`.\n\nThe update is all-or-nothing: if any `hit_id` is not found in the AML check, the request returns `404` and no hit is changed. A successful call fires a single `data.updated` webhook and records one activity entry covering all changes. For graph workflows with multiple AML checks, pass `node_id` to target a specific check.\n\nAuthenticate with your API key (`x-api-key`) or a user authorization header — either must carry the `write:sessions` privilege.",
        "operationId": "patch_v3_session_bulk_update_aml_hit_status",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "sessionId",
            "required": true,
            "description": "UUID of the verification session. Accepts both user (KYC) and business (KYB) session IDs — the service resolves the ID across both session types.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "in": "query",
            "name": "node_id",
            "required": false,
            "description": "Workflow graph node ID. Required only when the session has multiple AML checks (one per node); omit it for single-AML sessions.",
            "schema": {
              "type": "string",
              "example": "feature_aml_1"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hit_updates"
                ],
                "properties": {
                  "hit_updates": {
                    "type": "array",
                    "description": "Non-empty list of hit updates.",
                    "items": {
                      "type": "object",
                      "required": [
                        "hit_id",
                        "review_status"
                      ],
                      "properties": {
                        "hit_id": {
                          "type": "string",
                          "example": "abc123"
                        },
                        "review_status": {
                          "type": "string",
                          "enum": [
                            "Unreviewed",
                            "Confirmed Match",
                            "False Positive",
                            "Inconclusive"
                          ],
                          "example": "Confirmed Match"
                        }
                      }
                    }
                  },
                  "node_id": {
                    "type": "string",
                    "description": "Optional graph node ID. Use it (here or as a query parameter) when the session has multiple AML checks.",
                    "example": "feature_aml_1"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "All hits updated.",
            "content": {
              "application/json": {
                "example": {
                  "message": "Updated 2 hit(s) successfully",
                  "updated_hits": [
                    "abc123",
                    "def456"
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Validation error — `hit_updates` empty, or an entry is missing `hit_id` / `review_status` or carries an invalid value.",
            "content": {
              "application/json": {
                "example": {
                  "hit_updates": [
                    "hit_updates cannot be empty"
                  ]
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "API key lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "The session was not found, the session has no AML check, or one or more `hit_id`s do not exist (no hit is changed in that case).",
            "content": {
              "application/json": {
                "example": {
                  "detail": "Hits not found: zzz999"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"hit_updates\": [\n      { \"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\" },\n      { \"hit_id\": \"def456\", \"review_status\": \"False Positive\" }\n    ]\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/bulk-update-aml-hit-status/\",\n    headers={\"x-api-key\": \"YOUR_API_KEY\", \"Content-Type\": \"application/json\"},\n    json={\n        \"hit_updates\": [\n            {\"hit_id\": \"abc123\", \"review_status\": \"Confirmed Match\"},\n            {\"hit_id\": \"def456\", \"review_status\": \"False Positive\"},\n        ]\n    },\n)\nprint(response.json())"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/update-poa-data/": {
      "patch": {
        "summary": "Update Proof of Address data extracted from the POA document on a verification session",
        "description": "Correct the fields extracted from the user's Proof of Address document — utility bill, bank statement, or other address evidence — after the verification has finished: issuer, dates, the printed name, the address itself, and banking details. Every field in the body is optional: only the keys you send are written, everything else is left untouched (`PATCH` semantics).\n\nThe session must belong to your application and be in `Approved`, `Declined`, `In Review`, or `Kyc Expired` status. Sessions in any other state (e.g. still `In Progress`) return `404` with `\"No Session matches the given query.\"` — distinct from the `\"Not found.\"` returned for an unknown or deleted `sessionId`. The session must also carry a POA record: sessions whose workflow never ran a Proof of Address step return `404` (`\"The session does not have a POA.\"`). For graph workflows with multiple POA steps, target a specific step with `node_id` (query parameter or body field); when omitted, the most recent POA record is patched.\n\n`extra_fields` is merged key-by-key: nine known keys (`bank_iban`, `additional_names`, …) write through to dedicated POA columns, any other key is kept as a custom extra, and `null` / `\"\"` deletes a key. `poa_parsed_address` accepts only the seven documented keys and recomputes `formatted_address` / `poa_formatted_address` after every edit. If the free-text `poa_address` changes, the server may also re-geocode it and refresh the structured address (skipped for sandbox sessions).\n\nUnlike the KYC variant of this endpoint, the `data.updated` [webhook](/integration/webhooks) (trigger `manual_review`) fires only when at least one stored value **actually changed** — re-sending identical values is a silent no-op. When values change, a `POA_DATA_UPDATED` activity entry recording the changed fields with previous and new values is appended to the session's `reviews`, visible in [Get Decision](#get-/v3/session/-sessionId-/decision/) and the console activity timeline. The session's status and decision are **not** re-evaluated — use [Update Status](#patch-/v3/session/-sessionId-/update-status/) to change the outcome.\n\nAccepts an API key or a console user token; either way the credential needs the `write:sessions` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. This endpoint has a dedicated rate limit of 10 requests per minute per API key (invalid requests count too); the shared write budget of 300 requests per minute also applies.\n\nAn empty JSON body `{}` is a no-op: it returns `200` echoing the current POA record without firing the webhook (`has_changes` stays false).",
        "operationId": "patch_v3_session_update_poa_data",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "description": "UUID of the User Verification (KYC) session whose attached POA record you are correcting.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11111111-2222-3333-4444-555555555555"
            }
          },
          {
            "name": "node_id",
            "in": "query",
            "required": false,
            "description": "Workflow graph node whose POA record to patch, for graph workflows with multiple Proof of Address steps (also accepted as a `node_id` body field). When omitted, the most recent POA record on the session is patched. If no POA exists for the given node, the call returns `404` (`\"The session does not have a POA for node '<node_id>'.\"`).",
            "schema": {
              "type": "string",
              "example": "feature_poa"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "All fields optional — send only what you want to change.",
                "properties": {
                  "issuing_state": {
                    "type": "string",
                    "nullable": true,
                    "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.",
                    "example": "ESP"
                  },
                  "document_type": {
                    "type": "string",
                    "enum": [
                      "UTILITY_BILL",
                      "BANK_STATEMENT",
                      "GOVERNMENT_ISSUED_DOCUMENT",
                      "OTHER_POA_DOCUMENT",
                      "UNKNOWN"
                    ],
                    "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.",
                    "example": "UTILITY_BILL"
                  },
                  "document_language": {
                    "type": "string",
                    "maxLength": 50,
                    "nullable": true,
                    "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.",
                    "example": "es"
                  },
                  "issuer": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Organization that issued the document (utility company, bank, government agency).",
                    "example": "Energy Provider S.A."
                  },
                  "issue_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.",
                    "example": "2026-04-12"
                  },
                  "poa_address": {
                    "type": "string",
                    "nullable": true,
                    "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result. The re-geocode also requires `issuing_state` to be present on the POA record.",
                    "example": "Avda de Madrid 34, 28013 Madrid"
                  },
                  "name_on_document": {
                    "type": "string",
                    "maxLength": 255,
                    "nullable": true,
                    "description": "Recipient name as printed on the document.",
                    "example": "Carmen Espanola"
                  },
                  "extra_fields": {
                    "type": "object",
                    "additionalProperties": true,
                    "nullable": true,
                    "description": "Document-specific extras, merged key-by-key. Nine keys map to dedicated POA columns: `bank_account_number`, `bank_iban`, `bank_sort_code`, `bank_routing_number`, `bank_swift_bic`, `bank_branch_name`, `bank_branch_address`, `document_phone_number`, and `additional_names` (must be a list of strings or `null`, otherwise `400`). Any other key is stored as a custom extra field. Per-key semantics: a value sets the key; `null` or `\"\"` deletes it. Must be a JSON object, otherwise `400`."
                  },
                  "poa_parsed_address": {
                    "type": "object",
                    "nullable": true,
                    "description": "Structured address. Only these nested keys are accepted: `address_type`, `city`, `region`, `street_1`, `street_2`, `postal_code`, `country` — any other key returns `400`. Per-key semantics: a value sets the key, `null` removes it. `formatted_address` (and the top-level `poa_formatted_address`) is recomputed server-side by joining `street_1`, `street_2`, `city`, `region`, `postal_code`, and `country` — you cannot set it directly.",
                    "properties": {
                      "address_type": {
                        "type": "string",
                        "nullable": true,
                        "description": "Free-form address classification (e.g. `home`)."
                      },
                      "city": {
                        "type": "string",
                        "nullable": true,
                        "example": "Madrid"
                      },
                      "region": {
                        "type": "string",
                        "nullable": true,
                        "description": "State / province / autonomous community."
                      },
                      "street_1": {
                        "type": "string",
                        "nullable": true,
                        "example": "Avda de Madrid 34"
                      },
                      "street_2": {
                        "type": "string",
                        "nullable": true
                      },
                      "postal_code": {
                        "type": "string",
                        "nullable": true,
                        "example": "28013"
                      },
                      "country": {
                        "type": "string",
                        "nullable": true,
                        "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.",
                        "example": "ES"
                      }
                    }
                  }
                }
              },
              "examples": {
                "Correct issuer and address": {
                  "summary": "Fix document classification, issuer, and the structured address",
                  "value": {
                    "issuing_state": "ESP",
                    "document_type": "BANK_STATEMENT",
                    "issuer": "Banco Example S.A.",
                    "issue_date": "2026-05-02",
                    "name_on_document": "Carmen Espanola",
                    "poa_address": "Avda de Madrid 34, 28013 Madrid",
                    "extra_fields": {
                      "bank_iban": "ES9121000418450200051332",
                      "additional_names": [
                        "Carmen E. Espanola"
                      ]
                    },
                    "poa_parsed_address": {
                      "street_1": "Avda de Madrid 34",
                      "city": "Madrid",
                      "postal_code": "28013",
                      "country": "ES"
                    }
                  }
                },
                "Remove a banking detail": {
                  "summary": "Delete one extra-field key, keep the rest",
                  "value": {
                    "extra_fields": {
                      "bank_iban": null
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "POA data updated. Returns the **full current POA record** (every updatable field with its stored value, plus the complete `extra_fields` map, `poa_parsed_address`, and `poa_formatted_address`) — not just the fields you submitted. The `data.updated` webhook fires only when at least one value actually changed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issuing_state": {
                      "type": "string",
                      "nullable": true,
                      "description": "Country that issued the POA document, ISO 3166-1 alpha-3. Case-insensitive (normalized to uppercase); invalid codes return `400`.",
                      "example": "ESP"
                    },
                    "document_type": {
                      "type": "string",
                      "enum": [
                        "UTILITY_BILL",
                        "BANK_STATEMENT",
                        "GOVERNMENT_ISSUED_DOCUMENT",
                        "OTHER_POA_DOCUMENT",
                        "UNKNOWN"
                      ],
                      "description": "POA document category. Values are uppercase — lowercase variants like `utility_bill` return `400`.",
                      "example": "UTILITY_BILL"
                    },
                    "document_language": {
                      "type": "string",
                      "maxLength": 50,
                      "nullable": true,
                      "description": "Language of the document, typically an ISO 639-1 code (e.g. `es`). Free text — not validated as a language code.",
                      "example": "es"
                    },
                    "issuer": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Organization that issued the document (utility company, bank, government agency).",
                      "example": "Energy Provider S.A."
                    },
                    "issue_date": {
                      "type": "string",
                      "format": "date",
                      "nullable": true,
                      "description": "Issue date of the POA document in `YYYY-MM-DD`. Any other format returns `400`.",
                      "example": "2026-04-12"
                    },
                    "poa_address": {
                      "type": "string",
                      "nullable": true,
                      "description": "Free-text address as printed on the document. When this value changes (non-sandbox sessions only) and the structured address is still missing or incomplete, the server re-geocodes it in the background of the same request and may overwrite `poa_parsed_address` / `poa_formatted_address` with the geocoder result.",
                      "example": "Avda de Madrid 34, 28013 Madrid"
                    },
                    "name_on_document": {
                      "type": "string",
                      "maxLength": 255,
                      "nullable": true,
                      "description": "Recipient name as printed on the document.",
                      "example": "Carmen Espanola"
                    },
                    "extra_fields": {
                      "type": "object",
                      "additionalProperties": true,
                      "nullable": true,
                      "description": "Always present in the response: the nine known keys (with `null` for unset ones, `additional_names` defaulting to `[]`) plus any custom keys previously stored."
                    },
                    "poa_parsed_address": {
                      "type": "object",
                      "nullable": true,
                      "description": "Current structured address, or `null` when none exists. Includes the recomputed `formatted_address` alongside the seven editable keys. Returned as stored — after a server-side geocode it can contain bookkeeping keys (e.g. `raw_results`) alongside the editable keys.",
                      "properties": {
                        "address_type": {
                          "type": "string",
                          "nullable": true,
                          "description": "Free-form address classification (e.g. `home`)."
                        },
                        "city": {
                          "type": "string",
                          "nullable": true,
                          "example": "Madrid"
                        },
                        "region": {
                          "type": "string",
                          "nullable": true,
                          "description": "State / province / autonomous community."
                        },
                        "street_1": {
                          "type": "string",
                          "nullable": true,
                          "example": "Avda de Madrid 34"
                        },
                        "street_2": {
                          "type": "string",
                          "nullable": true
                        },
                        "postal_code": {
                          "type": "string",
                          "nullable": true,
                          "example": "28013"
                        },
                        "country": {
                          "type": "string",
                          "nullable": true,
                          "description": "ISO 3166-1 alpha-2 country code. Lowercase input is normalized to uppercase; alpha-3 codes are rejected with `400`.",
                          "example": "ES"
                        },
                        "formatted_address": {
                          "type": "string",
                          "nullable": true,
                          "description": "Joined, human-readable form of the structured address. Recomputed server-side after every `poa_parsed_address` edit.",
                          "example": "Avda de Madrid 34, Madrid, 28013, ES"
                        }
                      }
                    },
                    "poa_formatted_address": {
                      "type": "string",
                      "nullable": true,
                      "description": "Top-level copy of the recomputed `formatted_address`, kept in sync when the structured address changes.",
                      "example": "Avda de Madrid 34, Madrid, 28013, ES"
                    }
                  }
                },
                "examples": {
                  "Updated POA": {
                    "summary": "Full POA record after the correction",
                    "value": {
                      "issuing_state": "ESP",
                      "document_type": "BANK_STATEMENT",
                      "document_language": "es",
                      "issuer": "Banco Example S.A.",
                      "issue_date": "2026-05-02",
                      "poa_address": "Avda de Madrid 34, 28013 Madrid",
                      "name_on_document": "Carmen Espanola",
                      "extra_fields": {
                        "bank_account_number": null,
                        "bank_iban": "ES9121000418450200051332",
                        "bank_sort_code": null,
                        "bank_routing_number": null,
                        "bank_swift_bic": null,
                        "bank_branch_name": null,
                        "bank_branch_address": null,
                        "document_phone_number": null,
                        "additional_names": [
                          "Carmen E. Espanola"
                        ]
                      },
                      "poa_parsed_address": {
                        "city": "Madrid",
                        "street_1": "Avda de Madrid 34",
                        "postal_code": "28013",
                        "country": "ES",
                        "formatted_address": "Avda de Madrid 34, Madrid, 28013, ES"
                      },
                      "poa_formatted_address": "Avda de Madrid 34, Madrid, 28013, ES"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field errors arrive as `{\"<field>\": [\"<message>\"]}`; errors on nested `poa_parsed_address` / `extra_fields` keys use an object value: `{\"extra_fields\": {\"<key>\": \"<message>\"}}`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid document type": {
                    "summary": "Enum is uppercase — lowercase variants are rejected",
                    "value": {
                      "document_type": [
                        "\"utility_bill\" is not a valid choice."
                      ]
                    }
                  },
                  "Invalid issuing state": {
                    "summary": "issuing_state is not ISO 3166-1 alpha-3",
                    "value": {
                      "issuing_state": [
                        "Invalid ISO 3166-1 alpha-3 country code for issuing_state."
                      ]
                    }
                  },
                  "additional_names not a list": {
                    "summary": "additional_names accepts a list of strings or null",
                    "value": {
                      "extra_fields": {
                        "additional_names": "Must be a list or null."
                      }
                    }
                  },
                  "Unsupported poa_parsed_address key": {
                    "summary": "Only the seven documented keys are allowed",
                    "value": {
                      "poa_parsed_address": {
                        "zipcode": "Unsupported key. Allowed keys: address_type, city, region, street_1, street_2, postal_code, country"
                      }
                    }
                  },
                  "Bad date format": {
                    "summary": "Dates must be YYYY-MM-DD",
                    "value": {
                      "issue_date": [
                        "Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "Credential lacks write:sessions",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown session, session in a non-editable state, or no POA record to patch. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.",
            "content": {
              "application/json": {
                "examples": {
                  "Unknown session": {
                    "summary": "No session with this ID in your application (or it was deleted)",
                    "value": {
                      "detail": "Not found."
                    }
                  },
                  "Wrong session state": {
                    "summary": "Session exists but is not Approved / Declined / In Review / Kyc Expired",
                    "value": {
                      "detail": "No Session matches the given query."
                    }
                  },
                  "No POA record": {
                    "summary": "The workflow never ran a Proof of Address step",
                    "value": {
                      "detail": "The session does not have a POA."
                    }
                  },
                  "No POA for node": {
                    "summary": "node_id given but no POA exists for it",
                    "value": {
                      "detail": "The session does not have a POA for node 'feature_missing'."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Dedicated rate limit exceeded — 10 POA data updates per minute per API key (rejected requests count toward the budget). The shared 300/min write budget can also trigger with its own message. Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Rate limited": {
                    "summary": "POA update budget exhausted",
                    "value": {
                      "detail": "Session POA data update rate limit exceeded. You can make up to 10 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"issuing_state\": \"ESP\",\n    \"document_type\": \"BANK_STATEMENT\",\n    \"issuer\": \"Banco Example S.A.\",\n    \"issue_date\": \"2026-05-02\",\n    \"name_on_document\": \"Carmen Espanola\",\n    \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n    \"extra_fields\": {\n      \"bank_iban\": \"ES9121000418450200051332\"\n    },\n    \"poa_parsed_address\": {\n      \"city\": \"Madrid\",\n      \"postal_code\": \"28013\",\n      \"country\": \"ES\"\n    }\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/\",\n    headers={\n        \"x-api-key\": \"YOUR_API_KEY\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"issuing_state\": \"ESP\",\n        \"document_type\": \"BANK_STATEMENT\",\n        \"issuer\": \"Banco Example S.A.\",\n        \"issue_date\": \"2026-05-02\",\n        \"name_on_document\": \"Carmen Espanola\",\n        \"poa_address\": \"Avda de Madrid 34, 28013 Madrid\",\n        \"extra_fields\": {\"bank_iban\": \"ES9121000418450200051332\"},\n        \"poa_parsed_address\": {\"city\": \"Madrid\", \"postal_code\": \"28013\", \"country\": \"ES\"},\n    },\n)\nresponse.raise_for_status()\npoa = response.json()  # full current POA record\nprint(poa[\"issuer\"], poa[\"poa_formatted_address\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/11111111-2222-3333-4444-555555555555/update-poa-data/',\n  {\n    method: 'PATCH',\n    headers: {\n      'x-api-key': process.env.DIDIT_API_KEY,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      issuing_state: 'ESP',\n      document_type: 'BANK_STATEMENT',\n      issuer: 'Banco Example S.A.',\n      issue_date: '2026-05-02',\n      name_on_document: 'Carmen Espanola',\n      poa_address: 'Avda de Madrid 34, 28013 Madrid',\n      poa_parsed_address: { city: 'Madrid', postal_code: '28013', country: 'ES' },\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst poa = await response.json(); // full current POA record"
          }
        ]
      }
    },
    "/v3/session/{sessionId}/kyb/{companyUuid}/update-data/": {
      "patch": {
        "summary": "Update registry-extracted company data on a Business Verification (KYB) session",
        "description": "Correct the registry-extracted company data on a Business Verification (KYB) session — company name, registration number, addresses, status, contact details — identified by `companyUuid`. Typical callers are a compliance analyst fixing a registry misread, or your back office syncing corrected company data. Every field in the body is optional (`PATCH` semantics); an empty JSON body (`{}`) is a no-op that simply echoes the current company record.\n\nEdits are written in two places at once: fields with a canonical company column (name, registration number, country, incorporation date, tax number, type, statuses, capital fields (`registered_capital`, `registered_capital_amount`, `registered_capital_currency`), alternative names, nature of business, website, email, phone, LEI, location, and `legal_address` → `registered_address`) update that column, and **every** submitted key is also merged into the `registry_data` snapshot (dates as ISO strings) so the \"extracted from registry\" view reflects the correction. Fields without a column — `region`, `registration_date`, `dissolution_date`, `control_scheme` — land in `registry_data` only. `user_provided_data` (what the end user confirmed in the hosted flow) is never touched, and `last_console_edit_at` is stamped on every non-empty PATCH.\n\nUnlike the KYC/POA update endpoints, there is **no session-status gate**: the company can be edited at any point in the business session's lifecycle. The company must belong to the session in the path (`404` with `\"KYB company does not belong to the given session.\"` otherwise) and to your application. Every non-empty PATCH fires the `data.updated` [webhook](/integration/webhooks) on the business session (trigger `console_registry_edit`) — even when the submitted values equal the stored ones. The session's status and decision are **not** re-evaluated, and no review/audit entry is recorded.\n\nAccepts an API key or a console user token; either way the credential needs the `write:businesses` permission. Authentication and permission failures both return `403` — this API never responds `401`. Note that the session lookup runs before credential checks, so an unknown `sessionId` returns `404` even with missing credentials. The shared write budget of 300 requests per minute per API key applies.\n\nFor User Verification sessions, the KYC counterpart is `PATCH /v3/session/{sessionId}/update-data/`; to change the session decision itself use `PATCH /v3/session/{sessionId}/update-status/`.",
        "operationId": "patch_v3_session_kyb_company_update_data",
        "tags": [
          "Sessions"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "description": "UUID of the parent Business Verification (KYB) session.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "33333333-4444-5555-6666-777777777777"
            }
          },
          {
            "name": "companyUuid",
            "in": "path",
            "required": true,
            "description": "UUID of the company record to update (`registry_checks[].company.uuid` on the business session decision). Must belong to the session referenced by `sessionId`.",
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "88888888-9999-aaaa-bbbb-cccccccccccc"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "All fields optional — send only what you want to change. Provided fields update the canonical company columns (where one exists) and are merged into the `registry_data` snapshot.",
                "properties": {
                  "company_name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Legal name of the company. Written to the canonical column and the registry snapshot.",
                    "example": "Docs Probe Corp"
                  },
                  "registration_number": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Official registration number with the company registry.",
                    "example": "DPC-001"
                  },
                  "country_code": {
                    "type": "string",
                    "maxLength": 2,
                    "description": "Country of incorporation, ISO 3166-1 alpha-2. Case-insensitive (`us` is stored as `US`). Alpha-3 codes fail the 2-character limit with `400`.",
                    "example": "US"
                  },
                  "region": {
                    "type": "string",
                    "maxLength": 10,
                    "nullable": true,
                    "description": "Subdivision / region tag where the registry has one (e.g. US state). Stored in the registry snapshot only — it has no canonical column."
                  },
                  "incorporation_date": {
                    "type": "string",
                    "format": "date",
                    "description": "Incorporation date in `YYYY-MM-DD`. Optional like every other field — the `PATCH` is partial. Any other date format returns `400`.",
                    "example": "2015-04-12"
                  },
                  "registration_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Registration date where the registry distinguishes it from incorporation. Stored in the registry snapshot only."
                  },
                  "dissolution_date": {
                    "type": "string",
                    "format": "date",
                    "nullable": true,
                    "description": "Dissolution date for companies that are no longer active. Stored in the registry snapshot only."
                  },
                  "tax_number": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Tax identification number."
                  },
                  "legal_address": {
                    "type": "string",
                    "description": "Legal / registered address. Aliased server-side to the canonical `registered_address` column — the response returns it under `registered_address`, while the registry snapshot keeps the `legal_address` key you sent.",
                    "example": "1 Example Plaza, Wilmington, DE 19801"
                  },
                  "company_type": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Legal entity type (e.g. `Limited Liability Company`, `PLC`, `GmbH`).",
                    "example": "Limited Liability Company"
                  },
                  "registry_status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "dissolved",
                      "deregistered",
                      "see full details",
                      "authorised",
                      "appointed representative",
                      "unauthorised",
                      "inactive",
                      "no longer authorised",
                      "closed",
                      "struck off"
                    ],
                    "nullable": true,
                    "description": "Registry-reported company status. Lowercase, some values contain spaces (e.g. `struck off`, `no longer authorised`). Any other value returns `400`.",
                    "example": "active"
                  },
                  "verification_status": {
                    "type": "string",
                    "enum": [
                      "verified",
                      "failed",
                      "unknown"
                    ],
                    "nullable": true,
                    "description": "Outcome of the registry verification. Any other value returns `400`.",
                    "example": "verified"
                  },
                  "alternative_names": {
                    "type": "string",
                    "description": "Trade names, former legal names, DBA names (free text)."
                  },
                  "nature_of_business": {
                    "type": "string",
                    "description": "Short description of the company's activity.",
                    "example": "Software development"
                  },
                  "registered_capital": {
                    "type": "string",
                    "maxLength": 100,
                    "description": "Human-readable registered capital (e.g. `USD 50000`). Use `registered_capital_amount` + `registered_capital_currency` for structured values, or this field for free-form text.",
                    "example": "USD 50000"
                  },
                  "registered_capital_amount": {
                    "type": "number",
                    "format": "double",
                    "nullable": true,
                    "description": "Numeric registered-capital amount (up to 20 digits, 2 decimal places). Sets the `registered_capital_amount` column and is mirrored into `registry_data` as a decimal string; the response echoes it as a decimal string (e.g. `\"1500000.50\"`)."
                  },
                  "registered_capital_currency": {
                    "type": "string",
                    "maxLength": 3,
                    "nullable": true,
                    "description": "ISO 4217 currency code for `registered_capital_amount`. Case-insensitive (`usd` is stored as `USD`); values that are not exactly 3 letters return `400` with the ISO 4217 message. Sets the canonical `registered_capital_currency` column and is mirrored into `registry_data`.",
                    "example": "USD"
                  },
                  "website": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Company website. Bare domains are accepted and normalized with an `https://` prefix (`docsprobe.example.com` is stored as `https://docsprobe.example.com`); values that still fail URL validation return `400`.",
                    "example": "https://docsprobe.example.com"
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "description": "Contact email on file with the registry.",
                    "example": "legal@docsprobe.example.com"
                  },
                  "phone": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "Contact phone. Must match `+`, digits, spaces, dashes, dots, or parentheses, with 7-15 digits total — otherwise `400`.",
                    "example": "+1 302 555 0143"
                  },
                  "legal_entity_identifier": {
                    "type": "string",
                    "maxLength": 50,
                    "description": "LEI, where the company has one."
                  },
                  "location_of_registration": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "City / subdivision of the registry of record."
                  },
                  "vat_number": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "EU VAT number. Merged into the `registry_data` snapshot only — editing it here does not re-run VIES validation, so `vat_validation_status` and the other `vat_*` result fields are unchanged."
                  },
                  "control_scheme": {
                    "type": "string",
                    "description": "Control / governance scheme when the registry exposes it. Stored in the registry snapshot only."
                  }
                }
              },
              "examples": {
                "Correct registry data": {
                  "summary": "Fix the company's registry-extracted fields",
                  "value": {
                    "company_name": "Docs Probe Corp",
                    "registration_number": "DPC-001",
                    "country_code": "US",
                    "incorporation_date": "2015-04-12",
                    "company_type": "Limited Liability Company",
                    "legal_address": "1 Example Plaza, Wilmington, DE 19801",
                    "registry_status": "active",
                    "verification_status": "verified",
                    "website": "docsprobe.example.com",
                    "email": "legal@docsprobe.example.com",
                    "phone": "+1 302 555 0143",
                    "registered_capital": "USD 50000",
                    "nature_of_business": "Software development"
                  }
                },
                "Single field": {
                  "summary": "Only the keys you send are written",
                  "value": {
                    "registry_status": "dissolved"
                  }
                },
                "No-op": {
                  "summary": "Empty body echoes the current record without firing the webhook",
                  "value": {}
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Company data updated (or echoed unchanged for an empty body). Returns the full KYB company record — the same shape as `registry_checks[].company` on the business session decision. Note how `legal_address` comes back as `registered_address`. `registered_capital_amount`/`registered_capital_currency` populate their canonical columns and are mirrored into `registry_data`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "description": "Full KYB company record — the same shape as the `company` object inside `registry_checks[]` on the business session decision.",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Company record identifier (the `companyUuid` path parameter)."
                    },
                    "node_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "Workflow graph node this company record belongs to, when the workflow is graph-based."
                    },
                    "status": {
                      "type": "string",
                      "description": "Feature lifecycle status of the registry check (e.g. `Not Finished`, `Approved`, `Declined`, `In Review`)."
                    },
                    "registry_status": {
                      "type": "string",
                      "nullable": true,
                      "description": "Registry-reported company status (e.g. `active`, `dissolved`, `struck off`)."
                    },
                    "data_resolved": {
                      "type": "boolean",
                      "description": "`true` when the company data is complete — always `true` for manual-entry companies; for registry-sourced companies, `true` once the registry fetch resolved."
                    },
                    "company_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "registration_number": {
                      "type": "string",
                      "nullable": true
                    },
                    "country_code": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 3166-1 alpha-2 country of incorporation."
                    },
                    "region": {
                      "type": "string",
                      "nullable": true
                    },
                    "company_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "incorporation_date": {
                      "type": "string",
                      "format": "date",
                      "nullable": true
                    },
                    "registered_address": {
                      "type": "string",
                      "nullable": true,
                      "description": "Canonical registered address — updated when you send `legal_address`."
                    },
                    "tax_number": {
                      "type": "string",
                      "nullable": true
                    },
                    "risk_level": {
                      "type": "string",
                      "nullable": true
                    },
                    "verification_status": {
                      "type": "string",
                      "nullable": true,
                      "description": "`verified`, `failed`, or `unknown`."
                    },
                    "is_from_registry": {
                      "type": "boolean",
                      "description": "`true` when the company was pre-filled from a registry search rather than entered manually."
                    },
                    "fetch_status": {
                      "type": "string",
                      "nullable": true,
                      "description": "Registry fetch lifecycle for registry-sourced companies (`pending` / `resolved`); `null` for manual entries."
                    },
                    "alternative_names": {
                      "type": "string",
                      "nullable": true
                    },
                    "nature_of_business": {
                      "type": "string",
                      "nullable": true
                    },
                    "registered_capital": {
                      "type": "string",
                      "nullable": true
                    },
                    "registered_capital_amount": {
                      "type": "string",
                      "nullable": true,
                      "description": "Canonical column. Returned as a decimal string (e.g. `\"2750000.00\"`)."
                    },
                    "registered_capital_currency": {
                      "type": "string",
                      "nullable": true,
                      "description": "Canonical column. Set by the registry flow or by console/API edits via this endpoint."
                    },
                    "website": {
                      "type": "string",
                      "nullable": true
                    },
                    "email": {
                      "type": "string",
                      "nullable": true
                    },
                    "phone": {
                      "type": "string",
                      "nullable": true
                    },
                    "legal_entity_identifier": {
                      "type": "string",
                      "nullable": true
                    },
                    "location_of_registration": {
                      "type": "string",
                      "nullable": true
                    },
                    "vat_number": {
                      "type": "string",
                      "nullable": true,
                      "description": "EU VAT number, when collected during the hosted registry step or edited afterwards."
                    },
                    "vat_validation_status": {
                      "type": "string",
                      "enum": [
                        "valid",
                        "invalid",
                        "could_not_validate",
                        "not_applicable"
                      ],
                      "description": "Result of the EU VIES check run at registry submit. `not_applicable` when no VAT number was provided or the company is outside the EU VAT area (EU-27 plus Northern Ireland)."
                    },
                    "vat_validated_name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Trader name registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                    },
                    "vat_validated_address": {
                      "type": "string",
                      "nullable": true,
                      "description": "Trader address registered with VIES, when the member state discloses it. Only set when the VAT number is `valid`."
                    },
                    "vat_checked_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Timestamp of the VIES check. `null` when no check ran."
                    },
                    "financial_summary": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true
                    },
                    "officers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "Directors / officers attached to the company, including per-person KYC progress."
                    },
                    "beneficial_owners": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "Beneficial owners attached to the company, including per-person KYC progress."
                    },
                    "addresses": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "Registry address detail, when the registry returned any."
                    },
                    "industries": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "Registry industry classification, when available."
                    },
                    "accounts": {
                      "type": "array",
                      "nullable": true,
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      },
                      "description": "Registry filing-accounts detail, when available."
                    },
                    "registry_data": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "Raw registry payload snapshot. Every key you PATCH is merged into this map (dates serialized as ISO strings), so the \"extracted from registry\" view reflects operator corrections."
                    },
                    "user_provided_data": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "What the end user typed during the hosted registry-confirmation step. Never modified by this endpoint."
                    },
                    "confirmed_by_user_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "When the end user confirmed the company data in the hosted flow."
                    },
                    "last_console_edit_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Stamped on every successful non-empty PATCH, so UIs can surface \"edited by operator\" context."
                    },
                    "is_editable": {
                      "type": "boolean",
                      "description": "`true` while the end user can still edit the registry-sourced data in the hosted flow (registry-sourced and not yet confirmed)."
                    }
                  }
                },
                "examples": {
                  "Updated company": {
                    "summary": "Full company record after the correction",
                    "value": {
                      "uuid": "6b9479f5-6769-46c8-baf1-df63bbbc5f8c",
                      "node_id": null,
                      "status": "Not Finished",
                      "registry_status": "active",
                      "data_resolved": true,
                      "company_name": "Docs Probe Corp",
                      "registration_number": "DPC-001",
                      "country_code": "US",
                      "region": null,
                      "company_type": "Limited Liability Company",
                      "incorporation_date": "2015-04-12",
                      "registered_address": "1 Example Plaza, Wilmington, DE 19801",
                      "tax_number": null,
                      "risk_level": null,
                      "verification_status": "verified",
                      "is_from_registry": false,
                      "fetch_status": null,
                      "alternative_names": null,
                      "nature_of_business": "Software development",
                      "registered_capital": "USD 50000",
                      "registered_capital_amount": null,
                      "registered_capital_currency": "USD",
                      "website": "https://docsprobe.example.com",
                      "email": "legal@docsprobe.example.com",
                      "phone": "+1 302 555 0143",
                      "legal_entity_identifier": null,
                      "location_of_registration": null,
                      "financial_summary": null,
                      "officers": [],
                      "beneficial_owners": [],
                      "addresses": null,
                      "industries": null,
                      "accounts": null,
                      "registry_data": {
                        "company_name": "Docs Probe Corp",
                        "registration_number": "DPC-001",
                        "country_code": "US",
                        "incorporation_date": "2015-04-12",
                        "legal_address": "1 Example Plaza, Wilmington, DE 19801",
                        "company_type": "Limited Liability Company",
                        "registry_status": "active",
                        "verification_status": "verified",
                        "nature_of_business": "Software development",
                        "registered_capital": "USD 50000",
                        "registered_capital_currency": "USD",
                        "website": "https://docsprobe.example.com",
                        "email": "legal@docsprobe.example.com",
                        "phone": "+1 302 555 0143"
                      },
                      "user_provided_data": null,
                      "confirmed_by_user_at": null,
                      "last_console_edit_at": "2026-06-12T00:02:34.276061Z",
                      "is_editable": false
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error. Field errors arrive as `{\"<field>\": [\"<message>\"]}`. Max-length checks run before format checks, so `country_code: \"USA\"` reports the 2-character limit rather than the ISO message.",
            "content": {
              "application/json": {
                "examples": {
                  "Alpha-3 country code": {
                    "summary": "country_code must be alpha-2 — length check fires first",
                    "value": {
                      "country_code": [
                        "Ensure this field has no more than 2 characters."
                      ]
                    }
                  },
                  "Non-alphabetic country code": {
                    "summary": "2 characters but not letters",
                    "value": {
                      "country_code": [
                        "country_code must be a 2-letter ISO 3166-1 alpha-2 code."
                      ]
                    }
                  },
                  "Invalid currency": {
                    "summary": "3 characters but not an ISO 4217 code",
                    "value": {
                      "registered_capital_currency": [
                        "Currency must be a 3-letter ISO 4217 code (e.g. USD, EUR, GBP)."
                      ]
                    }
                  },
                  "Invalid phone": {
                    "summary": "Phone fails the format / digit-count check",
                    "value": {
                      "phone": [
                        "Invalid phone number format."
                      ]
                    }
                  },
                  "Invalid registry status": {
                    "summary": "registry_status not in the enum",
                    "value": {
                      "registry_status": [
                        "\"liquidated\" is not a valid choice."
                      ]
                    }
                  },
                  "Invalid email": {
                    "summary": "Malformed email address",
                    "value": {
                      "email": [
                        "Enter a valid email address."
                      ]
                    }
                  },
                  "Invalid website": {
                    "summary": "Value fails URL validation even after https:// normalization",
                    "value": {
                      "website": [
                        "Enter a valid URL."
                      ]
                    }
                  },
                  "Bad date format": {
                    "summary": "Dates must be YYYY-MM-DD",
                    "value": {
                      "incorporation_date": [
                        "Date has wrong format. Use one of these formats instead: YYYY-MM-DD."
                      ]
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Missing/invalid credentials or insufficient permissions. This API returns `403` for authentication failures — never `401`.",
            "content": {
              "application/json": {
                "examples": {
                  "Invalid Credentials": {
                    "summary": "Missing or invalid x-api-key",
                    "value": {
                      "detail": "Authentication credentials were not provided or are invalid."
                    }
                  },
                  "Missing Privilege": {
                    "summary": "Credential lacks write:businesses",
                    "value": {
                      "detail": "You do not have permission to perform this action."
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Unknown session, unknown company, or a company that belongs to a different session. Each case has a distinct `detail` message (see examples). The unknown-session lookup runs before credential checks.",
            "content": {
              "application/json": {
                "examples": {
                  "Unknown session": {
                    "summary": "No session with this ID in your application (or it was deleted)",
                    "value": {
                      "detail": "Not found."
                    }
                  },
                  "Unknown company": {
                    "summary": "No company with this UUID in your application",
                    "value": {
                      "detail": "No KYBCompany matches the given query."
                    }
                  },
                  "Company / session mismatch": {
                    "summary": "companyUuid exists but belongs to another session",
                    "value": {
                      "detail": "KYB company does not belong to the given session."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Shared write rate limit exceeded (300 POST/PATCH/DELETE requests per minute per API key). Inspect `Retry-After` and the `X-RateLimit-*` response headers before retrying.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                },
                "examples": {
                  "Rate limited": {
                    "summary": "Write budget exhausted",
                    "value": {
                      "detail": "Write request rate limit exceeded. You can make up to 300 requests per minute."
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH \\\n  https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/88888888-9999-aaaa-bbbb-cccccccccccc/update-data/ \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"company_name\": \"Docs Probe Corp\",\n    \"registration_number\": \"DPC-001\",\n    \"country_code\": \"US\",\n    \"incorporation_date\": \"2015-04-12\",\n    \"company_type\": \"Limited Liability Company\",\n    \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n    \"registry_status\": \"active\"\n  }'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import requests\n\nresponse = requests.patch(\n    \"https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/\"\n    \"88888888-9999-aaaa-bbbb-cccccccccccc/update-data/\",\n    headers={\n        \"x-api-key\": \"YOUR_API_KEY\",\n        \"Content-Type\": \"application/json\",\n    },\n    json={\n        \"company_name\": \"Docs Probe Corp\",\n        \"registration_number\": \"DPC-001\",\n        \"country_code\": \"US\",\n        \"incorporation_date\": \"2015-04-12\",\n        \"legal_address\": \"1 Example Plaza, Wilmington, DE 19801\",\n        \"registry_status\": \"active\",\n    },\n)\nresponse.raise_for_status()\ncompany = response.json()  # full company record\nprint(company[\"company_name\"], company[\"registered_address\"])"
          },
          {
            "lang": "javascript",
            "label": "JavaScript",
            "source": "const response = await fetch(\n  'https://verification.didit.me/v3/session/33333333-4444-5555-6666-777777777777/kyb/' +\n    '88888888-9999-aaaa-bbbb-cccccccccccc/update-data/',\n  {\n    method: 'PATCH',\n    headers: {\n      'x-api-key': process.env.DIDIT_API_KEY,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      company_name: 'Docs Probe Corp',\n      registration_number: 'DPC-001',\n      country_code: 'US',\n      incorporation_date: '2015-04-12',\n      legal_address: '1 Example Plaza, Wilmington, DE 19801',\n      registry_status: 'active',\n    }),\n  },\n);\nif (!response.ok) throw new Error(`HTTP ${response.status}`);\nconst company = await response.json(); // full company record"
          }
        ]
      }
    },
    "/v3/travel-rule/settings/": {
      "get": {
        "summary": "Get Travel Rule settings",
        "description": "Read the application's Travel Rule settings and VASP profile. Auto-creates a default (disabled) settings row and an empty profile on first read.",
        "operationId": "getTravelRuleSettings",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET https://verification.didit.me/v3/travel-rule/settings/ \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Current Travel Rule settings and VASP profile.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleSettingsDetail"
                },
                "examples": {
                  "Disabled (default)": {
                    "value": {
                      "is_enabled": false,
                      "jurisdiction": "EU",
                      "name_matching_strictness": "DEFAULT",
                      "confirmation_timeout_hours": 48,
                      "timeout_outcome": "HOLD",
                      "threshold_amount": "0.00",
                      "legal_name": "",
                      "lei": "",
                      "compliance_email": "",
                      "is_discoverable": true
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Update Travel Rule settings",
        "description": "Upsert the application's Travel Rule settings and VASP profile. Partial update — only send the fields you want to change. Setting `is_enabled: true` requires a non-blank `legal_name` (already stored or included in the same request), otherwise returns 400.",
        "operationId": "updateTravelRuleSettings",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRuleSettingsUpdate"
              },
              "example": {
                "is_enabled": true,
                "legal_name": "Origin CASP SL",
                "jurisdiction": "EU",
                "name_matching_strictness": "DEFAULT",
                "confirmation_timeout_hours": 48,
                "timeout_outcome": "HOLD",
                "is_discoverable": true
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PUT https://verification.didit.me/v3/travel-rule/settings/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"is_enabled\": true, \"legal_name\": \"Origin CASP SL\", \"jurisdiction\": \"EU\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated settings and profile, in the same shape as GET.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleSettingsDetail"
                }
              }
            }
          },
          "400": {
            "description": "Validation error, e.g. enabling without a legal_name.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "legal_name": [
                    "legal_name is required to enable travel rule."
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v3/travel-rule/wallet-addresses/": {
      "get": {
        "summary": "List wallet address book entries",
        "description": "Paginated list of your application's Travel Rule wallet address book, newest first.",
        "operationId": "listTravelRuleWalletAddresses",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated wallet address book entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WalletAddressEntry"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Register a wallet address",
        "description": "Add an entry to your Travel Rule wallet address book so inbound INTERNAL-rail transfers can resolve against it.",
        "operationId": "createTravelRuleWalletAddress",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletAddressEntryCreate"
              },
              "example": {
                "address": "0xBeneficiaryWallet01",
                "chain": "ethereum",
                "holder_name": "Ana Diaz",
                "holder_vendor_data": "user-042",
                "self_declared": true
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/travel-rule/wallet-addresses/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"address\": \"0xBeneficiaryWallet01\", \"chain\": \"ethereum\", \"holder_name\": \"Ana Diaz\", \"self_declared\": true}'"
          }
        ],
        "responses": {
          "201": {
            "description": "The created entry.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletAddressEntry"
                }
              }
            }
          },
          "400": {
            "description": "Duplicate address for this application and chain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                },
                "example": {
                  "address": [
                    "A wallet address entry with this address and chain already exists."
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v3/travel-rule/wallet-addresses/{entry_uuid}/": {
      "patch": {
        "summary": "Update wallet address holder metadata",
        "description": "Partial update of holder_name, holder_vendor_data, entity_type, and travel_address. Setting travel_address attaches a TRP routing address to the entry; invalid values are rejected with 400. The response echoes only these four fields.",
        "operationId": "updateTravelRuleWalletAddress",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "entry_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WalletAddressEntryUpdate"
              },
              "example": {
                "holder_name": "Ana Updated",
                "travel_address": "ta..."
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"holder_name\": \"Ana Updated\", \"travel_address\": \"ta...\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletAddressEntryUpdate"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Soft-delete a wallet address",
        "description": "Soft-deletes the entry (deleted_at is set). The address is freed up for re-registration; ownership proofs are retained for audit.",
        "operationId": "deleteTravelRuleWalletAddress",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "entry_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE https://verification.didit.me/v3/travel-rule/wallet-addresses/{entry_uuid}/ \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          }
        }
      }
    },
    "/v3/travel-rule/vasps/": {
      "get": {
        "summary": "Search the VASP directory",
        "description": "Paginated search across discoverable Didit customers with Travel Rule enabled, plus VASPs catalogued in the internal counterparty registry. Excludes your own application's profile.",
        "operationId": "listTravelRuleVasps",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Case-insensitive substring match on VASP name."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/travel-rule/vasps/?search=Bene' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated, name-sorted VASP directory results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/VaspDirectoryEntry"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/transactions/{transaction_id}/travel-rule/": {
      "patch": {
        "summary": "Finish, cancel, or resend a Travel Rule transfer",
        "description": "Provide `payment_txn_id` to report the on-chain hash and move an outbound COMPLETED transfer to FINISHED (its INTERNAL-rail counterpart also moves to FINISHED), `{\"action\": \"cancel\"}` to cancel a non-terminal transfer, or `{\"action\": \"resend\"}` to re-run routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA (improve the counterparty data first, e.g. add a travel_address to the destination wallet entry).",
        "operationId": "patchTravelRuleTransfer",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "description": "Didit-stable transaction UUID that owns the transfer.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRuleTransferPatchRequest"
              },
              "examples": {
                "Finish": {
                  "value": {
                    "payment_txn_id": "0xchainhash"
                  }
                },
                "Cancel": {
                  "value": {
                    "action": "cancel"
                  }
                },
                "Resend": {
                  "value": {
                    "action": "resend"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"payment_txn_id\": \"0xchainhash\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleTransferDetail"
                }
              }
            }
          },
          "400": {
            "description": "Neither payment_txn_id nor a supported action (cancel, resend) was provided."
          },
          "409": {
            "description": "The transfer is not in a state that allows this operation (e.g. not COMPLETED/outbound for finish, already terminal for cancel, or not an outbound transfer in a resendable status for resend)."
          }
        }
      }
    },
    "/v3/transactions/{transaction_id}/travel-rule/ownership/": {
      "post": {
        "summary": "Confirm or deny wallet ownership",
        "description": "Called by the beneficiary's application when its INTERNAL-rail transfer is UNCONFIRMED_OWNERSHIP. Confirming marks the matching wallet address book entry as ownership-verified and runs the beneficiary name match on both sides of the transfer; denying declines both sides.",
        "operationId": "confirmTravelRuleOwnership",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "description": "Didit-stable transaction UUID that owns the transfer (the beneficiary side's transaction).",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRuleOwnershipConfirmRequest"
              },
              "example": {
                "confirmed": true
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/transactions/{transaction_id}/travel-rule/ownership/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"confirmed\": true}'"
          }
        ],
        "responses": {
          "200": {
            "description": "The updated transfer. originator_data/beneficiary_data are omitted until ownership_confirmed is true on an inbound transfer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleTransferDetail"
                }
              }
            }
          },
          "409": {
            "description": "The transfer is not currently UNCONFIRMED_OWNERSHIP, or its matching wallet address book entry was deleted."
          }
        }
      }
    },
    "/v3/travel-rule/pickup/{token}/": {
      "get": {
        "summary": "Get email-rail pickup info",
        "description": "Public, unauthenticated. Lets a non-Didit counterparty VASP preview the pending exchange from the emailed pickup link. The token itself is the authentication.",
        "operationId": "getTravelRulePickup",
        "tags": [
          "Travel Rule"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl https://verification.didit.me/v3/travel-rule/pickup/{token}/"
          }
        ],
        "responses": {
          "200": {
            "description": "Pickup preview.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRulePickupInfo"
                }
              }
            }
          },
          "404": {
            "description": "Unknown token."
          },
          "410": {
            "description": "The pickup window has closed (expired, or the transfer already left AWAITING_COUNTERPARTY)."
          }
        }
      }
    },
    "/v3/travel-rule/pickup/{token}/respond/": {
      "post": {
        "summary": "Respond to an email-rail pickup",
        "description": "Public, unauthenticated. Accepts or declines the exchange from the emailed pickup link. On accept, beneficiary_name is checked against the originator's name_matching_strictness policy.",
        "operationId": "respondTravelRulePickup",
        "tags": [
          "Travel Rule"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRulePickupRespondRequest"
              },
              "example": {
                "decision": "accept",
                "beneficiary_name": "Carlos Ruiz"
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/travel-rule/pickup/{token}/respond/ \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"decision\": \"accept\", \"beneficiary_name\": \"Carlos Ruiz\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "The resulting exchange status.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "status": "COMPLETED"
                }
              }
            }
          },
          "404": {
            "description": "Unknown token."
          },
          "410": {
            "description": "The pickup window has closed."
          }
        }
      }
    },
    "/v3/travel-rule/inbound/": {
      "post": {
        "summary": "Register an after-deposit (sunrise) inbound transfer",
        "description": "Register Travel Rule data for a crypto deposit that already settled on-chain before any exchange took place. Creates an inbound travelRule transaction (or reuses an unclaimed deposit) and resolves the transfer against your wallet address book. A transaction.created webhook fires for newly-created transactions.",
        "operationId": "registerTravelRuleInbound",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRuleInboundRequest"
              },
              "example": {
                "chain": "ethereum",
                "txid": "0xdeposit-hash",
                "wallet_address": "0xYourDepositWallet",
                "amount": "500.00",
                "currency": "USDC",
                "originator_data": {
                  "name": "Origin CASP SL"
                },
                "beneficiary_data": {
                  "name": "Ana Diaz"
                },
                "originating_vasp": {
                  "name": "Origin CASP SL",
                  "lei": "",
                  "travel_address": "ta..."
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/travel-rule/inbound/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"chain\":\"ethereum\",\"txid\":\"0xdeposit-hash\",\"wallet_address\":\"0xYourDepositWallet\",\"amount\":\"500.00\",\"currency\":\"USDC\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "An existing unclaimed deposit was reused (created is false).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleInboundResponse"
                }
              }
            }
          },
          "201": {
            "description": "A new inbound transaction and transfer were created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleInboundResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v3/travel-rule/widget-session/": {
      "post": {
        "summary": "Mint a wallet-ownership widget session",
        "description": "Mint a hosted wallet-ownership widget URL/token. Send the customer to the returned url to prove control of a wallet (message signing, Satoshi test, screenshot, or self-declaration). The widget is web-only in this release - open the url in the system browser or a custom tab, never inside a mobile SDK webview.",
        "operationId": "createTravelRuleWidgetSession",
        "tags": [
          "Travel Rule"
        ],
        "security": [
          {
            "ApiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelRuleWidgetSessionCreate"
              },
              "example": {
                "wallet_address": "0xBeneficiaryWallet01",
                "chain": "ethereum",
                "holder_name": "Ana Diaz",
                "vendor_data": "user-042",
                "transaction_id": "22222222-3333-4444-5555-666666666666",
                "expires_in_minutes": 60
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST https://verification.didit.me/v3/travel-rule/widget-session/ \\\n  -H 'x-api-key: YOUR_API_KEY' -H 'Content-Type: application/json' \\\n  -d '{\"wallet_address\":\"0xBeneficiaryWallet01\",\"chain\":\"ethereum\",\"holder_name\":\"Ana Diaz\"}'"
          }
        ],
        "responses": {
          "201": {
            "description": "The minted widget session.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleWidgetSessionResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v3/travel-rule/widget/{token}/": {
      "get": {
        "summary": "Get wallet-ownership widget context",
        "description": "Public read endpoint keyed by the widget session token - no API key required. Returns the widget context plus the status of every proof, so you can poll it to detect completion; completed_at becomes non-null once a proof is verified. A SATOSHI_TEST proof can sit at status PENDING while its deposit is seen on-chain but unconfirmed - a background job automatically rechecks it and completes it once the deposit confirms, so no client action is needed while it waits. Each proof carries the fields its method needs: deposit_address, expected_amount, and its own expires_at for SATOSHI_TEST; challenge for MESSAGE_SIGNING. Prefer the travel_rule.status.updated webhook over polling when the session is linked to a transfer.",
        "operationId": "getTravelRuleWidgetContext",
        "tags": [
          "Travel Rule"
        ],
        "security": [],
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "description": "Opaque session token returned when the widget session was minted.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl https://verification.didit.me/v3/travel-rule/widget/{token}/"
          }
        ],
        "responses": {
          "200": {
            "description": "The widget context and proof statuses.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelRuleWidgetContext"
                }
              }
            }
          },
          "404": {
            "description": "Unknown token."
          },
          "410": {
            "description": "The session expired without completing."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/": {
      "get": {
        "summary": "List cases",
        "description": "List cases for the application, newest first. Supports filtering, search, and sorting; see query parameters. Paginated with `limit`/`offset`.",
        "operationId": "list_cases",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size. Defaults to 50."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset of the first record."
          },
          {
            "name": "status__in",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated statuses to include, e.g. OPEN,AWAITING_USER."
          },
          {
            "name": "priority",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated priorities to include."
          },
          {
            "name": "source__in",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated sources to include."
          },
          {
            "name": "blueprint",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter to cases on this blueprint."
          },
          {
            "name": "assigned_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter to cases assigned to this user."
          },
          {
            "name": "unassigned",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "true for unassigned cases, false for assigned cases."
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter to cases carrying this exact tag."
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Matches title, case_number, tag, subject name/UUID/external ID, linked session UUID, or linked transaction UUID."
          },
          {
            "name": "date_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Created-at lower bound (inclusive)."
          },
          {
            "name": "date_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Created-at upper bound (inclusive)."
          },
          {
            "name": "due_before",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "due_at upper bound."
          },
          {
            "name": "overdue",
            "in": "query",
            "schema": {
              "type": "boolean"
            },
            "description": "true to return only overdue open/awaiting-user cases."
          },
          {
            "name": "ordering",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created_at",
                "-created_at",
                "due_at",
                "-due_at"
              ],
              "default": "-created_at"
            },
            "description": "Sort order. due_at sorts nulls last."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/?status__in=OPEN,AWAITING_USER&ordering=-due_at' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.get(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    params={'status__in': 'OPEN,AWAITING_USER', 'ordering': '-due_at'},\n    timeout=10,\n)\nresp.raise_for_status()\nfor case in resp.json()['results']:\n    print(case['case_number'], case['title'], case['priority'], case['due_at'])"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated case list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or null."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or null."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseListItem"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create case",
        "description": "Create a case anchored to exactly one subject (a user or a business), directly or by deriving the subject from an attached session/transaction link. Source is always `MANUAL` for API-created cases.",
        "operationId": "create_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "title"
                ],
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "blueprint": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "CaseBlueprint UUID. Omit for no blueprint."
                  },
                  "priority": {
                    "type": "string",
                    "enum": [
                      "LOW",
                      "MEDIUM",
                      "HIGH"
                    ],
                    "default": "MEDIUM"
                  },
                  "due_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "subject": {
                    "type": "object",
                    "description": "Required unless links is provided; the case's exactly-one subject.",
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "user",
                          "business"
                        ]
                      },
                      "uuid": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  },
                  "links": {
                    "type": "array",
                    "description": "Sessions/business sessions/transactions to attach on creation, e.g. [{\"entity_type\": \"transaction\", \"entity_uuid\": \"...\"}]. When subject is omitted, the subject is derived from the first link's owner.",
                    "items": {
                      "type": "object"
                    }
                  },
                  "assigned_to": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "User UUID to assign immediately."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"title\": \"Manual - Jane Doe\", \"blueprint\": \"b1de0000-0000-4000-8000-000000000002\", \"priority\": \"HIGH\", \"subject\": {\"type\": \"user\", \"uuid\": \"5b9a0000-0000-4000-8000-00000000f00d\"}, \"tags\": [\"review\"]}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    json={\n        'title': 'Manual - Jane Doe',\n        'blueprint': 'b1de0000-0000-4000-8000-000000000002',\n        'priority': 'HIGH',\n        'subject': {'type': 'user', 'uuid': '5b9a0000-0000-4000-8000-00000000f00d'},\n    },\n    timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['case_number'], case['uuid'])"
          }
        ],
        "responses": {
          "201": {
            "description": "Case created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "400": {
            "description": "Neither subject nor links provided, or the subject/blueprint/assignee was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/": {
      "get": {
        "summary": "Get case",
        "description": "Retrieve a case with its subject, blueprint, notes, events, links, and checklist.",
        "operationId": "get_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Case detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "404": {
            "description": "Case (or nested resource) not found in this application.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update case",
        "description": "Update the case's title, priority, due date, or tags. Logs PRIORITY_CHANGED/DUE_DATE_CHANGED/TAG_ADDED/TAG_REMOVED events as applicable.",
        "operationId": "update_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "title": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "priority": {
                    "type": "string",
                    "enum": [
                      "LOW",
                      "MEDIUM",
                      "HIGH"
                    ]
                  },
                  "due_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"priority\": \"HIGH\", \"due_at\": \"2026-08-01T00:00:00Z\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete case",
        "description": "Soft-delete a case. There is no undelete endpoint; the row is retained internally for audit purposes only.",
        "operationId": "delete_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Case deleted."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/assign/": {
      "post": {
        "summary": "Assign or unassign case",
        "description": "Assign the case to a user, or unassign it by sending assignee_user_id: null. assignee_email/assignee_name auto-provision a User row when the assignee does not exist yet.",
        "operationId": "assign_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "assignee_user_id"
                ],
                "properties": {
                  "assignee_user_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "null to unassign."
                  },
                  "assignee_email": {
                    "type": "string",
                    "format": "email",
                    "nullable": true
                  },
                  "assignee_name": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/assign/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Case assigned/unassigned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/resolve/": {
      "post": {
        "summary": "Resolve case",
        "description": "Resolve the case as false_positive or valid_threat. Blocked with 400 if the blueprint's checklist is not optional and items remain incomplete, or if the blueprint requires a resolution note and none is given. When the case's blueprint has a four_eyes_checker_blueprint configured, the resolution is staged (case.pending_resolution) and the case moves to the checker blueprint instead of resolving immediately; a different user must call approve or reject-approval.",
        "operationId": "resolve_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "resolution"
                ],
                "properties": {
                  "resolution": {
                    "type": "string",
                    "enum": [
                      "FALSE_POSITIVE",
                      "VALID_THREAT"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "nullable": true,
                    "description": "Required when the blueprint sets require_resolution_note."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"resolution\": \"FALSE_POSITIVE\", \"note\": \"Confirmed legitimate activity after reviewing documents.\"}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/resolve/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    json={'resolution': 'FALSE_POSITIVE', 'note': 'Confirmed legitimate activity.'},\n    timeout=10,\n)\nresp.raise_for_status()\ncase = resp.json()\nprint(case['status'], case['resolution'], case.get('pending_resolution'))"
          }
        ],
        "responses": {
          "200": {
            "description": "Resolved, or staged for 4-eyes approval.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "400": {
            "description": "Incomplete checklist items, or a resolution note is required.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reopen/": {
      "post": {
        "summary": "Reopen case",
        "description": "Move an awaiting-user or resolved case back to open, clearing resolution, resolution_notes, resolved_at, and resolved_by_email.",
        "operationId": "reopen_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reopen/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Reopen case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/request-info/": {
      "post": {
        "summary": "Request info (awaiting user)",
        "description": "Move an open case to awaiting_user while more information is collected from the verified user. Fails with 400 if a resolution is currently pending 4-eyes approval.",
        "operationId": "request_case_info",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/request-info/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Request info (awaiting user).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/escalate/": {
      "post": {
        "summary": "Escalate case",
        "description": "Move the case to its blueprint's configured escalation_blueprint. Requires the current blueprint to have escalation_enabled and an escalation_blueprint set, otherwise 400.",
        "operationId": "escalate_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/escalate/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Escalate case.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/transfer/": {
      "post": {
        "summary": "Transfer case",
        "description": "Move the case to a different, active blueprint of the same application. Requires the current blueprint to have transfer_enabled, otherwise 400.",
        "operationId": "transfer_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "target_blueprint"
                ],
                "properties": {
                  "target_blueprint": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/transfer/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"target_blueprint\": \"b1de0000-0000-4000-8000-000000000002\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Case transferred.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/approve/": {
      "post": {
        "summary": "Approve staged resolution (4-eyes)",
        "description": "Apply a resolution staged by resolve on a maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403.",
        "operationId": "approve_case_resolution",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/approve/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Resolution applied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "403": {
            "description": "The caller is the same user who submitted the resolution (or the case has no recorded maker); a different officer must approve it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Case has no resolution pending approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/reject-approval/": {
      "post": {
        "summary": "Reject staged resolution (4-eyes)",
        "description": "Reject a resolution staged by resolve and return the case to its maker blueprint. The caller must be a different user than the one who submitted it, otherwise 403.",
        "operationId": "reject_case_resolution",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "notes": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/reject-approval/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"notes\": \"Needs a clearer rationale before this can be approved.\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Resolution rejected, case returned to the maker blueprint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "403": {
            "description": "The caller is the same user who submitted the resolution (or the case has no recorded maker); a different officer must reject it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Case has no resolution pending approval.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/entity-actions/": {
      "post": {
        "summary": "Apply entity action",
        "description": "Act on the case's subject without leaving the case: set_status changes the user/business status, add_tags applies applicant tags, add_note posts an internal case note, and add_to_blocklist adds the subject's latest document and face to the blocklist. Logs an ENTITY_ACTION event.",
        "operationId": "apply_case_entity_action",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "set_status",
                      "add_tags",
                      "add_note",
                      "add_to_blocklist"
                    ]
                  },
                  "status": {
                    "type": "string",
                    "nullable": true,
                    "description": "Required for set_status; a valid VendorUser/VendorBusiness status."
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Required for add_tags."
                  },
                  "note": {
                    "type": "string",
                    "nullable": true,
                    "description": "Required for add_note."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/entity-actions/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"action\": \"add_tags\", \"tags\": [\"high-risk\"]}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Action applied.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entity_type": {
                      "type": "string",
                      "enum": [
                        "user",
                        "business"
                      ]
                    },
                    "entity_uuid": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "action": {
                      "type": "string",
                      "enum": [
                        "set_status",
                        "add_tags",
                        "add_note",
                        "add_to_blocklist"
                      ]
                    }
                  },
                  "description": "Plus action-specific keys: previous_status/new_status, tags_added, comment_uuid, or blocklisted_document_uuids/blocklisted_face_uuids."
                }
              }
            }
          },
          "400": {
            "description": "Unsupported action, missing required field, or the case has no subject.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/print/": {
      "get": {
        "summary": "Print case (PDF)",
        "description": "Render the full case (summary, subject, checklist state, notes, attached sessions/transactions, events, FIU report list) as a PDF and return a presigned download URL.",
        "operationId": "print_case",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/print/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Presigned PDF URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/": {
      "get": {
        "summary": "List case notes",
        "description": "List notes on the case, newest first.",
        "operationId": "list_case_notes",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated notes.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or null."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or null."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseNote"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create case note",
        "description": "Post a plain-text note, optionally mentioning teammates, tagging it, or attaching files uploaded via notes/presign/.",
        "operationId": "create_case_note",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "comment"
                ],
                "properties": {
                  "comment": {
                    "type": "string"
                  },
                  "mentioned_emails": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "email"
                    }
                  },
                  "is_internal": {
                    "type": "boolean",
                    "default": false
                  },
                  "attachments": {
                    "type": "array",
                    "description": "s3_key values returned by POST notes/presign/; each must belong to this case.",
                    "items": {
                      "type": "object",
                      "properties": {
                        "s3_key": {
                          "type": "string"
                        },
                        "filename": {
                          "type": "string"
                        },
                        "file_size": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "tags": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"comment\": \"Called the customer, awaiting a reply with supporting documents.\", \"tags\": [\"follow-up\"]}'"
          }
        ],
        "responses": {
          "201": {
            "description": "Note created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseNote"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/{note_uuid}/": {
      "delete": {
        "summary": "Delete case note",
        "description": "Soft-delete a note.",
        "operationId": "delete_case_note",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "note_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "d0000000-0000-4000-8000-000000000003"
            },
            "description": "Note UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/d0000000-0000-4000-8000-000000000003/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Note deleted."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/notes/presign/": {
      "post": {
        "summary": "Upload note attachment",
        "description": "Upload a file (JPEG, PNG, WebP, PDF, plain text, or CSV; max 5 MB) to attach to a note. Returns the s3_key to pass in the note's attachments array.",
        "operationId": "upload_case_note_attachment",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -F 'file=@evidence.pdf'"
          }
        ],
        "responses": {
          "201": {
            "description": "File uploaded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "s3_key": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "filename": {
                      "type": "string"
                    },
                    "file_size": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "No file, unsupported content type, or file exceeds 5 MB.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Get note attachment URL",
        "description": "Mint a fresh presigned download URL for an attachment already stored on one of the case's notes.",
        "operationId": "get_case_note_attachment_url",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "s3_key",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "The attachment's s3_key, as returned by upload or listed on the note.",
            "required": true
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/notes/presign/?s3_key=case-notes/c1a5e000-0000-4000-8000-000000000001/evidence.pdf' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Presigned URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "s3_key": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "filename": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "s3_key missing or does not belong to this case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/": {
      "get": {
        "summary": "List case links",
        "description": "List sessions, business sessions, and transactions attached to the case.",
        "operationId": "list_case_links",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Case links.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CaseLink"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add case link",
        "description": "Attach a session, business session, or transaction to the case. Rejected with 400 if the entity belongs to a different subject than the case.",
        "operationId": "create_case_link",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "entity_type",
                  "entity_uuid"
                ],
                "properties": {
                  "entity_type": {
                    "type": "string",
                    "enum": [
                      "session",
                      "business_session",
                      "transaction"
                    ]
                  },
                  "entity_uuid": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"entity_type\": \"transaction\", \"entity_uuid\": \"77000000-0000-4000-8000-000000000077\"}'"
          }
        ],
        "responses": {
          "201": {
            "description": "Link created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseLink"
                }
              }
            }
          },
          "400": {
            "description": "Entity belongs to a different profile than the case subject.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/links/{link_uuid}/": {
      "delete": {
        "summary": "Remove case link",
        "description": "Detach a linked session, business session, or transaction from the case.",
        "operationId": "delete_case_link",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "link_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "11000000-0000-4000-8000-000000000004"
            },
            "description": "Link UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/links/11000000-0000-4000-8000-000000000004/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Link removed."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/events/": {
      "get": {
        "summary": "List case events",
        "description": "Read-only audit timeline of the case (created, status changes, assignment, resolution, escalation, transfers, tag/priority/due-date changes, entity actions, and more).",
        "operationId": "list_case_events",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/events/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated events, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows. Exact up to 100, capped at 100 beyond that for performance. Rely on `next` being null to detect the last page."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the next page, or null."
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true,
                      "description": "Absolute URL of the previous page, or null."
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseEvent"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/": {
      "get": {
        "summary": "List case checklist",
        "description": "List the case's checklist items, copied from the blueprint's content_config.checklist.items at creation, ordered.",
        "operationId": "list_case_checklist",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Checklist items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CaseChecklistItem"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/checklist/{item_uuid}/": {
      "patch": {
        "summary": "Toggle checklist item",
        "description": "Mark a checklist item complete or incomplete. Resolution is blocked while any item is incomplete, unless the blueprint marks the checklist optional.",
        "operationId": "toggle_case_checklist_item",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "item_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c4ec0000-0000-4000-8000-000000000005"
            },
            "description": "Checklist item UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "is_completed"
                ],
                "properties": {
                  "is_completed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/checklist/c4ec0000-0000-4000-8000-000000000005/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"is_completed\": true}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated checklist item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseChecklistItem"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/aml/": {
      "get": {
        "summary": "List case AML hits",
        "description": "List AML screening hits for the case's subject. Same shape as the hits returned by POST /v3/aml/.",
        "operationId": "list_case_aml_hits",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/aml/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "AML hits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/statistics/": {
      "get": {
        "summary": "Case analytics",
        "description": "Aggregate case analytics for the date range (defaults to the last 30 days; max 731 days): a created-vs-reviewed time series, per-officer/blueprint/source/status tables, rollup totals, and the live open/overdue queue depth.",
        "operationId": "get_case_analytics",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "date_from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Range start (inclusive). Defaults to 30 days ago."
          },
          {
            "name": "date_to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            },
            "description": "Range end (inclusive). Defaults to today."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/statistics/?date_from=2026-06-01&date_to=2026-06-30' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Analytics payload.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "date": {
                            "type": "string",
                            "format": "date"
                          },
                          "created": {
                            "type": "integer"
                          },
                          "reviewed": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "tables": {
                      "type": "object",
                      "properties": {
                        "officer": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions."
                              },
                              "created": {
                                "type": "integer"
                              },
                              "assigned": {
                                "type": "integer"
                              },
                              "resolved": {
                                "type": "integer"
                              },
                              "threat_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "false_positive_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "unresolved": {
                                "type": "integer"
                              },
                              "reassignment_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "escalation_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_assign_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_resolve_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_handling_hours": {
                                "type": "number",
                                "format": "float"
                              }
                            }
                          }
                        },
                        "blueprint": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions."
                              },
                              "created": {
                                "type": "integer"
                              },
                              "assigned": {
                                "type": "integer"
                              },
                              "resolved": {
                                "type": "integer"
                              },
                              "threat_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "false_positive_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "unresolved": {
                                "type": "integer"
                              },
                              "reassignment_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "escalation_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_assign_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_resolve_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_handling_hours": {
                                "type": "number",
                                "format": "float"
                              }
                            }
                          }
                        },
                        "source": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions."
                              },
                              "created": {
                                "type": "integer"
                              },
                              "assigned": {
                                "type": "integer"
                              },
                              "resolved": {
                                "type": "integer"
                              },
                              "threat_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "false_positive_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "unresolved": {
                                "type": "integer"
                              },
                              "reassignment_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "escalation_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_assign_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_resolve_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_handling_hours": {
                                "type": "number",
                                "format": "float"
                              }
                            }
                          }
                        },
                        "status": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "key": {
                                "type": "string",
                                "description": "Dimension value: officer email, \"blueprint_name:uuid\", source, or status. \"unassigned\"/\"none\" for missing dimensions."
                              },
                              "created": {
                                "type": "integer"
                              },
                              "assigned": {
                                "type": "integer"
                              },
                              "resolved": {
                                "type": "integer"
                              },
                              "threat_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "false_positive_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "unresolved": {
                                "type": "integer"
                              },
                              "reassignment_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "escalation_rate": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_assign_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_resolve_hours": {
                                "type": "number",
                                "format": "float"
                              },
                              "avg_handling_hours": {
                                "type": "number",
                                "format": "float"
                              }
                            }
                          }
                        }
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "created": {
                          "type": "integer"
                        },
                        "assigned": {
                          "type": "integer"
                        },
                        "resolved": {
                          "type": "integer"
                        },
                        "threat_rate": {
                          "type": "number",
                          "format": "float"
                        },
                        "false_positive_rate": {
                          "type": "number",
                          "format": "float"
                        },
                        "unresolved": {
                          "type": "integer"
                        },
                        "reassignment_rate": {
                          "type": "number",
                          "format": "float"
                        },
                        "escalation_rate": {
                          "type": "number",
                          "format": "float"
                        },
                        "avg_assign_hours": {
                          "type": "number",
                          "format": "float"
                        },
                        "avg_resolve_hours": {
                          "type": "number",
                          "format": "float"
                        },
                        "avg_handling_hours": {
                          "type": "number",
                          "format": "float"
                        }
                      }
                    },
                    "queue": {
                      "type": "object",
                      "description": "Live snapshot, not scoped to the date range.",
                      "properties": {
                        "open": {
                          "type": "integer"
                        },
                        "overdue": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/status/": {
      "post": {
        "summary": "Bulk update case status",
        "description": "Apply the same status transition to several cases in one call: OPEN reopens, AWAITING_USER requests info, RESOLVED resolves (resolution required). Each case is evaluated independently; gate failures (incomplete checklist, missing required note) are reported per case without aborting the batch.",
        "operationId": "bulk_update_case_status",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "case_uuids",
                  "status"
                ],
                "properties": {
                  "case_uuids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "OPEN",
                      "AWAITING_USER",
                      "RESOLVED"
                    ]
                  },
                  "resolution": {
                    "type": "string",
                    "enum": [
                      "FALSE_POSITIVE",
                      "VALID_THREAT"
                    ],
                    "description": "Required when status is RESOLVED."
                  },
                  "note": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/status/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"status\": \"RESOLVED\", \"resolution\": \"FALSE_POSITIVE\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-case results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "integer"
                    },
                    "failures": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "case_uuid": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/bulk/assign/": {
      "post": {
        "summary": "Bulk assign cases",
        "description": "Assign (or unassign, with assignee_user_id: null) several cases at once. All cases must share the same blueprint; mismatches are reported per case without aborting the batch.",
        "operationId": "bulk_assign_cases",
        "tags": [
          "Cases"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "case_uuids",
                  "assignee_user_id"
                ],
                "properties": {
                  "case_uuids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1
                  },
                  "assignee_user_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true
                  },
                  "assignee_email": {
                    "type": "string",
                    "format": "email",
                    "nullable": true
                  },
                  "assignee_name": {
                    "type": "string",
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/bulk/assign/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"case_uuids\": [\"c1a5e000-0000-4000-8000-000000000001\"], \"assignee_user_id\": \"9a000000-0000-4000-8000-0000000000aa\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-case results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "integer"
                    },
                    "failures": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "case_uuid": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "detail": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/": {
      "get": {
        "summary": "List blueprints",
        "description": "List case blueprints for the application.",
        "operationId": "list_case_blueprints",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated blueprint list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows, capped at 100 for performance."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseBlueprintListItem"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create blueprint",
        "description": "Create a case blueprint: general settings (assignment, escalation, transfer, 4-eyes pairing) plus content_config, the six sections that drive the case page.",
        "operationId": "create_case_blueprint",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "assignee_pool"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 24,
                    "description": "Max 24 characters, e.g. \"AML investigation\"."
                  },
                  "description": {
                    "type": "string",
                    "nullable": true
                  },
                  "assignee_pool": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"."
                  },
                  "assignment_method": {
                    "type": "string",
                    "enum": [
                      "MANUAL",
                      "AUTOMATIC"
                    ],
                    "default": "MANUAL"
                  },
                  "max_active_cases": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"."
                  },
                  "escalation_enabled": {
                    "type": "boolean",
                    "default": false
                  },
                  "escalation_blueprint": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set."
                  },
                  "transfer_enabled": {
                    "type": "boolean",
                    "default": false
                  },
                  "four_eyes_checker_blueprint": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue."
                  },
                  "require_resolution_note": {
                    "type": "boolean",
                    "default": false
                  },
                  "content_config": {
                    "$ref": "#/components/schemas/CaseContentConfig"
                  },
                  "is_active": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\": \"AML investigation\", \"assignee_pool\": [\"9a000000-0000-4000-8000-0000000000aa\"], \"assignment_method\": \"AUTOMATIC\", \"max_active_cases\": 15, \"require_resolution_note\": true, \"content_config\": {\"aml_control\": {\"enabled\": true, \"financial\": true, \"identity\": false}}}'"
          }
        ],
        "responses": {
          "201": {
            "description": "Blueprint created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseBlueprintDetail"
                }
              }
            }
          },
          "400": {
            "description": "Name over 24 chars, empty assignee_pool, or a cross-referenced blueprint (escalation/checker) not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/{blueprint_uuid}/": {
      "get": {
        "summary": "Get blueprint",
        "description": "Retrieve a blueprint, including its connected creation sources (AML config, transaction rules, workflow nodes) and which blueprints escalate to it.",
        "operationId": "get_case_blueprint",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "blueprint_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "b1de0000-0000-4000-8000-000000000002"
            },
            "description": "Blueprint UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Blueprint detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseBlueprintDetail"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update blueprint",
        "description": "Partially update a blueprint's general settings or content_config.",
        "operationId": "update_case_blueprint",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "blueprint_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "b1de0000-0000-4000-8000-000000000002"
            },
            "description": "Blueprint UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 24,
                    "description": "Max 24 characters, e.g. \"AML investigation\"."
                  },
                  "description": {
                    "type": "string",
                    "nullable": true
                  },
                  "assignee_pool": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minItems": 1,
                    "description": "List of User UUIDs. Empty arrays are rejected with \"Pick at least one assignee\"."
                  },
                  "assignment_method": {
                    "type": "string",
                    "enum": [
                      "MANUAL",
                      "AUTOMATIC"
                    ]
                  },
                  "max_active_cases": {
                    "type": "integer",
                    "nullable": true,
                    "minimum": 1,
                    "description": "Automatic-mode threshold: \"fewer than N cases of this blueprint assigned\"."
                  },
                  "escalation_enabled": {
                    "type": "boolean"
                  },
                  "escalation_blueprint": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Target blueprint UUID. Not enforced at save time, but the escalate action fails with 400 until both escalation_enabled and escalation_blueprint are set."
                  },
                  "transfer_enabled": {
                    "type": "boolean"
                  },
                  "four_eyes_checker_blueprint": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Setting this enables 4-eyes review: resolutions on this blueprint stage on the checker blueprint's queue."
                  },
                  "require_resolution_note": {
                    "type": "boolean"
                  },
                  "content_config": {
                    "$ref": "#/components/schemas/CaseContentConfig"
                  },
                  "is_active": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"transfer_enabled\": true}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated blueprint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseBlueprintDetail"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete blueprint",
        "description": "Soft-delete a blueprint. Deletion always succeeds and does not touch the blueprint's cases: existing cases keep their blueprint reference and its settings continue to apply to them. A deleted blueprint no longer appears in blueprint lists and can no longer be chosen as a transfer target; if a maker blueprint is deleted while a resolution is pending 4-eyes approval, a rejected resolution leaves the case on the checker blueprint.",
        "operationId": "delete_case_blueprint",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "blueprint_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "b1de0000-0000-4000-8000-000000000002"
            },
            "description": "Blueprint UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/b1de0000-0000-4000-8000-000000000002/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Blueprint deleted."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/": {
      "get": {
        "summary": "List blueprint presets",
        "description": "List the four built-in presets (Screening hit, AML investigation, Suspicious activity, KYC review) with their content_config and whether each is already installed on this application.",
        "operationId": "list_case_blueprint_presets",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Preset catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "preset_key": {
                            "type": "string",
                            "enum": [
                              "screening_hit",
                              "aml_investigation",
                              "suspicious_activity",
                              "kyc"
                            ]
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "content_config": {
                            "$ref": "#/components/schemas/CaseContentConfig"
                          },
                          "installed": {
                            "type": "boolean"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/blueprints/presets/install/": {
      "post": {
        "summary": "Install blueprint presets",
        "description": "Copy one or more presets into the application as editable blueprints. Idempotent per preset_key; omit preset_keys to install every preset not yet installed.",
        "operationId": "install_case_blueprint_presets",
        "tags": [
          "Case Blueprints"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "preset_keys": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "nullable": true,
                    "description": "Subset of screening_hit, aml_investigation, suspicious_activity, kyc. Omit to install all."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/blueprints/presets/install/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"preset_keys\": [\"aml_investigation\", \"kyc\"]}'"
          }
        ],
        "responses": {
          "200": {
            "description": "No new presets to install (all requested presets already exist).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "One or more presets installed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/report-templates/": {
      "get": {
        "summary": "List report templates",
        "description": "List FIU report templates for the application.",
        "operationId": "list_report_templates",
        "tags": [
          "Report Templates"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows, capped at 100 for performance."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ReportTemplateDetail"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create report template",
        "description": "Create a reusable per-jurisdiction, per-report-type goAML header template. Rejected with 400 if any of the template-level obligatory fields (rentity_id, submission_code, currency_code_local) is missing from field_config; an invalid template cannot be saved or used to create a report. Per-filing header fields (action, submission_date, transactions) are enforced at report validate/finalize time instead.",
        "operationId": "create_report_template",
        "tags": [
          "Report Templates"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "jurisdiction",
                  "report_type"
                ],
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "jurisdiction": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code, upper-cased on save."
                  },
                  "report_type": {
                    "type": "string",
                    "enum": [
                      "SAR",
                      "STR",
                      "CTR",
                      "TTR",
                      "UTR",
                      "IFT",
                      "CBR",
                      "TFR"
                    ]
                  },
                  "field_config": {
                    "type": "object",
                    "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\": \"US SAR default\", \"jurisdiction\": \"US\", \"report_type\": \"SAR\", \"field_config\": {\"rentity_id\": \"FIU-US-00123\", \"submission_code\": \"E\", \"currency_code_local\": \"USD\"}}'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    json={\n        'name': 'US SAR default',\n        'jurisdiction': 'US',\n        'report_type': 'SAR',\n        'field_config': {'rentity_id': 'FIU-US-00123', 'submission_code': 'E', 'currency_code_local': 'USD'},\n    },\n    timeout=10,\n)\nresp.raise_for_status()\nprint(resp.json()['uuid'])"
          }
        ],
        "responses": {
          "201": {
            "description": "Template created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportTemplateDetail"
                }
              }
            }
          },
          "400": {
            "description": "Unrecognized jurisdiction, or field_config fails obligatory-field validation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/report-templates/{template_uuid}/": {
      "get": {
        "summary": "Get report template",
        "description": "Retrieve a report template.",
        "operationId": "get_report_template",
        "tags": [
          "Report Templates"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "template_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "7ec00000-0000-4000-8000-000000000006"
            },
            "description": "Report template UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Template detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportTemplateDetail"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update report template",
        "description": "Partially update a report template. Re-validates obligatory goAML fields on save.",
        "operationId": "update_report_template",
        "tags": [
          "Report Templates"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "template_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "7ec00000-0000-4000-8000-000000000006"
            },
            "description": "Report template UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "jurisdiction": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "ISO 3166-1 alpha-2 country code, upper-cased on save."
                  },
                  "report_type": {
                    "type": "string",
                    "enum": [
                      "SAR",
                      "STR",
                      "CTR",
                      "TTR",
                      "UTR",
                      "IFT",
                      "CBR",
                      "TFR"
                    ]
                  },
                  "field_config": {
                    "type": "object",
                    "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template."
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"field_config\": {\"rentity_id\": \"FIU-US-00124\"}}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated template.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportTemplateDetail"
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete report template",
        "description": "Soft-delete a report template. Reports already created from it are unaffected.",
        "operationId": "delete_report_template",
        "tags": [
          "Report Templates"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "template_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "7ec00000-0000-4000-8000-000000000006"
            },
            "description": "Report template UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/report-templates/7ec00000-0000-4000-8000-000000000006/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Template deleted."
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/cases/{case_uuid}/regulatory-reports/": {
      "get": {
        "summary": "List reports for case",
        "description": "List FIU reports generated for the case.",
        "operationId": "list_case_regulatory_reports",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated reports for the case.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows, capped at 100 for performance."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RegulatoryReportDetail"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create report from case",
        "description": "Create a draft FIU report for the case: DRAFT status, auto-populated from the optional template plus the case's subject, linked transactions, and narrative. Validate then finalize to produce the goAML XML and PDF.",
        "operationId": "create_case_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "case_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "c1a5e000-0000-4000-8000-000000000001"
            },
            "description": "Case UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "report_type": {
                    "type": "string",
                    "enum": [
                      "SAR",
                      "STR",
                      "CTR",
                      "TTR",
                      "UTR",
                      "IFT",
                      "CBR",
                      "TFR"
                    ],
                    "description": "Required unless a template is given; the template's report_type is used otherwise."
                  },
                  "jurisdiction": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "Required unless a template is given."
                  },
                  "template": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Report template UUID; its field_config seeds the draft before case-derived data is merged in."
                  },
                  "transactions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "nullable": true,
                    "description": "Transactions to attach; defaults to the case's linked transactions."
                  },
                  "rentity_id": {
                    "type": "string",
                    "nullable": true
                  },
                  "rentity_branch": {
                    "type": "string",
                    "nullable": true
                  },
                  "submission_code": {
                    "type": "string",
                    "enum": [
                      "E",
                      "M"
                    ],
                    "default": "E"
                  },
                  "entity_reference": {
                    "type": "string",
                    "nullable": true
                  },
                  "fiu_ref_number": {
                    "type": "string",
                    "nullable": true
                  },
                  "currency_code_local": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "nullable": true
                  },
                  "submission_date": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "reporting_person": {
                    "type": "object"
                  },
                  "location": {
                    "type": "object"
                  },
                  "reason": {
                    "type": "string",
                    "nullable": true
                  },
                  "action": {
                    "type": "string",
                    "nullable": true
                  },
                  "activity": {
                    "type": "string",
                    "nullable": true
                  },
                  "narrative": {
                    "type": "object",
                    "properties": {
                      "who": {
                        "type": "string"
                      },
                      "what": {
                        "type": "string"
                      },
                      "when": {
                        "type": "string"
                      },
                      "where": {
                        "type": "string"
                      },
                      "why": {
                        "type": "string"
                      },
                      "how": {
                        "type": "string"
                      }
                    }
                  },
                  "report_indicators": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "included_fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/cases/c1a5e000-0000-4000-8000-000000000001/regulatory-reports/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"template\": \"7ec00000-0000-4000-8000-000000000006\"}'"
          }
        ],
        "responses": {
          "201": {
            "description": "Draft report created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegulatoryReportDetail"
                }
              }
            }
          },
          "400": {
            "description": "The referenced template was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/": {
      "get": {
        "summary": "List reports",
        "description": "Application-wide FIU report list, optionally filtered by status, report_type, or case.",
        "operationId": "list_regulatory_reports",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "minimum": 1
            },
            "description": "Page size."
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            },
            "description": "Zero-based offset."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "DRAFT",
                "VALIDATED",
                "FINALIZED"
              ]
            },
            "description": "Filter by status."
          },
          {
            "name": "report_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "SAR",
                "STR",
                "CTR",
                "TTR",
                "UTR",
                "IFT",
                "CBR",
                "TFR"
              ]
            },
            "description": "Filter by report type."
          },
          {
            "name": "case",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Filter to reports on this case."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated reports across the application.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer",
                      "description": "Number of matching rows, capped at 100 for performance."
                    },
                    "next": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RegulatoryReportDetail"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create standalone report",
        "description": "Create a draft FIU report from the standalone Reports page: both case and template are required (unlike the case-scoped create, which can omit template).",
        "operationId": "create_standalone_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "case",
                  "template"
                ],
                "properties": {
                  "case": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "template": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "report_type": {
                    "type": "string",
                    "enum": [
                      "SAR",
                      "STR",
                      "CTR",
                      "TTR",
                      "UTR",
                      "IFT",
                      "CBR",
                      "TFR"
                    ],
                    "description": "Required unless a template is given; the template's report_type is used otherwise."
                  },
                  "jurisdiction": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2,
                    "description": "Required unless a template is given."
                  },
                  "transactions": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "nullable": true,
                    "description": "Transactions to attach; defaults to the case's linked transactions."
                  },
                  "rentity_id": {
                    "type": "string",
                    "nullable": true
                  },
                  "rentity_branch": {
                    "type": "string",
                    "nullable": true
                  },
                  "submission_code": {
                    "type": "string",
                    "enum": [
                      "E",
                      "M"
                    ],
                    "default": "E"
                  },
                  "entity_reference": {
                    "type": "string",
                    "nullable": true
                  },
                  "fiu_ref_number": {
                    "type": "string",
                    "nullable": true
                  },
                  "currency_code_local": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "nullable": true
                  },
                  "submission_date": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "reporting_person": {
                    "type": "object"
                  },
                  "location": {
                    "type": "object"
                  },
                  "reason": {
                    "type": "string",
                    "nullable": true
                  },
                  "action": {
                    "type": "string",
                    "nullable": true
                  },
                  "activity": {
                    "type": "string",
                    "nullable": true
                  },
                  "narrative": {
                    "type": "object",
                    "properties": {
                      "who": {
                        "type": "string"
                      },
                      "what": {
                        "type": "string"
                      },
                      "when": {
                        "type": "string"
                      },
                      "where": {
                        "type": "string"
                      },
                      "why": {
                        "type": "string"
                      },
                      "how": {
                        "type": "string"
                      }
                    }
                  },
                  "report_indicators": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "included_fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"case\": \"c1a5e000-0000-4000-8000-000000000001\", \"template\": \"7ec00000-0000-4000-8000-000000000006\"}'"
          }
        ],
        "responses": {
          "201": {
            "description": "Draft report created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegulatoryReportDetail"
                }
              }
            }
          },
          "400": {
            "description": "The referenced template was not found.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/": {
      "get": {
        "summary": "Get report",
        "description": "Retrieve a regulatory report.",
        "operationId": "get_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Report detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegulatoryReportDetail"
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update report",
        "description": "Edit a draft or validated report's fields. Finalized reports cannot be edited (409).",
        "operationId": "update_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "jurisdiction": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 2
                  },
                  "rentity_id": {
                    "type": "string",
                    "nullable": true
                  },
                  "rentity_branch": {
                    "type": "string",
                    "nullable": true
                  },
                  "submission_code": {
                    "type": "string",
                    "enum": [
                      "E",
                      "M"
                    ],
                    "default": "E"
                  },
                  "entity_reference": {
                    "type": "string",
                    "nullable": true
                  },
                  "fiu_ref_number": {
                    "type": "string",
                    "nullable": true
                  },
                  "currency_code_local": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 3,
                    "nullable": true
                  },
                  "submission_date": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "reporting_person": {
                    "type": "object"
                  },
                  "location": {
                    "type": "object"
                  },
                  "reason": {
                    "type": "string",
                    "nullable": true
                  },
                  "action": {
                    "type": "string",
                    "nullable": true
                  },
                  "activity": {
                    "type": "string",
                    "nullable": true
                  },
                  "narrative": {
                    "type": "object",
                    "properties": {
                      "who": {
                        "type": "string"
                      },
                      "what": {
                        "type": "string"
                      },
                      "when": {
                        "type": "string"
                      },
                      "where": {
                        "type": "string"
                      },
                      "why": {
                        "type": "string"
                      },
                      "how": {
                        "type": "string"
                      }
                    }
                  },
                  "report_indicators": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "included_fields": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X PATCH 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n  -H 'x-api-key: YOUR_API_KEY' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"rentity_id\": \"FIU-US-00123\", \"currency_code_local\": \"USD\"}'"
          }
        ],
        "responses": {
          "200": {
            "description": "Updated report.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegulatoryReportDetail"
                }
              }
            }
          },
          "409": {
            "description": "Report is already finalized.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete report",
        "description": "Soft-delete a draft or validated report. Finalized reports cannot be deleted (409).",
        "operationId": "delete_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X DELETE 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "204": {
            "description": "Report deleted."
          },
          "409": {
            "description": "Report is already finalized.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/validate/": {
      "post": {
        "summary": "Validate report",
        "description": "Check the report against the obligatory goAML header fields (reporting entity ID, submission code, local currency, action taken, submission date), plus at least one attached transaction and a 2-letter jurisdiction, without generating any files. Also stores the result on the report: validation_errors and validated_at are updated, and a non-finalized report's status moves to VALIDATED (no errors) or back to DRAFT (errors).",
        "operationId": "validate_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/validate/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Validation result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "DRAFT",
                        "VALIDATED",
                        "FINALIZED"
                      ]
                    },
                    "is_valid": {
                      "type": "boolean"
                    },
                    "errors": {
                      "type": "array",
                      "description": "Empty when valid.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/finalize/": {
      "post": {
        "summary": "Finalize report",
        "description": "Re-validate, then render the goAML XML and PDF and upload both to S3. On SAR/STR/TFR reports, this also moves the case's transaction alerts from Pending SAR to SAR filed.",
        "operationId": "finalize_regulatory_report",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X POST 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          },
          {
            "lang": "python",
            "label": "Python",
            "source": "import os\n\nimport requests\n\nresp = requests.post(\n    'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/finalize/',\n    headers={'x-api-key': os.environ['DIDIT_API_KEY']},\n    timeout=30,\n)\nif resp.status_code == 400:\n    print('Validation failed:', resp.json()['errors'])\nelse:\n    resp.raise_for_status()\n    report = resp.json()\n    print(report['status'], report['report_number'])"
          }
        ],
        "responses": {
          "200": {
            "description": "Report finalized; xml_s3_key and pdf_s3_key are set.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegulatoryReportDetail"
                }
              }
            }
          },
          "400": {
            "description": "Report failed validation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field": {
                            "type": "string"
                          },
                          "message": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Report is not in a finalizable state.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v3/organization/{organization_id}/application/{application_id}/regulatory-reports/{report_uuid}/download-url/": {
      "get": {
        "summary": "Get report download URL",
        "description": "Mint a presigned URL for a finalized report's goAML XML or PDF.",
        "operationId": "get_regulatory_report_download_url",
        "tags": [
          "Regulatory Reports"
        ],
        "parameters": [
          {
            "name": "organization_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Organization UUID."
          },
          {
            "name": "application_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Application UUID."
          },
          {
            "name": "report_uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid",
              "example": "5ec00000-0000-4000-8000-000000000007"
            },
            "description": "Report UUID."
          },
          {
            "name": "file",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pdf",
                "xml"
              ],
              "default": "pdf"
            },
            "description": "Which file to download."
          }
        ],
        "x-codeSamples": [
          {
            "lang": "curl",
            "label": "curl",
            "source": "curl -X GET 'https://verification.didit.me/v3/organization/11111111-2222-3333-4444-555555555555/application/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/regulatory-reports/5ec00000-0000-4000-8000-000000000007/download-url/?file=xml' \\\n  -H 'x-api-key: YOUR_API_KEY'"
          }
        ],
        "responses": {
          "200": {
            "description": "Presigned download URL.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "download_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "file": {
                      "type": "string",
                      "enum": [
                        "pdf",
                        "xml"
                      ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Report is not finalized yet.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "detail": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SessionImportJob": {
        "type": "object",
        "required": [
          "uuid",
          "status",
          "import_type",
          "source_format",
          "source_filename",
          "total_rows",
          "processed_rows",
          "created_count",
          "updated_count",
          "skipped_count",
          "failed_count"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Import job id. Use this value to poll progress and fetch row errors."
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "PROCESSING",
              "COMPLETED",
              "FAILED",
              "PAUSED",
              "CANCELLED"
            ]
          },
          "import_type": {
            "type": "string",
            "enum": [
              "user_verification",
              "business_verification",
              "status_rules",
              "transactions"
            ]
          },
          "source_format": {
            "type": "string",
            "enum": [
              "csv",
              "ndjson"
            ]
          },
          "source_filename": {
            "type": "string"
          },
          "total_rows": {
            "type": "integer",
            "format": "int64"
          },
          "processed_rows": {
            "type": "integer",
            "format": "int64"
          },
          "checkpoint_row": {
            "type": "integer",
            "format": "int64"
          },
          "created_count": {
            "type": "integer",
            "format": "int64"
          },
          "updated_count": {
            "type": "integer",
            "format": "int64"
          },
          "skipped_count": {
            "type": "integer",
            "format": "int64"
          },
          "failed_count": {
            "type": "integer",
            "format": "int64"
          },
          "total_media_bytes": {
            "type": "integer",
            "format": "int64"
          },
          "last_error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SessionImportError": {
        "type": "object",
        "required": [
          "uuid",
          "row_number",
          "error",
          "raw_row"
        ],
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "row_number": {
            "type": "integer",
            "format": "int64"
          },
          "external_id": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string"
          },
          "raw_row": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BrandingCustomizationUpdate": {
        "type": "object",
        "description": "Partial branding update body. Provide only the fields you want to change. Hex colors accept `#RRGGBB` or `#RRGGBBAA`. Custom domain and white-label email are not accepted here.",
        "properties": {
          "color_primary": {
            "type": "string",
            "description": "Headers / primary text color."
          },
          "color_secondary": {
            "type": "string",
            "description": "Paragraph / secondary text color."
          },
          "color_background": {
            "type": "string",
            "description": "Page background color."
          },
          "color_panel": {
            "type": "string",
            "description": "Panel / accent color."
          },
          "color_panel_10": {
            "type": "string",
            "description": "Panel color at 10% opacity."
          },
          "color_on_panel_1": {
            "type": "string",
            "description": "Support text color on panels."
          },
          "color_on_panel_2": {
            "type": "string",
            "description": "Pill / badge color on panels."
          },
          "color_on_background": {
            "type": "string",
            "description": "Text color on background."
          },
          "color_button_1": {
            "type": "string",
            "description": "Primary button color."
          },
          "color_button_2": {
            "type": "string",
            "description": "Secondary button color."
          },
          "color_button_text_1": {
            "type": "string",
            "description": "Primary button text color."
          },
          "color_button_text_2": {
            "type": "string",
            "description": "Secondary button text color."
          },
          "color_pill_text": {
            "type": "string",
            "description": "Text color for pills, inputs and selectors."
          },
          "border_radius_panel": {
            "type": "integer",
            "minimum": 0,
            "description": "Panel corner radius in pixels."
          },
          "border_radius_buttons": {
            "type": "integer",
            "minimum": 0,
            "description": "Button corner radius in pixels."
          },
          "font_family": {
            "type": "string",
            "description": "Google Font family name (e.g. `Inter`, `AR One Sans`)."
          },
          "font_weight": {
            "type": "string",
            "description": "Font weight available for the selected family (e.g. `regular`, `500`, `700`)."
          },
          "app_public_name": {
            "type": "string",
            "nullable": true,
            "description": "Public name shown in the verification flow."
          },
          "privacy_policy_url": {
            "type": "string",
            "nullable": true,
            "description": "Privacy policy URL shown to the end user."
          },
          "skip_welcome_screen": {
            "type": "boolean",
            "description": "Skip the initial welcome screen."
          },
          "disable_login_with_didit": {
            "type": "boolean",
            "description": "Continue as guest instead of showing the login-with-Didit screen. Cannot be set back to `false` while a custom domain is configured (400)."
          },
          "hide_progress_bar": {
            "type": "boolean",
            "description": "Hide the progress bar during the flow."
          },
          "callback_seconds": {
            "type": "integer",
            "minimum": 0,
            "description": "Delay (seconds) before redirecting to the callback URL."
          },
          "delete_logo_square": {
            "type": "boolean",
            "description": "When `true`, removes the current square logo."
          },
          "delete_logo_rectangular": {
            "type": "boolean",
            "description": "When `true`, removes the current rectangular logo."
          },
          "delete_favicon": {
            "type": "boolean",
            "description": "When `true`, removes the current favicon."
          }
        }
      },
      "BrandingCustomization": {
        "type": "object",
        "description": "White-label branding for an application: colors, fonts, radii, logo URLs and UI/UX options. Logo URLs are read-only (set them by uploading images). Custom domain and white-label email are not included.",
        "properties": {
          "color_primary": {
            "type": "string"
          },
          "color_secondary": {
            "type": "string"
          },
          "color_background": {
            "type": "string"
          },
          "color_panel": {
            "type": "string"
          },
          "color_panel_10": {
            "type": "string"
          },
          "color_on_panel_1": {
            "type": "string"
          },
          "color_on_panel_2": {
            "type": "string"
          },
          "color_on_background": {
            "type": "string"
          },
          "color_button_1": {
            "type": "string"
          },
          "color_button_2": {
            "type": "string"
          },
          "color_button_text_1": {
            "type": "string"
          },
          "color_button_text_2": {
            "type": "string"
          },
          "color_pill_text": {
            "type": "string"
          },
          "border_radius_panel": {
            "type": "integer"
          },
          "border_radius_buttons": {
            "type": "integer"
          },
          "font_family": {
            "type": "string"
          },
          "font_weight": {
            "type": "string"
          },
          "font_url": {
            "type": "string",
            "nullable": true,
            "description": "Read-only. Resolved Google Fonts stylesheet URL for the selected family."
          },
          "logo_square": {
            "type": "string",
            "nullable": true,
            "description": "Read-only URL of the square logo. Upload via `image_square`."
          },
          "logo_rectangular": {
            "type": "string",
            "nullable": true,
            "description": "Read-only URL of the rectangular logo. Upload via `image_rectangular`."
          },
          "favicon": {
            "type": "string",
            "nullable": true,
            "description": "Read-only URL of the favicon. Upload via `image_favicon`."
          },
          "app_public_name": {
            "type": "string",
            "nullable": true
          },
          "privacy_policy_url": {
            "type": "string",
            "nullable": true
          },
          "skip_welcome_screen": {
            "type": "boolean"
          },
          "disable_login_with_didit": {
            "type": "boolean"
          },
          "hide_progress_bar": {
            "type": "boolean"
          },
          "callback_seconds": {
            "type": "integer"
          }
        }
      },
      "WebhookEvent": {
        "type": "string",
        "description": "Supported webhook event name. Use these exact strings in `subscribed_events`; unsupported values are rejected.",
        "enum": [
          "status.updated",
          "data.updated",
          "user.status.updated",
          "user.data.updated",
          "business.status.updated",
          "business.data.updated",
          "activity.created",
          "transaction.created",
          "transaction.status.updated",
          "travel_rule.status.updated"
        ]
      },
      "WebhookSubscribedEvents": {
        "type": "array",
        "description": "Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit `[]` is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged.",
        "minItems": 1,
        "uniqueItems": true,
        "items": {
          "$ref": "#/components/schemas/WebhookEvent"
        },
        "example": [
          "status.updated",
          "data.updated"
        ]
      },
      "PhoneVerificationSendResponse": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/phone/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`."
          },
          "status": {
            "type": "string",
            "enum": [
              "Success",
              "Retry",
              "Blocked"
            ],
            "description": "`Success` — OTP sent to a new verification. `Retry` — OTP re-sent for the pending verification created by a previous send. `Blocked` — the anti-fraud layer refused to send; the verification is immediately finalized as `Declined` and nothing is billed."
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "description": "Why a send was `Blocked` (e.g., `repeated_attempts`, `suspicious`, `spam`). `null` on `Success` and `Retry`."
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Echo of the `vendor_data` stored on the session (from the first send)."
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Echo of the `metadata` stored on the session (from the first send)."
          }
        }
      },
      "PhoneVerificationCheckResponse": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/phone/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later."
          },
          "status": {
            "type": "string",
            "enum": [
              "Approved",
              "Declined",
              "Failed",
              "Expired or Not Found"
            ],
            "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this number in the last 5 minutes."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code."
          },
          "phone": {
            "type": "object",
            "nullable": true,
            "description": "Full phone report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "Approved",
                  "Declined",
                  "In Review",
                  "Not Finished",
                  "Expired"
                ],
                "description": "Final status of the phone verification. Matches the top-level `status` for finalized checks."
              },
              "phone_number_prefix": {
                "type": "string",
                "description": "Country calling code with leading `+` (e.g., `+1`).",
                "example": "+1"
              },
              "phone_number": {
                "type": "string",
                "description": "National number without the country calling code.",
                "example": "4155552671"
              },
              "full_number": {
                "type": "string",
                "description": "Complete number in E.164 format.",
                "example": "+14155552671"
              },
              "country_code": {
                "type": "string",
                "nullable": true,
                "description": "ISO 3166-1 alpha-2 country of the number.",
                "example": "US"
              },
              "country_name": {
                "type": "string",
                "description": "Full country name derived from `country_code`.",
                "example": "United States"
              },
              "carrier": {
                "type": "object",
                "description": "Current network serving the number, from the carrier lookup run at finalization.",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Carrier name. Empty string when the lookup could not identify it."
                  },
                  "type": {
                    "type": "string",
                    "description": "Line type reported by the lookup: `mobile`, `fixed_line`, `voip`, `isp`, `vpn`, `toll_free`, `premium_rate`, `pager`, `payphone`, `satellite`, `service`, `shared_cost`, `short_codes_commercial`, `calling_cards`, `local_rate`, `universal_access`, `voice_mail`, `other`, or `unknown`."
                  }
                }
              },
              "is_disposable": {
                "type": "boolean",
                "description": "`true` when the number belongs to a temporary/burner number service."
              },
              "is_virtual": {
                "type": "boolean",
                "description": "`true` when the line type is virtual (`voip`, `isp`, or `vpn`)."
              },
              "verification_method": {
                "type": "string",
                "description": "Channel that actually delivered the OTP once delivery is confirmed (e.g., `whatsapp`, `sms`); until then, the requested channel."
              },
              "verification_attempts": {
                "type": "integer",
                "description": "Number of OTP sends for this verification (1, or 2 after a retry)."
              },
              "verified_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the correct code was entered. `null` if the verification was never approved."
              },
              "warnings": {
                "type": "array",
                "description": "Risk warnings recorded during the verification (duplicate, disposable, VoIP, attempts exceeded, blocklist, etc.). Empty array when there are none.",
                "items": {
                  "type": "object",
                  "properties": {
                    "feature": {
                      "type": "string",
                      "description": "Feature that produced the warning. Always `PHONE` here."
                    },
                    "risk": {
                      "type": "string",
                      "description": "Machine-readable risk code."
                    },
                    "additional_data": {
                      "type": "object",
                      "nullable": true,
                      "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data."
                    },
                    "log_type": {
                      "type": "string",
                      "enum": [
                        "error",
                        "warning",
                        "information"
                      ],
                      "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status."
                    },
                    "short_description": {
                      "type": "string",
                      "description": "Human-readable one-line summary of the risk."
                    },
                    "long_description": {
                      "type": "string",
                      "description": "Human-readable explanation of the risk."
                    }
                  }
                }
              },
              "lifecycle": {
                "type": "array",
                "description": "Chronological audit trail of the verification. Event `type` is one of `PHONE_VERIFICATION_MESSAGE_SENT`, `PHONE_VERIFICATION_RETRY_MESSAGE_SENT`, `PHONE_VERIFICATION_BLOCKED`, `PHONE_DELIVERY_DELIVERED`, `PHONE_DELIVERY_UNDELIVERABLE`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `PHONE_VERIFICATION_APPROVED`, `PHONE_VERIFICATION_DECLINED`, `PHONE_VERIFICATION_IN_REVIEW`, `PHONE_VERIFICATION_EXPIRED`.",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "Event type."
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the event happened."
                    },
                    "details": {
                      "type": "object",
                      "nullable": true,
                      "description": "Event-specific payload. Send events: `{status, reason, channel, actual_channel}` (`actual_channel` is filled once delivery is confirmed). Delivery events: `{channel, status}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review."
                    },
                    "fee": {
                      "type": "number",
                      "description": "USD amount billed for this event. Non-zero only on send events (the delivery price); 0 for code entries, delivery confirmations, and status events."
                    }
                  }
                }
              },
              "matches": {
                "type": "array",
                "description": "Other sessions of your application (KYC, KYB, or API) where the same phone number was used by a different user (different `vendor_data`). Up to 5 entries, a synthetic list-entry blocklist match first when present (session-derived matches keep query order). Empty array when there are no matches.",
                "items": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Session id of the other verification that used this phone number."
                    },
                    "session_number": {
                      "type": "integer",
                      "description": "Human-friendly sequential number of that session."
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "`vendor_data` of that session."
                    },
                    "verification_date": {
                      "type": "string",
                      "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)."
                    },
                    "phone_number": {
                      "type": "string",
                      "description": "The matched phone number."
                    },
                    "status": {
                      "type": "string",
                      "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)."
                    },
                    "is_blocklisted": {
                      "type": "boolean",
                      "description": "Whether the phone number is on your application's blocklist."
                    },
                    "api_service": {
                      "type": "string",
                      "nullable": true,
                      "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions."
                    },
                    "source": {
                      "type": "string",
                      "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`."
                    }
                  }
                }
              }
            }
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`."
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of this check response."
          }
        }
      },
      "EmailVerificationSendResponse": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "Session id of the verification. A `Retry` send returns the same `request_id` as the original send. This id appears in the Business Console, is returned again by a finalized `POST /v3/email/check/`, and can be passed to `GET /v3/session/{sessionId}/decision/`."
          },
          "status": {
            "type": "string",
            "enum": [
              "Success",
              "Retry",
              "Undeliverable"
            ],
            "description": "`Success` — OTP emailed to a new verification (billed). `Retry` — fresh OTP emailed for the pending verification created by a previous send (free). `Undeliverable` — the address failed deliverability validation or the message could not be sent; the verification is immediately finalized as `Declined` and nothing is billed."
          },
          "reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "email_can_not_be_delivered",
              null
            ],
            "description": "`email_can_not_be_delivered` when `status` is `Undeliverable`; `null` otherwise."
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Echo of the `vendor_data` stored on the session (from the first send)."
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Echo of the `metadata` stored on the session (from the first send)."
          }
        }
      },
      "EmailVerificationCheckResponse": {
        "type": "object",
        "properties": {
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "On `Approved`/`Declined`: the session id of the matched verification — identical to the `request_id` returned by `POST /v3/email/send/`. On `Failed` and `Expired or Not Found`: a random one-off UUID that cannot be looked up later."
          },
          "status": {
            "type": "string",
            "enum": [
              "Approved",
              "Declined",
              "Failed",
              "Expired or Not Found"
            ],
            "description": "`Approved` — correct code, no declining risk. `Declined` — terminal: a declining risk matched or the attempt budget (3) was exhausted. `Failed` — wrong code, attempts remaining. `Expired or Not Found` — no pending verification for this address in the last 5 minutes."
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation of the outcome, including the number of attempts remaining after a wrong code."
          },
          "email": {
            "type": "object",
            "nullable": true,
            "description": "Full email report. Present (non-null) only on finalized outcomes (`Approved`/`Declined`); `null` on `Failed` and absent on `Expired or Not Found`.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "Approved",
                  "Declined",
                  "In Review",
                  "Not Finished",
                  "Expired"
                ],
                "description": "Final status of the email verification. Matches the top-level `status` for finalized checks."
              },
              "email": {
                "type": "string",
                "format": "email",
                "description": "The verified email address.",
                "example": "alice@example.com"
              },
              "is_breached": {
                "type": "boolean",
                "description": "`true` when the address appears in known data breaches (breach intelligence lookup run at finalization)."
              },
              "breaches": {
                "type": "array",
                "description": "Up to 5 most recent known breaches containing this address. Empty array (or omitted) when `is_breached` is `false`.",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Breach identifier."
                    },
                    "domain": {
                      "type": "string",
                      "description": "Domain of the breached service."
                    },
                    "breach_date": {
                      "type": "string",
                      "format": "date",
                      "description": "Date the breach occurred."
                    },
                    "breach_emails_count": {
                      "type": "integer",
                      "description": "Total number of accounts exposed in the breach."
                    },
                    "description": {
                      "type": "string",
                      "description": "Human-readable description of the breach (may contain HTML markup)."
                    },
                    "logo_path": {
                      "type": "string",
                      "description": "URL of the breached service's logo."
                    },
                    "data_classes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Categories of data exposed, in snake_case (e.g., `email_addresses`, `passwords`)."
                    },
                    "is_verified": {
                      "type": "boolean",
                      "description": "Whether the breach has been verified as legitimate."
                    }
                  }
                }
              },
              "is_disposable": {
                "type": "boolean",
                "description": "`true` when the domain belongs to a known disposable/temporary-mail provider."
              },
              "is_undeliverable": {
                "type": "boolean",
                "description": "`true` when the address cannot receive mail (failed syntax or DNS/MX validation, or the message bounced)."
              },
              "verification_attempts": {
                "type": "integer",
                "description": "Number of OTP sends for this verification (1, or 2 after a retry)."
              },
              "verified_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the correct code was entered. `null` if the verification was never approved."
              },
              "warnings": {
                "type": "array",
                "description": "Risk warnings recorded during the verification (duplicate, breached, disposable, undeliverable, attempts exceeded, blocklist, etc.). Empty array when there are none.",
                "items": {
                  "type": "object",
                  "properties": {
                    "feature": {
                      "type": "string",
                      "description": "Feature that produced the warning. Always `EMAIL` here."
                    },
                    "risk": {
                      "type": "string",
                      "description": "Machine-readable risk code."
                    },
                    "additional_data": {
                      "type": "object",
                      "nullable": true,
                      "description": "Risk-specific context (e.g., the duplicated session id for duplicate risks). `null` when the risk carries no extra data."
                    },
                    "log_type": {
                      "type": "string",
                      "enum": [
                        "error",
                        "warning",
                        "information"
                      ],
                      "description": "Severity. `error` risks decline the verification, `warning` flags it for review, `information` is recorded without affecting the status."
                    },
                    "short_description": {
                      "type": "string",
                      "description": "Human-readable one-line summary of the risk."
                    },
                    "long_description": {
                      "type": "string",
                      "description": "Human-readable explanation of the risk."
                    }
                  }
                }
              },
              "lifecycle": {
                "type": "array",
                "description": "Chronological audit trail of the verification. Event `type` is one of `EMAIL_VERIFICATION_MESSAGE_SENT`, `EMAIL_VERIFICATION_RETRY_MESSAGE_SENT`, `VALID_CODE_ENTERED`, `INVALID_CODE_ENTERED`, `EMAIL_VERIFICATION_APPROVED`, `EMAIL_VERIFICATION_DECLINED`, `EMAIL_VERIFICATION_IN_REVIEW`, `EMAIL_VERIFICATION_EXPIRED`.",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string",
                      "description": "Event type."
                    },
                    "timestamp": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When the event happened."
                    },
                    "details": {
                      "type": "object",
                      "nullable": true,
                      "description": "Event-specific payload. Send events: `{status, reason}`. Code-entry events: `{code_tried, status}`. Final-status events: `null`, or `{reason}` when declined or in review."
                    },
                    "fee": {
                      "type": "number",
                      "description": "USD amount billed for this event. Non-zero only on the first successful send; 0 for retries, code entries, and status events."
                    }
                  }
                }
              },
              "matches": {
                "type": "array",
                "description": "Other sessions of your application (KYC, KYB, or API) where the same email address was used by a different user (different `vendor_data`). Up to 5 entries; a synthetic list-entry blocklist match is prepended when present (and only when no session-derived blocklisted email exists) — session-derived matches keep created_at order. Empty array when there are no matches.",
                "items": {
                  "type": "object",
                  "properties": {
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "Session id of the other verification that used this email address."
                    },
                    "session_number": {
                      "type": "integer",
                      "description": "Human-friendly sequential number of that session."
                    },
                    "vendor_data": {
                      "type": "string",
                      "nullable": true,
                      "description": "`vendor_data` of that session."
                    },
                    "verification_date": {
                      "type": "string",
                      "description": "When that session was created (`YYYY-MM-DDTHH:MM:SSZ`)."
                    },
                    "email": {
                      "type": "string",
                      "description": "The matched email address."
                    },
                    "status": {
                      "type": "string",
                      "description": "Status of that session (e.g., `Approved`, `Declined`, `In Progress`)."
                    },
                    "is_blocklisted": {
                      "type": "boolean",
                      "description": "Whether the email address is on your application's blocklist."
                    },
                    "api_service": {
                      "type": "string",
                      "nullable": true,
                      "description": "For API-type sessions, the standalone API that created it (e.g., `PHONE_VERIFICATION`); `null` for regular verification sessions."
                    },
                    "source": {
                      "type": "string",
                      "description": "Where the match was found. Usually `session`; a blocklist hit via an organization list entry prepends a synthetic match with `source: \"list_entry\"` and null `session_id`/`session_number`."
                    }
                  }
                }
              }
            }
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "`vendor_data` of the matched verification's session. `null` on `Expired or Not Found`."
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "`metadata` of the matched verification's session. `null` on `Expired or Not Found`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Timestamp of this check response."
          }
        }
      },
      "WorkflowListItem": {
        "type": "object",
        "description": "One workflow version in the list view.",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Per-version UUID. Use as `{settings_uuid}` for get/update/delete, and as `workflow_id` when creating sessions."
          },
          "workflow_id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable group ID shared by every version of the same workflow. Equals `uuid` for version 1."
          },
          "workflow_label": {
            "type": "string",
            "description": "Display name for the workflow."
          },
          "workflow_type": {
            "type": "string",
            "nullable": true,
            "description": "Base type for simple workflows: `kyc`, `kyb`, `adaptive_age_verification`, `biometric_authentication`, etc. `null` for graph-based workflows without a KYB feature."
          },
          "is_default": {
            "type": "boolean",
            "description": "Whether this is the default workflow for new sessions."
          },
          "is_archived": {
            "type": "boolean"
          },
          "is_white_label_enabled": {
            "type": "boolean",
            "description": "Whether white-label customization applies to sessions created with this workflow."
          },
          "total_price": {
            "type": "number",
            "description": "Fixed price per verification in USD (JSON number, excludes variable-priced features such as DATABASE_VALIDATION and PHONE_VERIFICATION)."
          },
          "min_price": {
            "type": "number",
            "description": "Cheapest fixed price across workflow-graph paths, in USD."
          },
          "max_price": {
            "type": "number",
            "description": "Most expensive fixed price across workflow-graph paths, in USD."
          },
          "features": {
            "type": "string",
            "description": "Enabled features as a single `\" + \"`-joined string, e.g. `\"OCR + LIVENESS + FACE_MATCH\"`. Split on `\" + \"` to get a list."
          },
          "is_simple_workflow": {
            "type": "boolean",
            "description": "True when the workflow has no custom workflow graph."
          },
          "is_editable": {
            "type": "boolean",
            "description": "True when this version is a `draft` (drafts are editable in place; published versions are immutable in the Console)."
          },
          "workflow_url": {
            "type": "string",
            "format": "uri",
            "description": "Public shareable verification URL for this workflow (`https://verify.didit.me/u/<token>`)."
          },
          "max_retry_attempts": {
            "type": "integer",
            "description": "Maximum retry attempts allowed after a declined verification (0-10)."
          },
          "retry_window_days": {
            "type": "integer",
            "nullable": true,
            "description": "Rolling window in days used to count retries (1-365). `null` = all-time limit."
          },
          "session_expiration_time": {
            "type": "integer",
            "description": "Seconds before an unfinished session expires (3600-2419200)."
          },
          "version": {
            "type": "integer",
            "description": "Version number within the `workflow_id` group, starting at 1."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "description": "Version status. New sessions use the latest `published` version of a group."
          },
          "has_draft": {
            "type": "boolean",
            "description": "True when any version in this `workflow_id` group is currently a draft."
          },
          "created_by": {
            "type": "object",
            "nullable": true,
            "description": "Who created this version: `{name, email, type}` where `type` is `user` (Console user, `email` set) or `api_key` (`name` is the client id, `email` is null). `null` when no creator was recorded.",
            "properties": {
              "name": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "nullable": true
              },
              "type": {
                "type": "string",
                "enum": [
                  "user",
                  "api_key"
                ]
              }
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SimpleWorkflowRequest": {
        "type": "object",
        "description": "Create a simple linear workflow. The API converts this feature list into a node-based workflow graph internally. STRICT field whitelist: any key not listed here (including `workflow_type`) is rejected with a 400.",
        "required": [
          "features"
        ],
        "properties": {
          "workflow_label": {
            "type": "string",
            "maxLength": 50,
            "default": "ID Verification",
            "description": "Display name for the workflow (max 50 characters). Defaults to `\"ID Verification\"` when omitted — always set it so workflows stay distinguishable.",
            "example": "Standard KYC"
          },
          "is_default": {
            "type": "boolean",
            "description": "Set this workflow as the default for new sessions."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "description": "Omit this field to create a published workflow ready for sessions. Use `draft` only when you want to save without publishing."
          },
          "features": {
            "type": "array",
            "minItems": 1,
            "description": "Verification features in execution order. The API links each item to the next one and adds one final status node automatically.",
            "items": {
              "$ref": "#/components/schemas/SimpleWorkflowFeature"
            }
          },
          "is_white_label_enabled": {
            "type": "boolean",
            "description": "Enable white-label customization for sessions created with this workflow.",
            "default": false
          },
          "is_desktop_allowed": {
            "type": "boolean",
            "description": "Allow the verification flow to run on desktop browsers.",
            "default": false
          },
          "max_retry_attempts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "default": 3,
            "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline."
          },
          "retry_window_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "nullable": true,
            "default": 7,
            "description": "Rolling window in days used to count retries. `null` enforces an all-time limit."
          },
          "face_liveness_max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3,
            "default": 3,
            "description": "Maximum liveness submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it."
          },
          "face_match_max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3,
            "default": 3,
            "description": "Maximum face-match submissions per session. Default 3: one initial attempt plus two retries. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it."
          },
          "session_expiration_time": {
            "type": "integer",
            "minimum": 3600,
            "maximum": 2419200,
            "default": 604800,
            "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)."
          }
        }
      },
      "SimpleWorkflowUpdateRequest": {
        "type": "object",
        "description": "Update workflow metadata or replace the full linear feature list. STRICT field whitelist: any key not listed here is rejected with a 400.",
        "properties": {
          "workflow_label": {
            "type": "string",
            "maxLength": 50,
            "description": "Display name for the workflow (max 50 characters)."
          },
          "is_default": {
            "type": "boolean",
            "description": "Set this workflow as the default for new sessions."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "description": "Controls versioning. Omit to update the current version in place (drafts auto-publish). `published` on an already-published version creates and returns a NEW version. `draft` is only valid while the version is still a draft."
          },
          "features": {
            "type": "array",
            "minItems": 1,
            "description": "If provided, this replaces the full feature order and configuration.",
            "items": {
              "$ref": "#/components/schemas/SimpleWorkflowFeature"
            }
          },
          "is_white_label_enabled": {
            "type": "boolean",
            "description": "Enable white-label customization for sessions created with this workflow."
          },
          "is_desktop_allowed": {
            "type": "boolean",
            "description": "Allow the verification flow to run on desktop browsers."
          },
          "max_retry_attempts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "description": "Maximum retry attempts allowed after a declined verification. `0` blocks the user after the first decline."
          },
          "retry_window_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "nullable": true,
            "description": "Rolling window in days used to count retries. `null` enforces an all-time limit."
          },
          "face_liveness_max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3,
            "description": "Maximum liveness submissions per session. This value is also copied into generated liveness and age-estimation workflow nodes unless the feature-level config overrides it."
          },
          "face_match_max_attempts": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3,
            "description": "Maximum face-match submissions per session. This value is also copied into generated face-match workflow nodes unless the feature-level config overrides it."
          },
          "session_expiration_time": {
            "type": "integer",
            "minimum": 3600,
            "maximum": 2419200,
            "description": "Seconds before an unfinished session expires. Minimum 1 hour (3600), maximum 4 weeks (2419200)."
          }
        }
      },
      "SimpleWorkflowFeature": {
        "type": "object",
        "required": [
          "feature"
        ],
        "properties": {
          "feature": {
            "type": "string",
            "description": "Feature to run. Use the uppercase values shown here.",
            "enum": [
              "OCR",
              "NFC",
              "LIVENESS",
              "FACE_MATCH",
              "PROOF_OF_ADDRESS",
              "QUESTIONNAIRE",
              "DOCUMENT_AI",
              "PHONE_VERIFICATION",
              "EMAIL_VERIFICATION",
              "DATABASE_VALIDATION",
              "AML",
              "IP_ANALYSIS",
              "AGE_ESTIMATION",
              "KYB_REGISTRY",
              "KYB_DOCUMENTS",
              "KYB_KEY_PEOPLE"
            ]
          },
          "config": {
            "type": "object",
            "description": "Feature-specific settings. For `QUESTIONNAIRE`, include `questionnaire_uuid` with the `questionnaire_id` returned by the questionnaire create endpoint.",
            "additionalProperties": true
          },
          "label": {
            "type": "string",
            "description": "Optional internal label for this workflow node."
          }
        }
      },
      "ReviewItem": {
        "type": "object",
        "description": "One entry in a session's review / activity feed. Status changes carry `previous_status`, `new_status`, `previous_value`, `new_value`, and `changed_fields`; comments carry `comment` and (for Console comments) `mentioned_emails`; the remaining fields are `null` or empty when they do not apply to the activity type.",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of this feed entry.",
            "example": "de0561bc-5583-4409-9171-1a31a4c8a5f4"
          },
          "activity_type": {
            "type": "string",
            "enum": [
              "COMMENT",
              "STATUS_UPDATED",
              "KYC_DATA_UPDATED",
              "POA_DATA_UPDATED",
              "FILE_ADDED",
              "FILE_REMOVED",
              "AML_ONGOING_MATCH",
              "AML_STATUS_UPDATED",
              "AML_HIT_STATUS_UPDATED",
              "KYB_DATA_UPDATED",
              "KYB_DOCUMENT_UPLOADED",
              "TAG_ADDED",
              "TAG_REMOVED"
            ],
            "description": "What kind of activity this entry records. `STATUS_UPDATED` rows come from manual decisions (`PATCH /v3/session/{session_id}/update-status/`) and from entries added via `POST …/reviews/` (which default to this type even when no status is recorded). `COMMENT` rows come from Console reviewers; Console detail-page views are also stored as `COMMENT` rows flagged with `metadata.system_event = \"DETAIL_VIEWED\"`. The remaining types track data edits, file changes, tags, and AML monitoring activity. STATUS_UPDATED rows are also written by system actors (e.g. AML ongoing monitoring with actor_type SYSTEM, and automatic feature-status updates), not only by the update-status endpoint and manual review posts.",
            "example": "STATUS_UPDATED"
          },
          "actor_display": {
            "type": "string",
            "description": "Human-readable author label, resolved in order: actor's full name → Console account identifier → local part of `actor_email` → `\"API Client\"` (for `API_KEY` actors) → `\"System\"` (for `SYSTEM` actors) → `\"Unknown\"`. Entries created via `POST …/reviews/` always display `\"Unknown\"` because that endpoint records no actor attribution.",
            "example": "API Client"
          },
          "actor_email": {
            "type": "string",
            "nullable": true,
            "description": "Email of the Console user who performed the action. `null` for API-key and system actors.",
            "example": null
          },
          "actor_type": {
            "type": "string",
            "enum": [
              "API_KEY",
              "CONSOLE_USER",
              "SYSTEM"
            ],
            "description": "Who performed the action: an API key, a Console user, or Didit itself (`SYSTEM`, e.g. AML ongoing monitoring). Entries created via `POST …/reviews/` carry the default `CONSOLE_USER` regardless of the caller.",
            "example": "API_KEY"
          },
          "new_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "Not Started",
              "In Progress",
              "Approved",
              "Declined",
              "In Review",
              "Expired",
              "Abandoned",
              "Kyc Expired",
              "Resubmitted",
              "Awaiting User"
            ],
            "description": "Session status recorded by this entry (for status updates). `null` for comments and data-change entries.",
            "example": "Approved"
          },
          "previous_status": {
            "type": "string",
            "nullable": true,
            "enum": [
              "Not Started",
              "In Progress",
              "Approved",
              "Declined",
              "In Review",
              "Expired",
              "Abandoned",
              "Kyc Expired",
              "Resubmitted",
              "Awaiting User"
            ],
            "description": "Status the session had before the change. Populated on `STATUS_UPDATED` entries written by the update-status endpoint; `null` on entries added via `POST …/reviews/`. Also populated on system-written STATUS_UPDATED rows.",
            "example": "In Review"
          },
          "comment": {
            "type": "string",
            "nullable": true,
            "description": "Free-text note attached to the entry, or `null`.",
            "example": "Document re-checked manually; address matches."
          },
          "mentioned_emails": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "Console users `@email`-mentioned in the comment. Always empty for entries created via this API — mention processing is a Console-only feature.",
            "example": []
          },
          "previous_value": {
            "type": "object",
            "nullable": true,
            "description": "Snapshot of the changed data before the action, e.g. `{\"status\": \"In Review\"}` on a status change. `null` when nothing was changed.",
            "example": {
              "status": "In Review"
            }
          },
          "new_value": {
            "type": "object",
            "nullable": true,
            "description": "Snapshot of the changed data after the action, e.g. `{\"status\": \"Approved\"}`. `null` when nothing was changed.",
            "example": {
              "status": "Approved"
            }
          },
          "changed_fields": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Names of the fields modified by this activity (e.g. `[\"status\"]`). Empty for comments and for entries added via `POST …/reviews/`.",
            "example": [
              "status"
            ]
          },
          "metadata": {
            "type": "object",
            "nullable": true,
            "description": "Extra context for system-generated entries. Console detail-page views carry `{\"system_event\": \"DETAIL_VIEWED\", \"detail_type\": \"verification\"}` so they can be filtered out of notification logic. `null` for ordinary entries.",
            "example": null
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the entry was written, ISO 8601 UTC.",
            "example": "2026-06-12T00:19:31.918735Z"
          }
        }
      },
      "QuestionnaireListItem": {
        "type": "object",
        "description": "One questionnaire version in the list view.",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Per-version UUID. Use as `{questionnaire_uuid}` for get/update/delete and as `questionnaire_uuid` in a workflow's QUESTIONNAIRE feature config."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "default_language": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "is_simple_questionnaire": {
            "type": "boolean",
            "description": "True for linear questionnaires created through this API; false for graph-based questionnaires built in the Console."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "question_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Uppercase element types of the answerable questions, in creation order (e.g. `SHORT_TEXT`, `MULTIPLE_CHOICE`, `FILE_UPLOAD`). Structural `SECTION_HEADER` and `SEPARATOR` elements are excluded. One entry per question, so values can repeat."
          },
          "workflow_names": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Names of workflows using this questionnaire. Currently always `[]` on this endpoint (populated only in the Console listing)."
          },
          "questionnaire_group_id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable identifier that groups all versions of the same questionnaire."
          },
          "version": {
            "type": "integer",
            "description": "Version number within the group, starting at 1."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When this version was published. `null` for drafts."
          },
          "is_editable": {
            "type": "boolean",
            "description": "True when this version is a `draft`."
          }
        }
      },
      "SimpleQuestionnaireRequest": {
        "type": "object",
        "description": "Create a simple linear questionnaire. The API converts `form_elements` into the internal questionnaire graph.",
        "required": [
          "title",
          "languages",
          "form_elements"
        ],
        "properties": {
          "title": {
            "type": "string",
            "description": "Internal name for the questionnaire."
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "description": "Languages included in every label. Must include `en`.",
            "items": {
              "type": "string"
            },
            "example": [
              "en"
            ]
          },
          "default_language": {
            "type": "string",
            "description": "Default language. Must be included in `languages`.",
            "example": "en"
          },
          "is_active": {
            "type": "boolean",
            "default": true
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "description": "Omit this field to create a published questionnaire ready for workflows."
          },
          "form_elements": {
            "type": "array",
            "minItems": 1,
            "description": "Questions in the exact order users should see them. Branching and conditional fields are not supported.",
            "items": {
              "$ref": "#/components/schemas/SimpleQuestionnaireElement"
            }
          }
        }
      },
      "SimpleQuestionnaireUpdateRequest": {
        "type": "object",
        "description": "Update questionnaire metadata or replace the full linear `form_elements` list.",
        "properties": {
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "default_language": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "form_elements": {
            "type": "array",
            "minItems": 1,
            "description": "If provided, this replaces the full linear question list.",
            "items": {
              "$ref": "#/components/schemas/SimpleQuestionnaireElement"
            }
          }
        }
      },
      "SimpleQuestionnaireElement": {
        "type": "object",
        "required": [
          "element_type",
          "label"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Optional stable question id. If omitted, the API generates `q1`, `q2`, etc. Use lowercase letters, numbers, and underscores for readability.",
            "example": "source_of_funds"
          },
          "element_type": {
            "type": "string",
            "description": "Question type. The API accepts these lowercase values and their uppercase equivalents.",
            "enum": [
              "short_text",
              "long_text",
              "dropdown",
              "single_choice",
              "multiple_choice",
              "number",
              "image",
              "file_upload",
              "time",
              "email",
              "address",
              "phone",
              "country",
              "date_picker",
              "consent",
              "section_header",
              "separator",
              "heading",
              "paragraph"
            ]
          },
          "label": {
            "type": "object",
            "description": "Question label by language code, for example `{ \"en\": \"Source of funds\" }`.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "description": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            }
          },
          "placeholder": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "string"
            }
          },
          "is_required": {
            "type": "boolean",
            "default": false
          },
          "options": {
            "type": "array",
            "description": "Use for `dropdown`, `single_choice`, and `multiple_choice`. If `value` is omitted, the API generates one from the option label.",
            "items": {
              "type": "object",
              "required": [
                "label"
              ],
              "properties": {
                "value": {
                  "type": "string"
                },
                "label": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "max_files": {
            "type": "integer",
            "minimum": 1,
            "maximum": 5,
            "description": "Use with `file_upload` or `image`."
          },
          "validation": {
            "type": "object",
            "nullable": true,
            "description": "Optional format validation for `short_text`, `long_text`, or `number`. Enforced while the user fills the field (they cannot proceed until it passes) and re-checked by the API on submission.",
            "properties": {
              "pattern": {
                "type": "string",
                "description": "Regular expression (ECMA-262 subset) the value must fully match. For `short_text` and `long_text`. Do not add ^/$ anchors; the whole value is matched."
              },
              "min_length": {
                "type": "integer",
                "minimum": 0,
                "description": "Minimum number of characters. For `short_text` and `long_text`."
              },
              "max_length": {
                "type": "integer",
                "minimum": 0,
                "description": "Maximum number of characters. For `short_text` and `long_text`."
              },
              "min": {
                "type": "number",
                "description": "Minimum numeric value. For `number`."
              },
              "max": {
                "type": "number",
                "description": "Maximum numeric value. For `number`."
              },
              "error_message": {
                "type": "object",
                "description": "Translated message shown when validation fails, keyed by language code.",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "QuestionnaireDetail": {
        "type": "object",
        "description": "Full questionnaire detail returned from GET, POST (create) and PATCH (update) endpoints.",
        "properties": {
          "questionnaire_id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique identifier of the questionnaire. Use this id when referencing the questionnaire from a workflow or in subsequent update/delete requests."
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "default_language": {
            "type": "string"
          },
          "is_active": {
            "type": "boolean"
          },
          "is_simple_questionnaire": {
            "type": "boolean"
          },
          "graph": {
            "type": "object",
            "description": "Graph structure with start_node and nodes map."
          },
          "sections": {
            "type": "array",
            "description": "Questionnaire content grouped into sections (derived from the graph).",
            "items": {
              "type": "object"
            }
          },
          "questionnaire_group_id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable identifier that groups all versions of the same questionnaire."
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "is_editable": {
            "type": "boolean",
            "description": "True when the questionnaire can still be edited in place (draft versions)."
          }
        }
      },
      "UserListItem": {
        "type": "object",
        "description": "A verified user.",
        "properties": {
          "didit_internal_id": {
            "type": "string",
            "format": "uuid",
            "description": "Didit's stable internal UUID for this user."
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Your unique identifier for this user (passed when creating sessions). This can be null when no vendor identifier was supplied."
          },
          "display_name": {
            "type": "string",
            "nullable": true,
            "description": "Custom display name set by you"
          },
          "full_name": {
            "type": "string",
            "nullable": true,
            "description": "Full name extracted from verified documents"
          },
          "date_of_birth": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "effective_name": {
            "type": "string",
            "nullable": true,
            "description": "Best available name: display_name if set, otherwise full_name"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "FLAGGED",
              "BLOCKED"
            ],
            "description": "Lifecycle status of the user record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks the user for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`."
          },
          "portrait_image_url": {
            "type": "string",
            "nullable": true,
            "description": "Presigned URL of the user's portrait photo (expires after a few hours)"
          },
          "session_count": {
            "type": "integer",
            "description": "Total number of verification sessions for this user"
          },
          "approved_count": {
            "type": "integer",
            "description": "Number of approved sessions"
          },
          "declined_count": {
            "type": "integer",
            "description": "Number of declined sessions"
          },
          "in_review_count": {
            "type": "integer",
            "description": "Number of sessions in review"
          },
          "issuing_states": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ISO 3166-1 alpha-3 codes of issuing countries seen on this user's approved ID documents, e.g. `[\"USA\", \"ESP\"]`. Empty array when none."
          },
          "approved_emails": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Verified email addresses collected from this user's approved sessions, e.g. `[\"john@example.com\"]`."
          },
          "approved_phones": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Verified phone numbers collected from this user's approved sessions, e.g. `[\"+14155551234\"]`."
          },
          "features": {
            "type": "object",
            "description": "Aggregated per-feature status across all of this user's sessions. Possible keys: `ID_VERIFICATION`, `NFC`, `LIVENESS`, `FACE_MATCH`, `POA`, `QUESTIONNAIRE`, `EMAIL_VERIFICATION`, `PHONE`, `AML`, `IP_ANALYSIS`, `AGE_ESTIMATION`, `DATABASE_VALIDATION`. Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`."
          },
          "features_list": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "feature": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            },
            "description": "Same data as `features`, as an ordered array of `{feature, status}` objects."
          },
          "last_session_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent session"
          },
          "first_session_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the first session"
          },
          "last_activity_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent activity on this user (session, transaction, status change, data edit, etc.)."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "color": {
                  "type": "string"
                }
              }
            },
            "description": "Tags assigned to this user"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BlocklistItem": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "face",
              "document",
              "phone",
              "email"
            ]
          },
          "session_id": {
            "type": "string",
            "format": "uuid"
          },
          "session_number": {
            "type": "integer"
          },
          "blocklisted_at": {
            "type": "string",
            "format": "date-time"
          },
          "face_image": {
            "type": "string",
            "nullable": true
          },
          "document_type": {
            "type": "string",
            "nullable": true
          },
          "document_number": {
            "type": "string",
            "nullable": true
          },
          "phone_number": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "UserDetailItem": {
        "type": "object",
        "description": "Full user detail. Extends UserListItem with metadata, comments, and updated_at.",
        "allOf": [
          {
            "$ref": "#/components/schemas/UserListItem"
          },
          {
            "type": "object",
            "properties": {
              "metadata": {
                "type": "object",
                "description": "Custom metadata JSON you attached to this user. Defaults to `{}`."
              },
              "tags": {
                "type": "array",
                "description": "Tag assignments. NOTE: on detail responses each entry is a tag *link* object (`{uuid, tag: {...}, added_by_email, added_by_name, created_at}`), unlike the flat `{uuid, name, color}` shape used on list responses.",
                "items": {
                  "type": "object",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid",
                      "description": "UUID of the tag assignment (not the tag itself)."
                    },
                    "tag": {
                      "type": "object",
                      "properties": {
                        "uuid": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "type": "string"
                        },
                        "color": {
                          "type": "string"
                        },
                        "description": {
                          "type": "string",
                          "nullable": true
                        },
                        "source": {
                          "type": "string",
                          "enum": [
                            "didit",
                            "custom"
                          ]
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "added_by_email": {
                      "type": "string",
                      "nullable": true
                    },
                    "added_by_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "comments": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "uuid": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "comment_type": {
                      "type": "string",
                      "enum": [
                        "COMMENT",
                        "STATUS_CHANGED",
                        "METADATA_UPDATED",
                        "TAG_ADDED",
                        "TAG_REMOVED",
                        "PROFILE_UPDATED"
                      ]
                    },
                    "comment": {
                      "type": "string",
                      "nullable": true
                    },
                    "actor_email": {
                      "type": "string",
                      "nullable": true
                    },
                    "actor_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "previous_status": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "ACTIVE",
                        "FLAGGED",
                        "BLOCKED",
                        null
                      ]
                    },
                    "new_status": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "ACTIVE",
                        "FLAGGED",
                        "BLOCKED",
                        null
                      ]
                    },
                    "previous_value": {
                      "type": "object",
                      "nullable": true,
                      "description": "Previous field values for PROFILE_UPDATED entries."
                    },
                    "new_value": {
                      "type": "object",
                      "nullable": true,
                      "description": "New field values for PROFILE_UPDATED entries."
                    },
                    "changed_fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Names of the fields changed by a PROFILE_UPDATED entry."
                    },
                    "metadata": {
                      "type": "object",
                      "nullable": true
                    },
                    "mentioned_emails": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "description": "Activity log and comments for this user (status changes, profile edits, manual notes)."
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "BusinessListItem": {
        "type": "object",
        "description": "A verified business.",
        "properties": {
          "didit_internal_id": {
            "type": "string",
            "format": "uuid",
            "description": "Didit's stable internal UUID for this business."
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Your unique identifier for this business (passed when creating sessions). This can be null when no vendor identifier was supplied."
          },
          "display_name": {
            "type": "string",
            "nullable": true,
            "description": "Custom display name set by you"
          },
          "legal_name": {
            "type": "string",
            "nullable": true,
            "description": "Official legal name from registry or manual entry"
          },
          "registration_number": {
            "type": "string",
            "nullable": true,
            "description": "Company registration or incorporation number"
          },
          "country_code": {
            "type": "string",
            "nullable": true,
            "description": "Country of incorporation (ISO 3166-1 alpha-2, e.g. `GB`, `US`)."
          },
          "effective_name": {
            "type": "string",
            "nullable": true,
            "description": "Best available name: display_name if set, otherwise legal_name"
          },
          "status": {
            "type": "string",
            "enum": [
              "ACTIVE",
              "FLAGGED",
              "BLOCKED"
            ],
            "description": "Lifecycle status of the business record (NOT a session status). `ACTIVE` is the default, `FLAGGED` marks it for manual attention, `BLOCKED` prevents new sessions for this `vendor_data`."
          },
          "session_count": {
            "type": "integer",
            "description": "Total number of verification sessions for this business"
          },
          "approved_count": {
            "type": "integer",
            "description": "Number of approved sessions"
          },
          "declined_count": {
            "type": "integer",
            "description": "Number of declined sessions"
          },
          "in_review_count": {
            "type": "integer",
            "description": "Number of sessions in review"
          },
          "features": {
            "type": "object",
            "description": "Aggregated per-feature status across all of this business's KYB sessions. Currently the only key is `KYB` (the registry company check status). Possible values: `Approved`, `Declined`, `In Review`, `Not Finished`, `Resub Requested`."
          },
          "features_list": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "feature": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            },
            "description": "Same data as `features`, as an ordered array of `{feature, status}` objects."
          },
          "last_session_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent session"
          },
          "first_session_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the first session"
          },
          "last_activity_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Timestamp of the most recent activity (status change, session update, etc.)"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "color": {
                  "type": "string"
                }
              }
            },
            "description": "Tags assigned to this business"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BusinessDetailItem": {
        "type": "object",
        "description": "Full business detail. Extends BusinessListItem with metadata and updated_at.",
        "allOf": [
          {
            "$ref": "#/components/schemas/BusinessListItem"
          },
          {
            "type": "object",
            "properties": {
              "metadata": {
                "type": "object",
                "description": "Custom metadata JSON you attached to this business. Defaults to `{}`."
              },
              "updated_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        ]
      },
      "TransactionDetail": {
        "type": "object",
        "description": "Full monitoring record for one transaction, as returned by `GET /v3/transactions/{transaction_id}/` and `POST /v3/transactions/`.",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid",
            "description": "Didit-stable transaction identifier. Use as `{transaction_id}` for follow-up calls."
          },
          "transaction_number": {
            "type": "integer",
            "description": "Application-scoped sequential transaction number shown in Console (e.g. `4123`)."
          },
          "txn_id": {
            "type": "string",
            "description": "The `transaction_id` you supplied at create time (max 128 chars). Unique per application."
          },
          "txn_date": {
            "type": "string",
            "format": "date-time",
            "description": "When the transaction occurred (from `transaction_at`; defaults to submission time)."
          },
          "zone_id": {
            "type": "string",
            "nullable": true,
            "description": "IANA time zone identifier provided at create time."
          },
          "transaction_type": {
            "type": "string",
            "description": "Top-level category as stored: `finance`, `kyc`, `travelRule`, `userPlatformEvent`, `gamblingBet`, `gamblingLimitChange`, `gamblingBonusChange`, `auditTrailEvent`. Note multi-word values are echoed back in camelCase even when submitted in snake_case."
          },
          "action_type": {
            "type": "string",
            "description": "Sub-type within the category (e.g. `deposit`, `withdrawal`, `transfer`). Defaults to the category when not supplied."
          },
          "direction": {
            "type": "string",
            "enum": [
              "INBOUND",
              "OUTBOUND"
            ],
            "description": "Direction relative to the subject. Stored uppercase regardless of the casing submitted (`in`/`out`/`inbound`/`outbound` are accepted on input)."
          },
          "status": {
            "type": "string",
            "enum": [
              "APPROVED",
              "IN_REVIEW",
              "DECLINED",
              "AWAITING_USER"
            ],
            "description": "Current monitoring verdict. Transactions are created `APPROVED`; rules may flip them to `IN_REVIEW`/`DECLINED` synchronously."
          },
          "amount": {
            "type": "string",
            "description": "Transaction amount as a decimal string with trailing zeros stripped (e.g. `\"1500\"`, `\"0.5\"`, `\"0.123456789012345678\"`). Up to 18 decimal places."
          },
          "currency": {
            "type": "string",
            "description": "Currency code of `amount` (e.g. `EUR`, `USD`, `BTC`)."
          },
          "currency_type": {
            "type": "string",
            "nullable": true,
            "description": "`fiat` or `crypto`, as submitted in `currency_kind`."
          },
          "amount_in_default_currency": {
            "type": "string",
            "nullable": true,
            "description": "Pre-converted amount you supplied, as a decimal string with trailing zeros stripped."
          },
          "default_currency_code": {
            "type": "string",
            "nullable": true
          },
          "preferred_currency_amount": {
            "type": "string",
            "nullable": true,
            "description": "`amount` converted to the application's preferred currency, when available."
          },
          "preferred_currency_code": {
            "type": "string",
            "nullable": true
          },
          "payment_details": {
            "type": "string",
            "nullable": true,
            "description": "Free-text payment reference or memo from the submission."
          },
          "payment_txn_id": {
            "type": "string",
            "nullable": true,
            "description": "External payment system reference (e.g. blockchain transaction hash) from `payment_reference_id`."
          },
          "score": {
            "type": "integer",
            "description": "Risk score accumulated by rules (typically 0–100). Higher = riskier."
          },
          "severity": {
            "type": "string",
            "nullable": true,
            "enum": [
              "UNKNOWN",
              "LOW",
              "MEDIUM",
              "HIGH",
              "CRITICAL"
            ],
            "description": "Categorical risk severity set by rules/providers (`UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`). `null` until something sets it."
          },
          "decision_reason_code": {
            "type": "string",
            "nullable": true,
            "description": "Machine-readable reason for the current `status`."
          },
          "decision_reason_label": {
            "type": "string",
            "nullable": true,
            "description": "Human-readable label for `decision_reason_code`."
          },
          "vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Convenience copy of the applicant's `vendor_data`."
          },
          "metadata": {
            "type": "object",
            "description": "Snapshot of the `subject` and `counterparty` payloads as submitted at create time."
          },
          "props": {
            "type": "object",
            "description": "The `custom_properties` you supplied at create time. Each key is addressable in rule conditions as `custom_values.<key>`."
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "color": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "nullable": true
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "didit",
                    "custom"
                  ],
                  "description": "Whether the tag is a Didit preset or a custom tag."
                }
              }
            },
            "description": "Tags attached to the transaction (manually or by rules)."
          },
          "parties": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "APPLICANT",
                    "REMITTER",
                    "BENEFICIARY",
                    "COUNTERPARTY"
                  ],
                  "description": "Party role. The create endpoint assigns `APPLICANT` (the subject) and `COUNTERPARTY`."
                },
                "entity_type": {
                  "type": "string",
                  "enum": [
                    "individual",
                    "company",
                    "external",
                    "unhostedWallet"
                  ]
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "USER",
                    "BUSINESS",
                    "EXTERNAL"
                  ],
                  "description": "`USER`/`BUSINESS` parties link back to a Didit-owned entity via `vendor_data`. `EXTERNAL` parties have no Didit entity behind them."
                },
                "vendor_data": {
                  "type": "string",
                  "nullable": true,
                  "description": "Your internal user/business identifier."
                },
                "full_name": {
                  "type": "string",
                  "nullable": true
                },
                "first_name": {
                  "type": "string",
                  "nullable": true
                },
                "last_name": {
                  "type": "string",
                  "nullable": true
                },
                "country_code": {
                  "type": "string",
                  "nullable": true,
                  "description": "Country code from the submitted `address.country` (typically ISO 3166-1 alpha-3)."
                },
                "dob": {
                  "type": "string",
                  "format": "date",
                  "nullable": true,
                  "description": "Date of birth from the submitted `date_of_birth`."
                },
                "address": {
                  "type": "object",
                  "description": "Address object as submitted. `{}` when omitted."
                },
                "institution_info": {
                  "type": "object",
                  "description": "Institution details as submitted. `{}` when omitted."
                },
                "device": {
                  "type": "object",
                  "description": "Normalized device context, including server-side IP enrichment under `network_context` (geolocation, VPN/data-center flags) when an IP was provided."
                },
                "external_party_snapshot": {
                  "type": "object",
                  "nullable": true,
                  "description": "Frozen copy of the party data for `EXTERNAL` parties (or after the linked entity was deleted). `null` while linked to a live entity."
                }
              }
            },
            "description": "Transaction parties (applicant, counterparty)."
          },
          "payment_methods": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "payment_method_type": {
                  "type": "string",
                  "enum": [
                    "bank_card",
                    "bank_account",
                    "e_wallet",
                    "crypto_wallet",
                    "unhosted_wallet",
                    "other"
                  ]
                },
                "label": {
                  "type": "string",
                  "nullable": true
                },
                "fingerprint": {
                  "type": "string",
                  "nullable": true,
                  "description": "SHA-256 over `type:account_id:issuing_country` — stable identifier to correlate reuse of the same instrument."
                },
                "account_id": {
                  "type": "string",
                  "nullable": true,
                  "description": "Account identifier (IBAN, wallet address, card token, …)."
                },
                "issuing_country": {
                  "type": "string",
                  "nullable": true
                },
                "owner_kind": {
                  "type": "string",
                  "enum": [
                    "USER",
                    "BUSINESS",
                    "EXTERNAL"
                  ]
                },
                "vendor_data": {
                  "type": "string",
                  "nullable": true,
                  "description": "Your identifier for the owning user/business."
                },
                "owner_name": {
                  "type": "string",
                  "nullable": true
                },
                "institution_info": {
                  "type": "object"
                },
                "details": {
                  "type": "object",
                  "description": "Raw `payment_method` object as submitted."
                },
                "external_owner_snapshot": {
                  "type": "object",
                  "nullable": true
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "SOURCE",
                    "DESTINATION",
                    "FUNDING",
                    "BENEFICIARY"
                  ],
                  "description": "How this method was used in the transaction. The create endpoint assigns `SOURCE`/`DESTINATION` from the direction (outbound: subject = SOURCE; inbound: subject = DESTINATION)."
                }
              }
            },
            "description": "Payment methods linked to the transaction."
          },
          "activities": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "activity_type": {
                  "type": "string",
                  "description": "E.g. `TRANSACTION_CREATED`, `TRANSACTION_NOTE_ADDED`, `TRANSACTION_STATUS_CHANGED`."
                },
                "source": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "nullable": true
                },
                "title": {
                  "type": "string",
                  "nullable": true
                },
                "description": {
                  "type": "string",
                  "nullable": true
                },
                "metadata": {
                  "type": "object"
                },
                "occurred_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "description": "Activity log entries (creation, notes, manual reviews, status changes)."
          },
          "alerts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": "string"
                },
                "description": {
                  "type": "string",
                  "nullable": true
                },
                "severity": {
                  "type": "string",
                  "enum": [
                    "UNKNOWN",
                    "LOW",
                    "MEDIUM",
                    "HIGH",
                    "CRITICAL"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "OPEN",
                    "INVESTIGATING",
                    "AWAITING_USER",
                    "PENDING_SAR",
                    "SAR_FILED",
                    "RESOLVED",
                    "DISMISSED"
                  ]
                },
                "source": {
                  "type": "string",
                  "enum": [
                    "RULE",
                    "PROVIDER",
                    "MANUAL"
                  ]
                },
                "metadata": {
                  "type": "object"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "due_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            },
            "description": "Alerts raised by rules and providers."
          },
          "rule_runs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "rule": {
                  "type": "string",
                  "format": "uuid",
                  "description": "UUID of the rule that ran."
                },
                "rule_title": {
                  "type": "string"
                },
                "rule_description": {
                  "type": "string",
                  "nullable": true
                },
                "rule_category": {
                  "type": "string"
                },
                "matched": {
                  "type": "boolean",
                  "description": "Whether the rule's conditions matched this transaction."
                },
                "is_test": {
                  "type": "boolean",
                  "description": "True when the rule ran in TEST mode (no effect on status/score)."
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "ACTIVE",
                    "DISABLED",
                    "TEST"
                  ]
                },
                "severity": {
                  "type": "string",
                  "nullable": true
                },
                "score_delta": {
                  "type": "integer",
                  "description": "Score added by this rule's `add_score` actions."
                },
                "status_target": {
                  "type": "string",
                  "nullable": true,
                  "description": "Status set by a `change_status` action, if any."
                },
                "action_summary": {
                  "type": "object",
                  "description": "Summary of the actions the rule executed."
                },
                "metadata": {
                  "type": "object"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "description": "Per-rule execution results — which rule fired, with what score impact."
          },
          "provider_results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "uuid": {
                  "type": "string",
                  "format": "uuid"
                },
                "provider": {
                  "type": "string"
                },
                "result_type": {
                  "type": "string",
                  "enum": [
                    "FIAT_MONITORING",
                    "WALLET_SCREENING",
                    "TRANSACTION_SCREENING",
                    "TRAVEL_RULE",
                    "NETWORK_GRAPH",
                    "CUSTOM"
                  ]
                },
                "status": {
                  "type": "string"
                },
                "severity": {
                  "type": "string",
                  "nullable": true
                },
                "score": {
                  "type": "integer"
                },
                "summary": {
                  "type": "string",
                  "nullable": true
                },
                "payload": {
                  "type": "object",
                  "description": "Raw provider response payload."
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "description": "Raw provider payloads (AML screening, blockchain analytics, etc.)."
          },
          "travel_rule": {
            "type": "object",
            "nullable": true,
            "description": "Travel Rule compliance check, present when `travel_rule_details` was submitted.",
            "properties": {
              "uuid": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string"
              },
              "protocol": {
                "type": "string",
                "nullable": true
              },
              "required": {
                "type": "boolean"
              },
              "obligations_count": {
                "type": "integer"
              },
              "originator_data": {
                "type": "object"
              },
              "beneficiary_data": {
                "type": "object"
              },
              "metadata": {
                "type": "object"
              }
            }
          },
          "network_snapshot": {
            "type": "object",
            "nullable": true,
            "description": "Graph snapshot of related transactions/parties used by the rules engine, present when submitted.",
            "properties": {
              "uuid": {
                "type": "string",
                "format": "uuid"
              },
              "nodes": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "edges": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "metrics": {
                "type": "object"
              }
            }
          },
          "remediation": {
            "type": "object",
            "nullable": true,
            "description": "Remediation session offered to the user when re-verification is required. Superseded by `action_required`, which is the canonical block; kept for backward compatibility.",
            "properties": {
              "session_id": {
                "type": "string"
              },
              "session_token": {
                "type": "string"
              },
              "url": {
                "type": "string",
                "format": "uri"
              },
              "status": {
                "type": "string"
              }
            }
          },
          "action_required": {
            "type": "object",
            "nullable": true,
            "description": "Pending end-user action required to complete this transaction, or null when there is none. The Didit SDKs auto-launch the action by default and refresh the transaction when it finishes. Two variants, discriminated by `type`: `verification_session` (a hosted verification session created by a rule action) and `wallet_ownership` (a wallet-ownership widget session auto-minted for a Travel Rule transfer that needs proof of wallet control, gated by the `auto_wallet_verification` Travel Rule setting). When both could apply, `wallet_ownership` takes precedence because it blocks the Travel Rule exchange.",
            "oneOf": [
              {
                "title": "Verification session",
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "verification_session"
                    ]
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Hosted verification URL to open for the end user."
                  },
                  "session_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Verification session id."
                  },
                  "session_token": {
                    "type": "string",
                    "description": "Session token, used by the mobile SDKs to launch the flow natively."
                  },
                  "status": {
                    "type": "string",
                    "description": "Verification session status."
                  }
                },
                "required": [
                  "type",
                  "url"
                ]
              },
              {
                "title": "Wallet ownership",
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "wallet_ownership"
                    ]
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Hosted wallet-ownership widget URL. Open it in a real browser surface - on mobile use SFSafariViewController or a Chrome Custom Tab, never an embedded webview."
                  },
                  "widget_session_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Wallet-ownership widget session id."
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "When the widget session expires."
                  }
                },
                "required": [
                  "type",
                  "url"
                ]
              }
            ]
          },
          "cost_breakdown": {
            "type": "object",
            "nullable": true,
            "description": "Per-feature credit cost breakdown for this transaction."
          }
        }
      },
      "TravelRuleSettingsDetail": {
        "type": "object",
        "properties": {
          "is_enabled": {
            "type": "boolean",
            "description": "Whether the managed Travel Rule negotiation flow is active for outbound `travelRule` transactions on this application."
          },
          "jurisdiction": {
            "type": "string",
            "maxLength": 8,
            "description": "Your VASP's operating jurisdiction code. Default `\"EU\"`."
          },
          "name_matching_strictness": {
            "type": "string",
            "enum": [
              "NONE",
              "STRICT",
              "DEFAULT",
              "FUZZY"
            ],
            "description": "How strictly beneficiary names must match before a transfer can reach COMPLETED."
          },
          "confirmation_timeout_hours": {
            "type": "integer",
            "minimum": 1,
            "description": "Hours an AWAITING_COUNTERPARTY transfer may remain unconfirmed before the periodic sweeper expires it. Default 48."
          },
          "timeout_outcome": {
            "type": "string",
            "enum": [
              "HOLD",
              "REJECT",
              "PROCEED"
            ],
            "description": "What happens to the underlying transaction when a transfer expires. Default HOLD."
          },
          "threshold_amount": {
            "type": "string",
            "description": "Decimal string. Minimum transfer amount that triggers a managed exchange; transfers below it resolve straight to NOT_APPLICABLE. Defaults to \"0.00\" (EU TFR applies no minimum amount)."
          },
          "legal_name": {
            "type": "string",
            "description": "Your VASP's legal name. Required (non-blank) to set is_enabled to true."
          },
          "lei": {
            "type": "string",
            "description": "Your Legal Entity Identifier, surfaced in the VASP directory."
          },
          "compliance_email": {
            "type": "string",
            "format": "email",
            "description": "Compliance contact email for your VASP profile."
          },
          "is_discoverable": {
            "type": "boolean",
            "description": "Whether your VASP profile appears in GET /v3/travel-rule/vasps/ search results and can be reached on the INTERNAL rail. Default true."
          },
          "inbound_auto_accept": {
            "type": "boolean",
            "description": "When true, inbound transfers whose beneficiary wallet is a verified address-book entry with a matching name are accepted automatically, without a manual review step. Default false."
          },
          "allow_self_declaration": {
            "type": "boolean",
            "description": "When true, the wallet-ownership widget offers self-declaration as a fallback proof method. Default false."
          },
          "allow_screenshot_proof": {
            "type": "boolean",
            "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true."
          },
          "auto_wallet_verification": {
            "type": "boolean",
            "description": "When true, submitting an outbound Travel Rule transfer that needs end-user proof of wallet control auto-mints a wallet-ownership widget session bound to the transaction and exposes it in the transaction's action_required block. Default true."
          },
          "vasp_attribution_enabled": {
            "type": "boolean",
            "description": "When true, an outbound Travel Rule transfer that no rail can route is attributed through blockchain analytics: the destination wallet is resolved to its owning VASP, the entity is recorded on the transfer and in the VASP directory, and the rails are re-run once with the attributed name. Default true."
          },
          "travel_address": {
            "type": "string",
            "description": "Read-only. Your VASP's own travel address - the encoded inbound TRP inquiry endpoint other VASPs use to reach you. Populated once your VASP profile is complete."
          }
        }
      },
      "TravelRuleSettingsUpdate": {
        "type": "object",
        "properties": {
          "is_enabled": {
            "type": "boolean"
          },
          "legal_name": {
            "type": "string"
          },
          "lei": {
            "type": "string"
          },
          "jurisdiction": {
            "type": "string",
            "maxLength": 8
          },
          "name_matching_strictness": {
            "type": "string",
            "enum": [
              "NONE",
              "STRICT",
              "DEFAULT",
              "FUZZY"
            ]
          },
          "confirmation_timeout_hours": {
            "type": "integer",
            "minimum": 1
          },
          "timeout_outcome": {
            "type": "string",
            "enum": [
              "HOLD",
              "REJECT",
              "PROCEED"
            ]
          },
          "compliance_email": {
            "type": "string",
            "format": "email"
          },
          "is_discoverable": {
            "type": "boolean"
          },
          "threshold_amount": {
            "type": "string",
            "description": "Decimal string. Minimum transfer amount that triggers a managed exchange."
          },
          "inbound_auto_accept": {
            "type": "boolean"
          },
          "allow_self_declaration": {
            "type": "boolean"
          },
          "allow_screenshot_proof": {
            "type": "boolean",
            "description": "When true, the wallet-ownership widget offers screenshot upload as an ownership evidence method. Default true."
          },
          "auto_wallet_verification": {
            "type": "boolean"
          },
          "vasp_attribution_enabled": {
            "type": "boolean"
          }
        },
        "description": "All fields optional; PUT applies a partial update."
      },
      "WalletAddressEntry": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "address": {
            "type": "string",
            "description": "The wallet address. Unique per application and chain among non-deleted entries."
          },
          "chain": {
            "type": "string",
            "nullable": true,
            "description": "Free-text chain identifier, e.g. \"ethereum\", \"bitcoin\"."
          },
          "holder_vendor_data": {
            "type": "string",
            "nullable": true,
            "description": "Your internal identifier for the holder."
          },
          "holder_name": {
            "type": "string",
            "nullable": true,
            "description": "Name of the wallet holder, used for the beneficiary name match."
          },
          "entity_type": {
            "type": "string",
            "description": "Free text, defaults to \"individual\"."
          },
          "is_ownership_verified": {
            "type": "boolean",
            "description": "Read-only. Whether ownership of this address has been proven."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "self_declared": {
            "type": "boolean",
            "description": "Whether the entry was created as a self-declared ownership proof."
          },
          "travel_address": {
            "type": "string",
            "nullable": true,
            "description": "The counterparty VASP's travel address for this wallet, if known. Lets Didit route this beneficiary's transfers over the TRP rail."
          }
        }
      },
      "WalletAddressEntryCreate": {
        "type": "object",
        "required": [
          "address"
        ],
        "properties": {
          "address": {
            "type": "string"
          },
          "chain": {
            "type": "string"
          },
          "holder_vendor_data": {
            "type": "string"
          },
          "holder_name": {
            "type": "string"
          },
          "entity_type": {
            "type": "string",
            "default": "individual"
          },
          "self_declared": {
            "type": "boolean",
            "default": false,
            "description": "Write-only. When true, the entry is created already ownership-verified via a SELF_DECLARATION proof."
          },
          "travel_address": {
            "type": "string",
            "description": "Optional. The counterparty VASP's travel address for this wallet, enabling TRP routing."
          }
        }
      },
      "WalletAddressEntryUpdate": {
        "type": "object",
        "properties": {
          "holder_name": {
            "type": "string"
          },
          "holder_vendor_data": {
            "type": "string"
          },
          "entity_type": {
            "type": "string"
          },
          "travel_address": {
            "type": "string"
          }
        },
        "description": "PATCH is a partial update; the response echoes only holder_name, holder_vendor_data, entity_type, and travel_address."
      },
      "TravelRuleTransferDetail": {
        "type": "object",
        "description": "Full transfer object returned by the ownership-confirmation and finish/cancel endpoints. originator_data/beneficiary_data are omitted while PII is masked (see description).",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "AWAITING_COUNTERPARTY",
              "ON_HOLD",
              "COMPLETED",
              "FINISHED",
              "CANCELLED",
              "EXPIRED",
              "COUNTERPARTY_MISMATCHED_DATA",
              "COUNTERPARTY_VASP_NOT_FOUND",
              "COUNTERPARTY_VASP_NOT_REACHABLE",
              "UNCONFIRMED_OWNERSHIP",
              "COUNTERPARTY_VASP_GENERAL_DECLINE",
              "NOT_ENOUGH_COUNTERPARTY_DATA",
              "NOT_APPLICABLE"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "INBOUND",
              "OUTBOUND"
            ]
          },
          "rail": {
            "type": "string",
            "nullable": true,
            "enum": [
              "INTERNAL",
              "TRP",
              "GTR",
              "EMAIL",
              null
            ]
          },
          "protocol": {
            "type": "string",
            "nullable": true
          },
          "ivms_version": {
            "type": "string",
            "example": "ivms101.2023"
          },
          "required": {
            "type": "boolean"
          },
          "ownership_confirmed": {
            "type": "boolean",
            "nullable": true
          },
          "deadline_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "timeline": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "counterparty_vasp": {
            "type": "object",
            "nullable": true,
            "properties": {
              "name": {
                "type": "string"
              },
              "lei": {
                "type": "string"
              },
              "dd_score": {
                "type": "integer"
              }
            }
          },
          "originator_data": {
            "type": "object"
          },
          "beneficiary_data": {
            "type": "object"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TravelRuleOwnershipConfirmRequest": {
        "type": "object",
        "required": [
          "confirmed"
        ],
        "properties": {
          "confirmed": {
            "type": "boolean"
          }
        }
      },
      "TravelRuleTransferPatchRequest": {
        "type": "object",
        "description": "Provide exactly one of: payment_txn_id (finish), action=cancel, or action=resend.",
        "properties": {
          "payment_txn_id": {
            "type": "string",
            "description": "The on-chain transaction hash. Requires the transfer to be an outbound transfer in COMPLETED status."
          },
          "action": {
            "type": "string",
            "enum": [
              "cancel",
              "resend"
            ],
            "description": "\"cancel\" cancels a non-terminal transfer. \"resend\" re-runs routing for an outbound transfer stuck in COUNTERPARTY_VASP_NOT_FOUND, COUNTERPARTY_VASP_NOT_REACHABLE, or NOT_ENOUGH_COUNTERPARTY_DATA."
          }
        }
      },
      "VaspDirectoryEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "lei": {
            "type": "string"
          },
          "jurisdiction": {
            "type": "string"
          },
          "dd_status": {
            "type": "string",
            "enum": [
              "NOT_STARTED",
              "IN_PROGRESS",
              "COMPLETED",
              "REJECTED"
            ]
          },
          "dd_score": {
            "type": "integer"
          },
          "reachable_rails": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "INTERNAL",
                "TRP",
                "GTR",
                "EMAIL"
              ]
            }
          },
          "is_didit_customer": {
            "type": "boolean",
            "description": "true for discoverable Didit applications (dd_status is always COMPLETED, dd_score always 0 for these); false for internal counterparty-registry rows, which carry a real due-diligence status and score."
          },
          "dd_score_breakdown": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Per-component contributions to dd_score (licensing/registry, sanctions/PEP, jurisdiction risk, on-chain analytics risk), all derived from checks Didit runs in-house. Empty for Didit-customer rows."
          },
          "dd_assessed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the due-diligence assessment last ran. Null for Didit-customer rows."
          }
        }
      },
      "TravelRulePickupInfo": {
        "type": "object",
        "properties": {
          "originator_vasp_name": {
            "type": "string"
          },
          "amount": {
            "type": "string"
          },
          "currency": {
            "type": "string"
          },
          "beneficiary_wallet": {
            "type": "string"
          },
          "status": {
            "type": "string"
          }
        }
      },
      "TravelRulePickupRespondRequest": {
        "type": "object",
        "required": [
          "decision"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "accept",
              "decline"
            ]
          },
          "beneficiary_name": {
            "type": "string",
            "description": "Required in practice for decision=accept; run through the originator's name_matching_strictness policy."
          }
        }
      },
      "TravelRuleInboundRequest": {
        "type": "object",
        "required": [
          "chain",
          "txid",
          "wallet_address",
          "amount",
          "currency"
        ],
        "properties": {
          "chain": {
            "type": "string",
            "description": "Chain the deposit settled on, e.g. \"ethereum\"."
          },
          "txid": {
            "type": "string",
            "description": "On-chain transaction hash. Deduped together with wallet_address."
          },
          "wallet_address": {
            "type": "string",
            "description": "The destination wallet you received the deposit on."
          },
          "amount": {
            "type": "string",
            "description": "Deposit amount as a decimal string."
          },
          "currency": {
            "type": "string",
            "description": "Deposit asset, e.g. \"USDC\"."
          },
          "originator_data": {
            "type": "object",
            "description": "IVMS-101 originator payload from the sending VASP, if known.",
            "additionalProperties": true
          },
          "beneficiary_data": {
            "type": "object",
            "description": "IVMS-101 beneficiary payload (your customer).",
            "additionalProperties": true
          },
          "originating_vasp": {
            "type": "object",
            "description": "Optional description of the sending VASP.",
            "properties": {
              "name": {
                "type": "string"
              },
              "lei": {
                "type": "string"
              },
              "travel_address": {
                "type": "string",
                "description": "Travel address that lets Didit reach the sender on the TRP rail."
              }
            }
          }
        }
      },
      "TravelRuleInboundResponse": {
        "type": "object",
        "properties": {
          "transaction_id": {
            "type": "string",
            "format": "uuid",
            "description": "Didit transaction UUID for the inbound deposit."
          },
          "txn_id": {
            "type": "string",
            "description": "Didit-generated txn_id for the sunrise transaction."
          },
          "created": {
            "type": "boolean",
            "description": "true when a new transaction was minted (HTTP 201); false when an existing unclaimed deposit was reused (HTTP 200)."
          },
          "travel_rule": {
            "$ref": "#/components/schemas/TravelRuleTransferDetail"
          }
        }
      },
      "TravelRuleWidgetSessionCreate": {
        "type": "object",
        "required": [
          "wallet_address",
          "chain"
        ],
        "properties": {
          "wallet_address": {
            "type": "string",
            "description": "The wallet address to verify."
          },
          "chain": {
            "type": "string",
            "description": "Chain identifier for the address, e.g. \"ethereum\", \"bitcoin\", \"solana\"."
          },
          "holder_name": {
            "type": "string",
            "description": "Name of the wallet holder, used for the beneficiary name match."
          },
          "vendor_data": {
            "type": "string",
            "description": "Your internal identifier for the holder, carried onto the created address-book entry."
          },
          "transaction_id": {
            "type": "string",
            "format": "uuid",
            "description": "Optional. Didit transaction UUID of a Travel Rule transfer to advance when the proof verifies."
          },
          "satoshi_deposit_address": {
            "type": "string",
            "description": "Optional. A deposit address you control; supplying it enables the SATOSHI_TEST proof method."
          },
          "callback_url": {
            "type": "string",
            "description": "Optional. Where the widget returns the customer after completion."
          },
          "expires_in_minutes": {
            "type": "integer",
            "description": "Optional. Lifetime of the minted link, in minutes. Automatically extended to cover the Satoshi test window when SATOSHI_TEST is offered."
          }
        }
      },
      "TravelRuleWidgetSessionResponse": {
        "type": "object",
        "properties": {
          "widget_session_id": {
            "type": "string",
            "format": "uuid"
          },
          "token": {
            "type": "string",
            "description": "Opaque session token embedded in the widget URL."
          },
          "url": {
            "type": "string",
            "description": "The hosted wallet-ownership widget URL. Open it verbatim in the system browser (never inside a mobile SDK webview)."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TravelRuleWidgetContext": {
        "type": "object",
        "properties": {
          "vasp_name": {
            "type": "string",
            "description": "Legal name from your VASP profile, shown to the customer in the widget. Empty when no profile exists."
          },
          "wallet_address": {
            "type": "string"
          },
          "chain": {
            "type": "string"
          },
          "holder_name": {
            "type": "string"
          },
          "allowed_methods": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "MESSAGE_SIGNING",
                "SCREENSHOT",
                "SATOSHI_TEST",
                "SELF_DECLARATION"
              ]
            },
            "description": "Proof methods available for this session. MESSAGE_SIGNING requires a supported chain family, SATOSHI_TEST requires a satoshi_deposit_address on the session, SCREENSHOT requires the allow_screenshot_proof setting, and SELF_DECLARATION requires the allow_self_declaration setting."
          },
          "transfer_status": {
            "type": "string",
            "nullable": true,
            "description": "Status of the linked Travel Rule transfer. Null when the session was minted without a transaction_id."
          },
          "callback_url": {
            "type": "string",
            "description": "Where the widget returns the customer after completion. Empty when not set."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Non-null once a proof has been verified."
          },
          "proofs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TravelRuleWidgetProofSummary"
            }
          },
          "customization": {
            "type": "object",
            "nullable": true,
            "description": "The application's whitelabel customization (colors, logo, font family) when white label is enabled - the widget renders with the same branding as the verification flow. Null otherwise."
          }
        }
      },
      "TravelRuleWidgetProofSummary": {
        "type": "object",
        "properties": {
          "proof_id": {
            "type": "string",
            "format": "uuid"
          },
          "method": {
            "type": "string",
            "enum": [
              "MESSAGE_SIGNING",
              "SATOSHI_TEST",
              "SCREENSHOT",
              "SELF_DECLARATION"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "VERIFIED",
              "REJECTED",
              "EXPIRED"
            ],
            "description": "PENDING on a SATOSHI_TEST proof means the deposit has been seen on-chain but has not yet reached enough confirmations; a background job automatically rechecks it and moves it to VERIFIED once it confirms, with no resubmission needed. REJECTED means a genuinely wrong deposit was submitted (absent, wrong sender, or wrong amount). EXPIRED means the proof's own test window closed before a valid deposit was seen."
          },
          "wallet_address": {
            "type": "string"
          },
          "chain": {
            "type": "string"
          },
          "deposit_address": {
            "type": "string",
            "nullable": true,
            "description": "Deposit address the customer must send the Satoshi test amount to. Present only on a SATOSHI_TEST proof."
          },
          "expected_amount": {
            "type": "string",
            "nullable": true,
            "description": "Exact on-chain amount, in the deposit's native currency, the Satoshi test deposit must match. Present only on a SATOSHI_TEST proof."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Deadline for this proof's own Satoshi test window. Present only on a SATOSHI_TEST proof; the widget session's own expires_at is extended to cover it when SATOSHI_TEST is offered."
          },
          "challenge": {
            "type": "string",
            "nullable": true,
            "description": "Didit-issued message the customer signs with their wallet. Present only on a MESSAGE_SIGNING proof."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseSubject": {
        "type": "object",
        "nullable": true,
        "description": "The single user or business the case is anchored to.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "user",
              "business"
            ]
          },
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": "string",
            "nullable": true,
            "description": "The subject's vendor_data."
          }
        }
      },
      "CaseAssignee": {
        "type": "object",
        "nullable": true,
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "full_name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "CaseBlueprintSummary": {
        "type": "object",
        "nullable": true,
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 24
          }
        }
      },
      "CaseContentConfig": {
        "type": "object",
        "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.",
        "properties": {
          "checklist": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "optional": {
                "type": "boolean",
                "description": "When true, a case can be resolved with incomplete checklist items."
              },
              "items": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "type": "string",
                      "maxLength": 255
                    },
                    "order": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "aml_control": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "financial": {
                "type": "boolean"
              },
              "identity": {
                "type": "boolean"
              }
            }
          },
          "identity": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "personal_information": {
                "type": "boolean"
              },
              "applicant_tags": {
                "type": "boolean"
              },
              "contact_information": {
                "type": "boolean"
              },
              "risk_overview": {
                "type": "boolean"
              },
              "poi_documents": {
                "type": "boolean"
              },
              "poa_documents": {
                "type": "boolean"
              }
            }
          },
          "verifications": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "summary": {
                "type": "boolean"
              }
            }
          },
          "reporting": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "fiu_reports": {
                "type": "boolean"
              }
            }
          },
          "financial": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "payment_methods": {
                "type": "boolean"
              },
              "transactions": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "CaseNote": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "comment": {
            "type": "string"
          },
          "actor_email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "actor_name": {
            "type": "string",
            "nullable": true
          },
          "mentioned_emails": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "is_internal": {
            "type": "boolean"
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "s3_key": {
                  "type": "string"
                },
                "filename": {
                  "type": "string"
                },
                "file_size": {
                  "type": "integer"
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseEvent": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "event_type": {
            "type": "string",
            "enum": [
              "CREATED",
              "STATUS_CHANGED",
              "ASSIGNED",
              "UNASSIGNED",
              "RESOLVED",
              "REOPENED",
              "LINK_ADDED",
              "LINK_REMOVED",
              "NOTE_ADDED",
              "PRIORITY_CHANGED",
              "DUE_DATE_CHANGED",
              "TAG_ADDED",
              "TAG_REMOVED",
              "ESCALATED",
              "TRANSFERRED",
              "CHECKLIST_COMPLETED",
              "REPORT_CREATED",
              "REPORT_FINALIZED",
              "SUBMITTED_FOR_APPROVAL",
              "APPROVAL_GRANTED",
              "APPROVAL_REJECTED",
              "ENTITY_ACTION"
            ]
          },
          "actor_email": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "actor_name": {
            "type": "string",
            "nullable": true
          },
          "previous_status": {
            "type": "string",
            "enum": [
              "OPEN",
              "AWAITING_USER",
              "RESOLVED"
            ],
            "nullable": true
          },
          "new_status": {
            "type": "string",
            "enum": [
              "OPEN",
              "AWAITING_USER",
              "RESOLVED"
            ],
            "nullable": true
          },
          "metadata": {
            "type": "object"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseLink": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "linked_entity_type": {
            "type": "string",
            "enum": [
              "session",
              "business_session",
              "transaction"
            ],
            "nullable": true
          },
          "linked_entity_uuid": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "linked_entity_summary": {
            "type": "object",
            "nullable": true,
            "description": "session_id/session_number/status for session and business_session links; uuid/txn_id/status for transaction links."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseChecklistItem": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string",
            "maxLength": 255
          },
          "is_completed": {
            "type": "boolean"
          },
          "completed_by": {
            "type": "string",
            "format": "email",
            "nullable": true
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "order": {
            "type": "integer"
          }
        }
      },
      "CaseBlueprintListItem": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 24
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "assignment_method": {
            "type": "string",
            "enum": [
              "MANUAL",
              "AUTOMATIC"
            ]
          },
          "assignee_pool": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "max_active_cases": {
            "type": "integer",
            "nullable": true
          },
          "escalation_enabled": {
            "type": "boolean"
          },
          "transfer_enabled": {
            "type": "boolean"
          },
          "require_resolution_note": {
            "type": "boolean"
          },
          "is_active": {
            "type": "boolean"
          },
          "preset_key": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseBlueprintDetail": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 24
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "assignment_method": {
            "type": "string",
            "enum": [
              "MANUAL",
              "AUTOMATIC"
            ]
          },
          "assignee_pool": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "max_active_cases": {
            "type": "integer",
            "nullable": true
          },
          "escalation_enabled": {
            "type": "boolean"
          },
          "transfer_enabled": {
            "type": "boolean"
          },
          "require_resolution_note": {
            "type": "boolean"
          },
          "is_active": {
            "type": "boolean"
          },
          "preset_key": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "escalation_blueprint": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "escalation_blueprint_name": {
            "type": "string",
            "nullable": true
          },
          "four_eyes_checker_blueprint": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "four_eyes_checker_blueprint_name": {
            "type": "string",
            "nullable": true
          },
          "content_config": {
            "type": "object",
            "description": "The six togglable sections that drive which tabs and fields appear on the case page. Unknown sections or keys are rejected.",
            "properties": {
              "checklist": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "optional": {
                    "type": "boolean",
                    "description": "When true, a case can be resolved with incomplete checklist items."
                  },
                  "items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string",
                          "maxLength": 255
                        },
                        "order": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              },
              "aml_control": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "financial": {
                    "type": "boolean"
                  },
                  "identity": {
                    "type": "boolean"
                  }
                }
              },
              "identity": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "personal_information": {
                    "type": "boolean"
                  },
                  "applicant_tags": {
                    "type": "boolean"
                  },
                  "contact_information": {
                    "type": "boolean"
                  },
                  "risk_overview": {
                    "type": "boolean"
                  },
                  "poi_documents": {
                    "type": "boolean"
                  },
                  "poa_documents": {
                    "type": "boolean"
                  }
                }
              },
              "verifications": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "summary": {
                    "type": "boolean"
                  }
                }
              },
              "reporting": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "fiu_reports": {
                    "type": "boolean"
                  }
                }
              },
              "financial": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "payment_methods": {
                    "type": "boolean"
                  },
                  "transactions": {
                    "type": "boolean"
                  }
                }
              }
            }
          },
          "creation_sources": {
            "type": "array",
            "description": "Connectors currently routing new cases into this blueprint.",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "aml",
                    "rule",
                    "workflow"
                  ]
                },
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "receives_escalation_from": {
            "type": "array",
            "description": "Other blueprints whose Escalation target points at this one.",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              }
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CaseListItem": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "case_number": {
            "type": "string",
            "nullable": true,
            "description": "Auto-assigned, e.g. CS-0001."
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "AWAITING_USER",
              "RESOLVED"
            ]
          },
          "resolution": {
            "type": "string",
            "enum": [
              "FALSE_POSITIVE",
              "VALID_THREAT"
            ],
            "nullable": true
          },
          "priority": {
            "type": "string",
            "enum": [
              "LOW",
              "MEDIUM",
              "HIGH"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "MANUAL",
              "AML_SCREENING",
              "TRANSACTION_RULE",
              "WORKFLOW_BUILDER"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "$ref": "#/components/schemas/CaseSubject"
          },
          "assigned_to": {
            "$ref": "#/components/schemas/CaseAssignee"
          },
          "assigned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resolved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_by_email": {
            "type": "string",
            "nullable": true
          },
          "created_by_name": {
            "type": "string",
            "nullable": true
          },
          "blueprint": {
            "$ref": "#/components/schemas/CaseBlueprintSummary"
          },
          "links_count": {
            "type": "integer"
          },
          "notes_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "application_id": {
            "type": "string",
            "format": "uuid"
          },
          "application_name": {
            "type": "string"
          },
          "organization_id": {
            "type": "string",
            "format": "uuid"
          },
          "organization_name": {
            "type": "string"
          }
        }
      },
      "CaseDetail": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "case_number": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "OPEN",
              "AWAITING_USER",
              "RESOLVED"
            ]
          },
          "resolution": {
            "type": "string",
            "enum": [
              "FALSE_POSITIVE",
              "VALID_THREAT"
            ],
            "nullable": true
          },
          "resolution_notes": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "type": "string",
            "enum": [
              "LOW",
              "MEDIUM",
              "HIGH"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "MANUAL",
              "AML_SCREENING",
              "TRANSACTION_RULE",
              "WORKFLOW_BUILDER"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "subject": {
            "$ref": "#/components/schemas/CaseSubject"
          },
          "assigned_to": {
            "$ref": "#/components/schemas/CaseAssignee"
          },
          "assigned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "due_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resolved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resolved_by_email": {
            "type": "string",
            "nullable": true
          },
          "pending_resolution": {
            "type": "object",
            "nullable": true,
            "description": "Set only while a 4-eyes resolution is staged on the checker blueprint, awaiting approve or reject-approval.",
            "properties": {
              "resolution": {
                "type": "string",
                "enum": [
                  "FALSE_POSITIVE",
                  "VALID_THREAT"
                ]
              },
              "resolution_notes": {
                "type": "string",
                "nullable": true
              },
              "maker_email": {
                "type": "string",
                "format": "email"
              },
              "maker_name": {
                "type": "string",
                "nullable": true
              },
              "maker_blueprint_id": {
                "type": "string",
                "format": "uuid"
              },
              "staged_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "metadata": {
            "type": "object"
          },
          "blueprint": {
            "$ref": "#/components/schemas/CaseBlueprintDetail"
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaseNote"
            },
            "description": "Newest 50 notes."
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaseEvent"
            },
            "description": "Newest 100 events."
          },
          "links": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaseLink"
            }
          },
          "links_count": {
            "type": "integer"
          },
          "notes_count": {
            "type": "integer"
          },
          "checklist_items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CaseChecklistItem"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ReportTemplateDetail": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "jurisdiction": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2 country code."
          },
          "report_type": {
            "type": "string",
            "enum": [
              "SAR",
              "STR",
              "CTR",
              "TTR",
              "UTR",
              "IFT",
              "CBR",
              "TFR"
            ]
          },
          "report_type_display": {
            "type": "string"
          },
          "field_config": {
            "type": "object",
            "description": "Reusable goAML header defaults. Template-level obligatory fields: rentity_id, submission_code, and currency_code_local (3-letter). Optional: enabled_fields (which optional report blocks to include) plus reporting_person, location, reason, and action defaults. Per-filing header fields (action, submission_date, transactions, jurisdiction) are enforced when the report is validated or finalized, not on the template."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "RegulatoryReportDetail": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "format": "uuid"
          },
          "case_uuid": {
            "type": "string",
            "format": "uuid"
          },
          "case_number": {
            "type": "string",
            "nullable": true
          },
          "report_type": {
            "type": "string",
            "enum": [
              "SAR",
              "STR",
              "CTR",
              "TTR",
              "UTR",
              "IFT",
              "CBR",
              "TFR"
            ]
          },
          "report_type_display": {
            "type": "string"
          },
          "report_code": {
            "type": "string",
            "description": "goAML report code derived from report_type, e.g. STR, CTR, BCR."
          },
          "report_number": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "DRAFT",
              "VALIDATED",
              "FINALIZED"
            ]
          },
          "status_display": {
            "type": "string"
          },
          "jurisdiction": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2
          },
          "rentity_id": {
            "type": "string",
            "nullable": true,
            "description": "Reporting entity ID issued by the FIU."
          },
          "rentity_branch": {
            "type": "string",
            "nullable": true
          },
          "submission_code": {
            "type": "string",
            "enum": [
              "E",
              "M"
            ]
          },
          "entity_reference": {
            "type": "string",
            "nullable": true
          },
          "fiu_ref_number": {
            "type": "string",
            "nullable": true
          },
          "currency_code_local": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "nullable": true
          },
          "submission_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "reporting_person": {
            "type": "object"
          },
          "location": {
            "type": "object"
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "action": {
            "type": "string",
            "nullable": true
          },
          "activity": {
            "type": "string",
            "nullable": true
          },
          "narrative": {
            "type": "object",
            "description": "Five Ws narrative sections.",
            "properties": {
              "who": {
                "type": "string"
              },
              "what": {
                "type": "string"
              },
              "when": {
                "type": "string"
              },
              "where": {
                "type": "string"
              },
              "why": {
                "type": "string"
              },
              "how": {
                "type": "string"
              }
            }
          },
          "report_indicators": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "included_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "transactions": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "validation_errors": {
            "type": "array",
            "description": "Result of the most recent validation; each entry is a {field, message} object.",
            "items": {
              "type": "object",
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "validated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finalized_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_by_email": {
            "type": "string",
            "nullable": true
          },
          "created_by_name": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      },
      "TransactionTokenAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Transaction-Token",
        "description": "Short-lived scoped token minted by your backend via POST /v3/transactions/sdk-token/. Used by the Didit SDKs on the device-facing /v1/transactions/ endpoints."
      },
      "SessionTokenAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "Session-Token",
        "description": "Short-lived token returned in the `session_token` field of POST /v3/session/. Used by the hosted verification flow (and custom sandbox UIs) to call session-scoped endpoints on behalf of the verifying user, without your server-side API key."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Unauthorized",
        "content": {
          "application/json": {
            "examples": {
              "Invalid API Key": {
                "summary": "Invalid API Key",
                "value": {
                  "detail": "Invalid API Key."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "Forbidden",
        "content": {
          "application/json": {
            "examples": {
              "No Permission": {
                "summary": "No Permission",
                "value": {
                  "detail": "You do not have permission to perform this action."
                }
              },
              "Not Enough Credits": {
                "summary": "Not Enough Credits",
                "value": {
                  "error": "You don't have enough credits to perform this request. Please top up at https://business.didit.me"
                }
              }
            }
          }
        }
      },
      "NotFound": {
        "description": "Not Found",
        "content": {
          "application/json": {
            "examples": {
              "Not Found": {
                "summary": "Not Found",
                "value": {
                  "detail": "Resource not found."
                }
              }
            }
          }
        }
      }
    }
  }
}