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

# Handles `POST /v1/verifications/sessions`.

> Runs the checks the tenant's policy requires and returns the outcome. A
provider outage is not an error here: it produces a review-required
session, because an unreachable model says nothing about the person.

# Which answer a request gets

Supplying `selfie_ref` or `document_ref` runs the session now and answers
[`VerificationSessionOutcomeBody::Decided`] - a decision, its reasons, and
the per-check results. Supplying neither opens a hosted capture link and
answers [`VerificationSessionOutcomeBody::Opened`], where nothing has been
decided and `status` is always `AWAITING_CAPTURE`; the decision arrives
later, by webhook.

# Errors

Returns `401` without usable credentials, `403` when the caller may not
submit verifications, and `503` when a store the decision depends on
cannot be reached.



## OpenAPI

````yaml /openapi.json post /v1/verifications/sessions
openapi: 3.1.0
info:
  title: Esectra API
  description: >-
    Transaction screening, identity verification, and wallet risk.


    Every create route accepts `Idempotency-Key`; replaying one returns the
    original record with `200` where the first call returned `201`. `POST
    /v1/transactions` requires the header, because a duplicated transaction is a
    duplicated financial record.
  license:
    name: proprietary
    identifier: proprietary
  version: 0.1.0
servers:
  - url: https://qa.esectra.com
    description: Esectra QA Documentation
security: []
paths:
  /v1/verifications/sessions:
    post:
      tags:
        - Verifications
      summary: Handles `POST /v1/verifications/sessions`.
      description: >-
        Runs the checks the tenant's policy requires and returns the outcome. A

        provider outage is not an error here: it produces a review-required

        session, because an unreachable model says nothing about the person.


        # Which answer a request gets


        Supplying `selfie_ref` or `document_ref` runs the session now and
        answers

        [`VerificationSessionOutcomeBody::Decided`] - a decision, its reasons,
        and

        the per-check results. Supplying neither opens a hosted capture link and

        answers [`VerificationSessionOutcomeBody::Opened`], where nothing has
        been

        decided and `status` is always `AWAITING_CAPTURE`; the decision arrives

        later, by webhook.


        # Errors


        Returns `401` without usable credentials, `403` when the caller may not

        submit verifications, and `503` when a store the decision depends on

        cannot be reached.
      operationId: run_verification_session_handler
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VerificationSessionRequestBody'
        required: true
      responses:
        '200':
          description: >-
            A decision when captures were supplied, or a capture link when they
            were not
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VerificationSessionOutcomeBody'
        '401':
          description: No usable credentials
        '403':
          description: The caller does not hold VERIFICATIONS_SUBMIT
        '422':
          description: Unknown intent, or an unusable capture
        '503':
          description: A backing store could not be reached
      security:
        - api_key: []
        - session_cookie: []
components:
  schemas:
    VerificationSessionRequestBody:
      type: object
      description: Request to run a verification session.
      required:
        - subject_id
      properties:
        document_ref:
          type:
            - string
            - 'null'
          description: Evidence reference for the identity document image.
        intent:
          type:
            - string
            - 'null'
          description: >-
            `FIRST_TIME` (the default) or `REVERIFICATION`.


            A first-time verification is proven against the document; a

            reverification against the face proven when the customer first
            passed.

            Defaulting to first-time is the safe direction: it never lets a
            stored

            face stand in as identity proof by omission.
        selfie_ref:
          type:
            - string
            - 'null'
          description: Evidence reference for the live capture.
        subject_id:
          type: string
          description: Person being verified, as the customer identifies them.
    VerificationSessionOutcomeBody:
      oneOf:
        - $ref: '#/components/schemas/CaptureSessionOpenedBody'
          description: A hosted capture link, awaiting the person being verified.
        - $ref: '#/components/schemas/VerificationSessionResponseBody'
          description: A session that ran to a decision.
      description: >-
        What `POST /v1/verifications/sessions` answers with.


        The route has two answers because it has two jobs. Supplying captures
        runs

        the session now and returns what was decided; supplying none opens a
        hosted

        capture link and returns where to send the person, with nothing decided

        yet. Untagged, so the body is the shape itself rather than the shape

        wrapped in a discriminator - which is what callers already parse.


        Naming the two removes a `serde_json::to_value(..).unwrap_or_default()`

        from each path. That turned a serialisation failure into `200` with a
        body

        of `null`: a success status carrying nothing, which a caller has no way
        to

        tell from a session that legitimately answered nothing.
    CaptureSessionOpenedBody:
      type: object
      description: Verification session response.
      required:
        - verification_session_id
        - status
        - intent
        - capture_url
        - expires_at
      properties:
        capture_url:
          type: string
          description: |-
            Where to send the person being verified.

            Contains the only copy of the capture token that will ever exist -
            the session keeps a hash. Treat it as a credential: send it to the
            person, do not log it.
        expires_at:
          type: string
          description: When the link stops working.
        intent:
          type: string
          description: Whether this is a first-time proof or a reverification.
        status:
          type: string
          description: 'Always `AWAITING_CAPTURE`: nothing has been decided yet.'
        verification_session_id:
          type: string
          description: Session identifier, for correlating the webhook that follows.
    VerificationSessionResponseBody:
      type: object
      description: A session that ran to a decision.
      required:
        - verification_session_id
        - status
        - intent
        - decision_id
        - decision
        - risk_level
        - reasons
        - evidence
        - checks
      properties:
        checks:
          type: array
          items:
            $ref: '#/components/schemas/VerificationCheckBody'
          description: Per-check results.
        decision:
          $ref: '#/components/schemas/Decision'
          description: Decision outcome.
        decision_id:
          type: string
          description: Decision emitted for the session.
        evidence:
          type: array
          items:
            $ref: '#/components/schemas/EvidenceRef'
          description: Evidence the decision cites.
        intent:
          type: string
          description: Whether this was a first-time proof or a reverification.
        reasons:
          type: array
          items:
            $ref: '#/components/schemas/DecisionReason'
          description: Why.
        review_case_id:
          type:
            - string
            - 'null'
          description: Manual-review case, when the outcome needs a person.
        risk_level:
          $ref: '#/components/schemas/RiskLevel'
          description: Risk level.
        status:
          type: string
          description: Terminal status.
        verification_session_id:
          type: string
          description: Session identifier.
    VerificationCheckBody:
      type: object
      description: |-
        One check's result, as a customer sees it.

        Deliberately carries no score, model name, or threshold: how the checks
        work is ours, and what they concluded is theirs.
      required:
        - check
        - outcome
        - detail
      properties:
        check:
          type: string
          description: Which check.
        detail:
          type: string
          description: Readable detail.
        outcome:
          type: string
          description: What it concluded.
    Decision:
      type: string
      description: Decision emitted by an automated or human-assisted workflow.
      enum:
        - ALLOW
        - WARN
        - BLOCK
        - HOLD
        - REVIEW_REQUIRED
        - FLAG
        - BLOCK_RECOMMENDED
        - SUSPEND
        - END_STREAM
    EvidenceRef:
      type: object
      description: >-
        Reference to retained evidence without embedding raw evidence in
        decisions.
      required:
        - id
        - kind
        - location
        - retention_policy
      properties:
        id:
          type: string
          description: Internal evidence identifier.
        kind:
          $ref: '#/components/schemas/EvidenceKind'
          description: Evidence type.
        location:
          type: string
          description: Storage URI or provider-specific object key.
        retention_policy:
          type: string
          description: Retention policy identifier applied to this evidence.
    DecisionReason:
      type: object
      description: Human-readable reason with a stable machine code.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: Stable reason code.
        message:
          type: string
          description: Explainable user/operator-facing message.
    RiskLevel:
      type: string
      description: Explainable risk level.
      enum:
        - LOW
        - MEDIUM
        - HIGH
        - CRITICAL
    EvidenceKind:
      type: string
      description: Kind of evidence captured for a decision or case.
      enum:
        - IMAGE
        - VIDEO
        - TEXT
        - DOCUMENT
        - METADATA
  securitySchemes:
    api_key:
      type: http
      scheme: bearer
      description: >-
        A tenant API key. Acts for exactly one tenant and cannot conclude a
        case, because a conclusion records a person.
    session_cookie:
      type: apiKey
      in: cookie
      name: esectra_session
      description: >-
        A signed-in reviewer's session. httpOnly and SameSite=Lax; set by `POST
        /v1/control/sessions` and only usable once the second factor is met.

````