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

# Link to documents

> Link to the files on the record, whoever provided them.

Name the documents you want by id, or ask for the whole record at once. Each link is short-lived and yours to fetch: this call hands back addresses rather than the files themselves, and following one saves the file under the name the record states.

Every entry states the file as the record does, its name, type, size, hash and the slot and kind it was provided as, and names the step it answers with your own label for it. A download of the whole record is a list of files from across it, so what each one is comes with the link to it.

A verification run in one request keeps no files: its documents were read as they arrived and stored nowhere, so asking for the whole record links nothing and naming one of them answers `409`. The record still carries each file's name, type and hash, which is what it was checked as.

Once a verification has expired its files are gone too, so asking for the whole record links nothing and naming an id answers `404`.



## OpenAPI

````yaml /verification/api-reference/openapi.json post /verifications/{verification_id}/documents/download
openapi: 3.1.0
info:
  title: Verify with Privue
  description: >-
    Start identity-and-business verifications and read the results.


    There are three ways to run one, and a verification reads the same whichever
    it was. Every workflow declares which it is for as its `mode`, and a
    workflow is one of them and never more.


    **Hosted.** You create a verification with the user's mobile number and
    receive a journey URL to hand to them. They work through the steps your
    workflow configures, on our screens, and submit. Use this when the person
    being verified is reachable and holds their own documents.


    **Staged.** You create a verification, upload each document you hold against
    the step it answers, and ask for the checks to run. They take longer than a
    request should be held open, so you read the result from a callback or by
    polling. Use this when documents reach you over time, or when you want them
    kept on the record to download later.


    **Instant.** You send the whole submission in one request. The checks run
    before it answers, so the reply is the finished record. The documents are
    read as they arrive and stored nowhere. Use this when you hold everything
    already and want the answer in the call you made.


    Whichever it was, you read the whole record: every step, its state, what it
    collected, and why anything did not pass. An instant verification hands you
    that record in its reply; for the other two, poll the verification or
    register a callback endpoint and be told when it is submitted.


    Across your whole account you can list the workflows configured for you,
    count your verifications by status, and page through the records behind a
    count.


    Every error response has the shape `{"code": "...", "detail": "..."}`.
    Branch on `code`: several causes share a status, and `detail` is written for
    a person reading a log rather than for code. Treat a code you do not
    recognise as the status it arrived at.
  version: 0.1.0
servers:
  - url: https://api.verify.privue.ai
    description: Production
security: []
tags:
  - name: Workflows
    description: >-
      The workflows configured for your account. Read one to see its mode, the
      documents it asks for, and how each step is answered over the API: the
      slots its documents go in and the values it takes.
  - name: Verifications
    description: >-
      Starting a verification, ending one, clearing one to run the same subject
      again, and purging one. Shared by both modes. A start answers 201 when it
      created the verification and 200 when the subject already had one, so a
      duplicate is visible from the status code alone.
  - name: Hosted workflows
    description: >-
      For a workflow the person being verified walks themselves. Start a
      verification with their mobile number, hand them the journey URL on the
      record, or open it for them from inside your own app with a handoff.
  - name: Staged workflows
    description: >-
      For a workflow you build up from documents you already hold. Upload each
      file against the step and slot it answers, withdraw one you got wrong,
      then ask for the checks to run and read the result when they have. Nobody
      is sent anywhere.
  - name: Instant workflows
    description: >-
      For a workflow answered in the request that asks it. Send the whole
      submission at once and read the finished record in the reply. Nothing is
      kept of the documents and nothing is left open to come back to.
  - name: Results
    description: >-
      Reading what a verification came to: one record, a page of them, counts
      across your account, and links to the files it holds.
paths:
  /verifications/{verification_id}/documents/download:
    post:
      tags:
        - Results
      summary: Link to documents
      description: >-
        Link to the files on the record, whoever provided them.


        Name the documents you want by id, or ask for the whole record at once.
        Each link is short-lived and yours to fetch: this call hands back
        addresses rather than the files themselves, and following one saves the
        file under the name the record states.


        Every entry states the file as the record does, its name, type, size,
        hash and the slot and kind it was provided as, and names the step it
        answers with your own label for it. A download of the whole record is a
        list of files from across it, so what each one is comes with the link to
        it.


        A verification run in one request keeps no files: its documents were
        read as they arrived and stored nowhere, so asking for the whole record
        links nothing and naming one of them answers `409`. The record still
        carries each file's name, type and hash, which is what it was checked
        as.


        Once a verification has expired its files are gone too, so asking for
        the whole record links nothing and naming an id answers `404`.
      operationId: >-
        download_documents_verifications__verification_id__documents_download_post
      parameters:
        - name: verification_id
          in: path
          required: true
          schema:
            type: string
            format: uuid
            title: Verification Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentDownloadRequest'
      responses:
        '200':
          description: Every file asked for, with a short-lived download link.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentDownloadResponse'
              example:
                documents:
                  - id: bb9909b4-44e4-414a-af82-bad690d434ed
                    slot: gst-certificate
                    kind: gst-certificate
                    label: gst_certificate
                    filename: gst-certificate.pdf
                    content_type: application/pdf
                    size_bytes: 182044
                    sha256: >-
                      63f90f32e899197edb4f038322e354f3c6b907602a44a58a0ae52b40a2cd9811
                    received_at: '2026-08-10T14:21:58'
                    step_key: gst
                    step_label: tax_registration
                    url: >-
                      https://storage.privue.ai/verification-4b8f21c6-59ad-4a70-9e11-7c3d0a52f8b4/3f2504e0-4f89-41d3-9a0c-0305e82c3301/gst/bb9909b4-44e4-414a-af82-bad690d434ed/gst-certificate.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260810T151244Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=8f0c1d4a7b6e2593c04f1a8d7b3e69a2
                  - id: 4fd79a54-a8a7-4e07-b902-a2e75af3e467
                    slot: pan-card
                    kind: pan-card
                    label: entity_pan
                    filename: pan-card.jpg
                    content_type: image/jpeg
                    size_bytes: 92412
                    sha256: >-
                      42b7e28cf9a26dcef6e275de42f7516ad36ee86c6aa45888821a81217e62a670
                    received_at: '2026-08-10T14:09:12'
                    step_key: pan
                    step_label: entity_pan
                    url: >-
                      https://storage.privue.ai/verification-4b8f21c6-59ad-4a70-9e11-7c3d0a52f8b4/3f2504e0-4f89-41d3-9a0c-0305e82c3301/pan/4fd79a54-a8a7-4e07-b902-a2e75af3e467/pan-card.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20260810T151244Z&X-Amz-Expires=300&X-Amz-SignedHeaders=host&X-Amz-Signature=2d71e4c908ba53f6710d8e2fb945c3a7
                expires_in_seconds: 300
        '401':
          description: |-
            The API key is missing, malformed, or revoked.

            The code is `unauthenticated`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: unauthenticated
                detail: Missing or malformed Authorization header
        '403':
          description: >-
            The workflow belongs to the other environment. An API key is for
            `uat` or for `production` and reaches only that environment's
            workflows. Or the key is valid but not enrolled for verifications,
            or not set up for exactly one of `uat` and `production`, in which
            case ask us to reissue it.


            The code is one of `environment-mismatch` or `client-not-enrolled`.
          content:
            application/json:
              example:
                code: environment-mismatch
                detail: >-
                  workflow 'merchant-onboarding' is production and this request
                  is for uat
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: >-
            No such verification on your account, or one of the ids is not a
            document on it. The detail names every id that was not found.


            The code is one of `document-not-found` or `verification-not-found`.
          content:
            application/json:
              example:
                code: document-not-found
                detail: 'no such document: 4fd79a54-a8a7-4e07-b902-a2e75af3e467'
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The document was read in the request that carried it and was not
            stored, so there is no file to link to.


            The code is `document-not-stored`.
          content:
            application/json:
              example:
                code: document-not-stored
                detail: >-
                  document 4fd79a54-a8a7-4e07-b902-a2e75af3e467 was read in the
                  request that carried it and was not stored
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: >-
            An identifier in the path or body is not a valid UUID, or too many
            documents were asked for.


            The code is `request-invalid`.
          content:
            application/json:
              example:
                code: request-invalid
                detail: >-
                  document_ids.1: Input should be a valid UUID, invalid
                  character: found `n` at 1
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: |-
            The API key is rate limited. Retry after a pause.

            The code is `rate-limited`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: rate-limited
                detail: 'Unauthorized: RATE_LIMITED'
        '503':
          description: |-
            API keys could not be checked right now. Retry.

            The code is `auth-unavailable`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                code: auth-unavailable
                detail: Auth provider unavailable
      security:
        - HTTPBearer: []
components:
  schemas:
    DocumentDownloadRequest:
      properties:
        document_ids:
          anyOf:
            - items:
                type: string
                format: uuid
              type: array
              maxItems: 25
            - type: 'null'
          title: Document Ids
          description: >-
            The documents to link, at most 25 per call. Omit the field to link
            every file on the record; send an empty list to link none. Every id
            must belong to this verification, otherwise the whole call is
            refused and no links are issued.
      additionalProperties: false
      type: object
      title: DocumentDownloadRequest
      description: Which of a verification's files to link.
      examples:
        - document_ids:
            - bb9909b4-44e4-414a-af82-bad690d434ed
            - 4fd79a54-a8a7-4e07-b902-a2e75af3e467
    DocumentDownloadResponse:
      properties:
        documents:
          items:
            $ref: '#/components/schemas/DocumentLink'
          type: array
          title: Documents
          description: >-
            One entry per document, in the order the ids were given, or oldest
            first when the whole record was linked. Each states the file the way
            the record does, names the step it answers, and carries the link to
            it.
        expires_in_seconds:
          type: integer
          title: Expires In Seconds
          description: How long the URLs stay valid; every link shares the window.
      type: object
      required:
        - documents
        - expires_in_seconds
      title: DocumentDownloadResponse
      description: >-
        Short-lived links to the documents that were asked for, all minted
        together.
    ErrorResponse:
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
          description: >-
            What went wrong. Branch on this rather than on the status code,
            which several causes share, and never on `detail`.


            Treat a code you do not recognise as the status it arrived at: codes
            are added as new causes become reachable, and an existing one never
            changes meaning.
        detail:
          type: string
          title: Detail
          description: >-
            What went wrong, in words, naming the workflow, verification, step
            or field at fault where one is. Written for a person reading a log
            rather than for code to read, so it is not stable and nothing should
            be matched against it.
      type: object
      required:
        - code
        - detail
      title: ErrorResponse
      description: The envelope every failure is answered in.
    DocumentLink:
      properties:
        id:
          type: string
          format: uuid
          title: Id
          description: The document's id, used to download it.
        slot:
          type: string
          title: Slot
          description: The named slot the file sits in.
        kind:
          type: string
          title: Kind
          description: >-
            Which of its slot's kinds the file was provided as. Every slot names
            at least one, and `intake.slots` names them for every step answered
            over this API. A file a check fetched carries the kind of document
            the source returned, and a photo taken on the person's own screens
            carries the kind of photo it was asked for.
        label:
          anyOf:
            - type: string
            - type: 'null'
          title: Label
          description: >-
            That kind's name in your own system, where your workflow gives it
            one. The same label on the same document across branches is how you
            find it without knowing which branch ran.
        filename:
          type: string
          title: Filename
          description: The original filename.
        content_type:
          type: string
          title: Content Type
          description: The file's MIME type.
        size_bytes:
          type: integer
          title: Size Bytes
          description: The file's size in bytes.
        sha256:
          type: string
          title: Sha256
          description: The SHA-256 hash of the file's contents.
        received_at:
          type: string
          format: date-time
          title: Received At
          description: When the file was received.
        step_key:
          type: string
          title: Step Key
          description: The step this file answers.
        step_label:
          anyOf:
            - type: string
            - type: 'null'
          title: Step Label
          description: Your own name for that step, where your workflow carries one.
        url:
          type: string
          title: Url
          description: A presigned URL that downloads the file under its original name.
      type: object
      required:
        - id
        - slot
        - kind
        - label
        - filename
        - content_type
        - size_bytes
        - sha256
        - received_at
        - step_key
        - step_label
        - url
      title: DocumentLink
      description: >-
        One file as the record states it, the step it answers, and a short-lived
        link to fetch it.
    ErrorCode:
      type: string
      enum:
        - unauthenticated
        - client-not-enrolled
        - rate-limited
        - auth-unavailable
        - environment-mismatch
        - request-invalid
        - body-unreadable
        - page-cursor-invalid
        - window-invalid
        - request-too-large
        - not-found
        - method-not-allowed
        - internal-error
        - workflow-not-found
        - workflow-version-not-found
        - workflow-not-runnable
        - workflow-not-walkable
        - workflow-misconfigured
        - context-incomplete
        - context-value-invalid
        - mobile-required
        - journey-fields-refused
        - return-url-not-allowed
        - reference-taken
        - verification-not-found
        - verification-not-open
        - verification-expired
        - run-in-progress
        - credits-exhausted
        - resets-exhausted
        - live-limit-reached
        - step-not-found
        - step-not-ready
        - step-invalid
        - document-not-found
        - document-not-stored
        - document-too-large
        - document-type-not-allowed
      title: ErrorCode
      description: >-
        What went wrong, as a value to branch on.


        Published vocabulary, stated here rather than shared with any other
        product of ours: these are read by integrations we do not control, so a
        name here means what it says for as long as the endpoint answering it
        exists.


        One code names one cause, so a status carrying several causes is told
        apart by the code and never by the message. Codes are added as new
        causes become reachable; an integration therefore treats an unrecognised
        code as the status it arrived at.
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer

````

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