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

> ## Agent Instructions
> Money amounts are always major-unit decimal strings (e.g. "1000.00"), never floats and never minor units, in every request, response, and webhook payload.
> Authentication is OAuth 2.0 client credentials: exchange the Client ID and Client Secret at the Token URL for a 24-hour JWT and cache it; do not request a token per call.
> Set a unique clientRef on every money-moving request; it is the idempotency key, and retries return the original resource.
> Quotes are single-use, intent-locked, and expire fast: always read expiresAt, consume the quote with the executor matching its intent, and re-quote on expiry instead of retrying.
> Prefer webhooks over polling for payout, order, application, and virtual-account status tracking.
> Use the sandbox (api.sandbox.zuba.com) with its deterministic magic values before touching production.

# Start the applicant's, or a director's or shareholder's, identity verification

> Starts an identity verification attempt for the application’s applicant, or with `personId` for a director or shareholder on it, and returns `url`, a hosted verification page. Send the person to it: they complete the identity check there, with no Zuba dashboard session, and land on `returnUrl` when given, or on a Zuba page saying the check is complete. The outcome reaches Zuba by webhook; read it back from GET /v2/applications/{id}/identity-verification, or subscribe to the `application.identity_verification_updated` webhook, which fires with the `personId` the session was started for: for the applicant, with a null `personId`, when they are verified or rejected (an expired attempt is rejected); for a director or shareholder, on each decision about their verification, by the provider, a reviewer or Zuba’s check of the name on the passed document, `pending` included when a decided session is reopened. Every director, and every shareholder at or above the ownership threshold (10% in NG, GH and KE, 25% elsewhere), must verify. The roster person marked `isApplicant` is the applicant: when they are one of those, starting the applicant’s session records them as a beneficial owner and their pass files its ID evidence on that record (once that start is recorded, the pass files on the marked record when they are a director or a shareholder), and `personId` naming them answers with the application’s own session; the mark is accepted only on a row carrying the applicant’s names, and exempts nobody else. A dashboard member with the developer role starts the applicant’s session only; `personId` answers 403 person_link_role for them. The link is valid until the session expires under the verification workflow’s expiry. A repeat call while the attempt is pending returns the same link, which keeps the `returnUrl` it was started with (for the applicant’s session, unless the verification workflow was republished in between, when a new session starts); once the attempt is rejected or expired, a call starts a new one with a new link. A new attempt replaces an earlier one, a `waived` outcome included, which then reads `pending` and no longer satisfies POST /v2/applications/{id}/submit until the attempt passes. A start is refused (409) while an attempt is still being decided by the verification provider or a reviewer, for a person already verified, and when the attempt changes while starting. A `verified` or `waived` outcome satisfies the identity requirement of POST /v2/applications/{id}/submit.



## OpenAPI

````yaml /openapi.json post /v2/applications/{id}/identity-verification/session
openapi: 3.0.0
info:
  contact: {}
  description: >-
    Comprehensive payment platform API supporting fiat and crypto payments,
    currency conversion, and compliance management
  title: Zuba Payment Platform API
  version: '1.0'
servers: []
security: []
tags:
  - description: Manage M2M API keys and credentials
    name: API Keys
  - description: Per-country bank directories and bank-account enquiries
    name: Banks
  - description: Internal ledger accounts and transactions
    name: Ledger
  - description: Handle incoming payments and deposits
    name: Pay-ins
  - description: Manage payouts, beneficiaries, and SEPA transfers
    name: Payouts
  - description: Manage outbound webhook endpoints and deliveries
    name: Webhooks
paths:
  /v2/applications/{id}/identity-verification/session:
    post:
      tags:
        - Applications (v2)
      summary: >-
        Start the applicant's, or a director's or shareholder's, identity
        verification
      description: >-
        Starts an identity verification attempt for the application’s applicant,
        or with `personId` for a director or shareholder on it, and returns
        `url`, a hosted verification page. Send the person to it: they complete
        the identity check there, with no Zuba dashboard session, and land on
        `returnUrl` when given, or on a Zuba page saying the check is complete.
        The outcome reaches Zuba by webhook; read it back from GET
        /v2/applications/{id}/identity-verification, or subscribe to the
        `application.identity_verification_updated` webhook, which fires with
        the `personId` the session was started for: for the applicant, with a
        null `personId`, when they are verified or rejected (an expired attempt
        is rejected); for a director or shareholder, on each decision about
        their verification, by the provider, a reviewer or Zuba’s check of the
        name on the passed document, `pending` included when a decided session
        is reopened. Every director, and every shareholder at or above the
        ownership threshold (10% in NG, GH and KE, 25% elsewhere), must verify.
        The roster person marked `isApplicant` is the applicant: when they are
        one of those, starting the applicant’s session records them as a
        beneficial owner and their pass files its ID evidence on that record
        (once that start is recorded, the pass files on the marked record when
        they are a director or a shareholder), and `personId` naming them
        answers with the application’s own session; the mark is accepted only on
        a row carrying the applicant’s names, and exempts nobody else. A
        dashboard member with the developer role starts the applicant’s session
        only; `personId` answers 403 person_link_role for them. The link is
        valid until the session expires under the verification workflow’s
        expiry. A repeat call while the attempt is pending returns the same
        link, which keeps the `returnUrl` it was started with (for the
        applicant’s session, unless the verification workflow was republished in
        between, when a new session starts); once the attempt is rejected or
        expired, a call starts a new one with a new link. A new attempt replaces
        an earlier one, a `waived` outcome included, which then reads `pending`
        and no longer satisfies POST /v2/applications/{id}/submit until the
        attempt passes. A start is refused (409) while an attempt is still being
        decided by the verification provider or a reviewer, for a person already
        verified, and when the attempt changes while starting. A `verified` or
        `waived` outcome satisfies the identity requirement of POST
        /v2/applications/{id}/submit.
      operationId: ApplicationV2Controller_startIdentityVerification
      parameters:
        - in: path
          name: id
          required: true
          schema:
            format: uuid
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/StartApplicationIdentityVerificationDto'
        required: false
      responses:
        '201':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApplicationIdentityVerificationSessionDto'
          description: ''
        '400':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericErrorResponseDto'
          description: >-
            The application id is not a UUID, `returnUrl` is not an absolute
            `https` address without credentials (invalid_return_url;
            `http://localhost` is accepted outside production), or the identity
            provider refused a request while starting verification, other than a
            rate limit
        '403':
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/GenericErrorResponseDto'
                  - $ref: '#/components/schemas/StepLockedErrorResponseDto'
          description: >-
            The application is back for more information without the step open
            to redo: step_locked (body adds `lockedStep` and `editableSteps`).
            An application that cannot be changed in its status (under review,
            approved or rejected), one belonging to another account, or a
            dashboard session whose workspace role is viewer (Insufficient
            role), returns a generic body; person_link_role is a dashboard
            session with the developer role asking for another person’s link.
        '404':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericErrorResponseDto'
          description: No such application, or no such person on it
        '409':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericErrorResponseDto'
          description: >-
            `error` names the cause: identity_already_verified (the person is
            already verified), identity_verification_in_review (the current
            attempt is still being decided), identity_verification_changed (the
            attempt changed while starting), identity_verification_not_linked
            (the person does not verify from a link: the applicant, who verifies
            in the application’s own session, a shareholder below the ownership
            threshold, or anyone on an application Zuba relies on the customer’s
            own identity checks for) or application_closed (a rejected
            application). Read GET /v2/applications/{id}/identity-verification
            for the current state.
        '429':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericErrorResponseDto'
          description: The identity provider is rate limiting starts; retry in a minute
        '503':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GenericErrorResponseDto'
          description: Identity verification is not available in this environment
      security:
        - bearer: []
components:
  schemas:
    StartApplicationIdentityVerificationDto:
      properties:
        personId:
          description: >-
            A director or shareholder on the application (`persons` on GET
            /v2/applications/{id}/identity-verification) to start the session
            for, so the link is theirs and the outcome and ID evidence file on
            their record. Every director, and every shareholder at or above the
            ownership threshold, has one; the person marked `isApplicant` gets
            the application’s own session. Omit it for the applicant.
          format: uuid
          type: string
        returnUrl:
          description: >-
            Where the person lands after completing the hosted page: an absolute
            `https` address of yours (`http://localhost` is accepted outside
            production). Without it, the person lands on a Zuba page saying the
            check is complete. A session keeps the address it was started with:
            a repeat call while the attempt is pending returns the same link,
            with its original return address. The applicant’s session is the
            exception when the verification workflow was republished in between:
            a new session then starts, with this address.
          example: https://example.com/onboarding/identity-done
          maxLength: 2048
          type: string
      type: object
    ApplicationIdentityVerificationSessionDto:
      properties:
        personId:
          description: >-
            The director or shareholder the session is for, from `persons` on
            GET /v2/applications/{id}/identity-verification; null for the
            application’s own session, the applicant’s.
          format: uuid
          nullable: true
          type: string
        sessionId:
          description: >-
            The attempt's session id. In the sandbox, where starting a session
            verifies the person at once, it is null for the applicant’s session
            and a placeholder for a director’s or shareholder’s.
          nullable: true
          type: string
        status:
          description: >-
            `pending` until the person completes the link and the outcome
            arrives; read it back from GET
            /v2/applications/{id}/identity-verification. `verified` in the
            sandbox, where starting a session verifies the person at once.
          enum:
            - pending
            - verified
          type: string
        submittedAt:
          description: >-
            When the attempt reached its outcome: null while it is pending, set
            in the sandbox, where the attempt is verified at once.
          format: date-time
          nullable: true
          type: string
        url:
          description: >-
            The hosted verification page for this attempt. Send the person to
            it: they complete the identity check there, and the outcome reaches
            Zuba by webhook. Valid until the session expires under the
            verification workflow’s expiry. A repeat call while the attempt is
            pending returns it again. Null in the sandbox, where starting a
            session verifies the person at once.
          nullable: true
          type: string
      required:
        - personId
        - status
        - sessionId
        - submittedAt
        - url
      type: object
    GenericErrorResponseDto:
      properties:
        error:
          description: Error type identifier
          example: BAD_REQUEST
          type: string
        message:
          description: Error message
          example: Bad Request
          type: string
        path:
          description: Request path that generated the error
          example: /v1/resource
          type: string
        statusCode:
          description: HTTP status code
          example: 400
          type: number
        timestamp:
          description: ISO timestamp when the error occurred
          example: '2024-01-15T10:30:00.000Z'
          type: string
      required:
        - statusCode
        - message
        - error
        - timestamp
        - path
      type: object
    StepLockedErrorResponseDto:
      properties:
        editableSteps:
          description: Sections the reviewer did flag for editing
          example:
            - documents
          items:
            type: string
          type: array
        error:
          description: Error type identifier
          example: step_locked
          type: string
        lockedStep:
          description: Section rejected because it was not flagged by the reviewer
          example: business_info
          type: string
        message:
          description: Error message
          example: Bad Request
          type: string
        path:
          description: Request path that generated the error
          example: /v1/resource
          type: string
        statusCode:
          description: HTTP status code
          example: 400
          type: number
        timestamp:
          description: ISO timestamp when the error occurred
          example: '2024-01-15T10:30:00.000Z'
          type: string
      required:
        - statusCode
        - message
        - error
        - timestamp
        - path
        - lockedStep
        - editableSteps
      type: object
  securitySchemes:
    bearer:
      bearerFormat: JWT
      description: Enter Auth0 JWT token
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.