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

# Capture in your own UI

> Build the capture screens yourself and drive the verification through the API, step by step.

Use this when you capture the selfie and the documents in your own app instead of ProofAge's hosted widget. This page is the order of the requests; [Consent](/integration/web/consent) and [Uploading media](/integration/web/uploading-media) have the detail of each.

Every request is signed with your secret key (see [API authentication](/getting-started/api-authentication)), so they all run on your backend: your app sends the images to your backend, which forwards them.

## Before you build it

The hosted widget does a lot that you would take over:

* the consent screen and the consent text in the person's language, in 28 languages;
* camera access, including recovery when the browser or an in-app browser blocks it;
* capture guidance: framing, light, the head movement for [liveness](/core-technology/liveness), and retakes;
* moving from a desktop to a phone by QR code.

Build your own UI when you need capture inside a native app or a flow the widget cannot fit. Otherwise use the [Browser SDK](/integration/web/browser-sdk) or a [redirect](/integration/web/redirect).

## The requests, in order

<Steps>
  <Step title="Create the verification">
    ```bash theme={null}
    POST /v1/verifications
    {"external_id": "user_12345"}
    ```

    Returns the verification in `created` status with its `id`. The check comes from the workspace. See the [Quick start](/getting-started/quick-start).
  </Step>

  <Step title="Record consent">
    ```bash theme={null}
    GET /v1/consent
    POST /v1/verifications/{id}/consent
    {"consent_version_id": 3, "text_sha256": "b5e50967…"}
    ```

    Show the person the consent page at the `url` that `GET /v1/consent` returns, and record their acceptance with its `id` and `text_sha256`. Uploads answer `403 CONSENT_REQUIRED` until you do. See [Consent](/integration/web/consent).
  </Step>

  <Step title="Upload the images">
    ```bash theme={null}
    POST /v1/verifications/{id}/media        (multipart/form-data)
    type=selfie, file=@selfie.jpg
    type=document, side=front, document=passport, file=@front.jpg
    ```

    | Workspace type | Images needed before submit |
    | - | - |
    | Identity (KYC) | A selfie, the document front, and the document back unless the document is a passport. |
    | Age verification by ID document | The same as Identity (KYC). |
    | Facial age estimation | A selfie. If the estimate cannot confirm the age, the verification moves to `documents_required` and also needs the document front, and the back unless it is a passport. |

    Each image is checked as it arrives; an unusable one is refused with `422` and a code that says what to retake. The signature for a file upload covers the form fields and a hash of each file: see [Uploading media](/integration/web/uploading-media).
  </Step>

  <Step title="Submit">
    ```bash theme={null}
    POST /v1/verifications/{id}/submit
    ```

    Answers `200` with an empty body, and the verification moves to `submitted`. If an image is missing it answers `422 MISSING_REQUIRED_MEDIA`. In a test workspace the verification goes to `review` and waits for you to set the outcome: see [Sandbox and test data](/integration/sandbox-testing).
  </Step>

  <Step title="Receive the decision">
    A [webhook](/integration/webhooks) arrives when the verification reaches `approved`, `declined`, `resubmission_requested` or `review` (and later `abandoned` or `expired` if the person never finishes).

    On `resubmission_requested`, the person retakes what failed in a new attempt of the same verification: repeat steps 3 and 4. Attempts are limited; when they run out the verification is `declined`, and `reason` is the last attempt's reason. Treat `declined` as final whatever the code. See [Handle a resubmission](/recipes/handle-resubmission).
  </Step>

  <Step title="Read the result">
    ```bash theme={null}
    GET /v1/verifications/{id}
    GET /v1/verifications/{id}/document
    GET /v1/verifications/{id}/estimation
    GET /v1/verifications/{id}/media/{media}
    ```

    See [Reading results](/integration/retrieving-results).
  </Step>
</Steps>

## The same calls in the SDKs

| Step | Node.js | Python | PHP and Laravel |
| - | - | - | - |
| Create | `client.verifications().create(…)` | `client.verifications.create(…)` | `verifications()->create([…])` |
| Consent text | `client.workspace().getConsent()` | `client.workspace.consent()` | `workspace()->getConsent()` |
| Accept consent | `client.verifications(id).acceptConsent(…)` | `client.verifications.accept_consent(id, …)` | `verifications($id)->acceptConsent([…])` |
| Upload | `client.verifications(id).uploadMedia(…)` | `client.verifications.upload_media(id, …)` | `verifications($id)->uploadMedia([…])` |
| Submit | `client.verifications(id).submit()` | `client.verifications.submit(id)` | `verifications($id)->submit()` |
| Read | `client.verifications(id).get()` | `client.verifications.get(id)` | `verifications($id)->get()` / Laravel `verifications()->find($id)` |

The SDKs sign every request, including the multipart uploads. See [SDKs and integrations](/integration/sdks).


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