> ## 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.

# Data models

> The verification object and the other objects the API and webhooks return, field by field.

The objects below are what the API returns, without an envelope. New fields can be added within `v1`; ignore the ones you do not know. The event sent to your webhook URL is described in [Decision webhook](/api-reference/webhook-decision).

## Verification

Returned by `POST /verifications` and `GET /verifications/{verification}`.

<ResponseField name="id" type="string (uuid)">
  The verification ID. Keep it next to your user.
</ResponseField>

<ResponseField name="external_id" type="string | null">
  Your identifier for the person, as sent on create. `null` when the create request was not signed.
</ResponseField>

<ResponseField name="external_metadata" type="object | null">
  The metadata you sent on create, returned as sent.
</ResponseField>

<ResponseField name="redirect_url" type="string | null">
  Where the person's browser goes after the final screen: the `callback_url` of this verification, or the workspace's redirect URL.
</ResponseField>

<ResponseField name="status" type="string">
  One of `created`, `started`, `documents_required`, `submitted`, `review`, `resubmission_requested`, `approved`, `declined`, `abandoned`, `expired`. See [Verification statuses](/integration/verification-statuses).
</ResponseField>

<ResponseField name="reason" type="string | null">
  The [decision reason](/core-technology/decision-reasons) when the status is `declined` or `resubmission_requested`; `null` otherwise, and also `null` while the status is `documents_required`. When attempts run out, a `declined` verification carries the last attempt's reason.
</ResponseField>

<ResponseField name="duplicate_check" type="object">
  The result of [duplicate detection](/core-technology/duplicate-detection).

  <Expandable title="properties">
    <ResponseField name="checked" type="boolean">`false` until the check has run, and on workspaces where it does not run.</ResponseField>
    <ResponseField name="duplicate_count" type="integer">How many other accounts (distinct `external_id`s) in the workspace show the same face.</ResponseField>

    <ResponseField name="duplicates" type="object[]">
      <Expandable title="properties">
        <ResponseField name="verification_id" type="string (uuid)">The earlier verification.</ResponseField>
        <ResponseField name="external_id" type="string | null">Its `external_id`.</ResponseField>
        <ResponseField name="similarity_score" type="number">How similar the two faces are; higher is more similar.</ResponseField>
        <ResponseField name="verified_at" type="string | null">When the earlier verification's identity was recorded.</ResponseField>
      </Expandable>
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="erasure" type="object | null">
  `null` unless the verification's personal data was erased on request. Empty fields then mean "erased", not "never captured". The scheduled deletion of images after the retention period does not set it: those images just have a `null` `url`. See [Data retention and erasure](/console/data-retention).

  <Expandable title="properties">
    <ResponseField name="erased_at" type="string">When the data was erased.</ResponseField>
    <ResponseField name="scope" type="string">`personal_data`: media, document fields and analysis results are gone; the status, reason, dates and audit trail remain.</ResponseField>
    <ResponseField name="reason" type="string | null">`data_subject_request`, `customer_request`, `retention_policy`, `test_data` or `other`.</ResponseField>
    <ResponseField name="requested_via" type="string | null">`customer` (your team, in the console or the MCP) or `proofage` (ProofAge staff).</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="consent_accepted_at" type="string | null">
  When the person accepted the consent text.
</ResponseField>

<ResponseField name="created_at" type="string">
  When the verification was created.
</ResponseField>

<ResponseField name="updated_at" type="string">
  When it last changed.
</ResponseField>

<ResponseField name="url" type="string">
  Returned by `POST /verifications` only: the link that opens the verification for the person.
</ResponseField>

## Document result

Returned by `GET /verifications/{verification}/document`.

<ResponseField name="document.type" type="string | null">
  `passport`, `id`, `driver_license`, `residence_permit` or `other`; `null` when the type could not be determined. An open enum: handle values you do not know. `other` is a readable document that is not an identity card, passport, driving licence or residence permit, such as a health insurance card. `id` is the same value the upload endpoint takes.
</ResponseField>

<ResponseField name="document.issuing_country" type="string | null">
  The issuing country, ISO 3166-1 alpha-2 (`XK` for Kosovo); `null` when it could not be read. Present on every workspace.
</ResponseField>

<ResponseField name="document.issuing_subdivision" type="string | null">
  The state or province that issued the document, as a bare code beside `issuing_country` (for example `FL` with `US`); `null` when unknown or when `issuing_country` is `null`. Today it is filled for US driving licences and ID cards. Present on every workspace and kept after erasure.
</ResponseField>

<ResponseField name="document.fields" type="object">
  What was read from the identity document. Each field is `null` when it was not read or is not printed on the document. Dates are `YYYY-MM-DD`. Age workspaces receive only `first_name`, `last_name`, `date_of_birth` and `document_number`; the seven fields marked KYC workspaces only are absent there.

  <Expandable title="properties">
    <ResponseField name="first_name" type="string | null" />

    <ResponseField name="middle_name" type="string | null">KYC workspaces only.</ResponseField>

    <ResponseField name="last_name" type="string | null" />

    <ResponseField name="date_of_birth" type="string (date) | null">`YYYY-MM-DD`. A partially printed date is `null`, never rounded.</ResponseField>
    <ResponseField name="gender" type="string | null">KYC workspaces only. `F`, `M` or `X` (an open enum). `X` means the document states that the sex is unspecified. The German identity card prints no sex. `null` on a Mexican voter card or driving licence created before 20 August 2026 17:00 UTC.</ResponseField>
    <ResponseField name="nationality" type="string | null">KYC workspaces only. ISO 3166-1 alpha-2 (`XK` for Kosovo), published only when the document itself states it. Can differ from `issuing_country`, for example on a residence permit.</ResponseField>
    <ResponseField name="place_of_birth" type="string | null">KYC workspaces only. The printed text, not normalised.</ResponseField>
    <ResponseField name="address" type="string | null">KYC workspaces only. The printed text as read: trimmed, `null` when empty, not parsed and not normalised. It may contain line breaks.</ResponseField>

    <ResponseField name="document_number" type="string | null" />

    <ResponseField name="issue_date" type="string (date) | null">KYC workspaces only. `YYYY-MM-DD`; a partially printed date is `null`.</ResponseField>
    <ResponseField name="expiry_date" type="string (date) | null">KYC workspaces only. `YYYY-MM-DD`. A document that prints only the month or year reports the last day of that period. A permanent document (for example `PERMANENTE` or `INDEFINIDA`) is `null`.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="media" type="object[]">
  The images of the attempt the decision is based on, selfie first, then the document front and back.

  <Expandable title="properties">
    <ResponseField name="id" type="string (uuid)" />

    <ResponseField name="type" type="string">`selfie`, `document_front` or `document_back`.</ResponseField>
    <ResponseField name="url" type="string | null">An API URL to download the image with a signed request; `null` once the image has been deleted.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="meta.attempt_id" type="string | null">
  The attempt these results come from. A verification can have several attempts when the person is asked to try again.
</ResponseField>

## Age estimation result

Returned by `GET /verifications/{verification}/estimation`.

<ResponseField name="verification_id" type="string (uuid)" />

<ResponseField name="attempt_id" type="string | null">
  The attempt the estimate comes from.
</ResponseField>

<ResponseField name="age_threshold" type="object">
  <Expandable title="properties">
    <ResponseField name="minimum" type="integer | null">The workspace's minimum age.</ResponseField>
    <ResponseField name="passed" type="boolean | null">Whether the person was estimated to be at or above it; `null` before the estimate exists.</ResponseField>
    <ResponseField name="confidence" type="number | null">How confident the estimate is in that answer, from 0 to 1.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="gender" type="object | null">
  <Expandable title="properties">
    <ResponseField name="value" type="integer | null">`0` female, `1` male, `null` unknown.</ResponseField>
    <ResponseField name="confidence" type="number | null">From 0 to 1.</ResponseField>
  </Expandable>
</ResponseField>

## Workspace

Returned by `GET /workspace`: the configuration of the workspace the public key belongs to.

<ResponseField name="id" type="string (uuid)" />

<ResponseField name="name" type="string" />

<ResponseField name="flow_type" type="string">`kyc` or `age`.</ResponseField>
<ResponseField name="mode" type="string">`test` or `live`.</ResponseField>
<ResponseField name="age_mode" type="string | null">On `age` workspaces: `estimation` (Facial age estimation) or `document_verification` (Age verification by ID document).</ResponseField>
<ResponseField name="age_threshold" type="integer | null">On `age` workspaces: the minimum age.</ResponseField>
<ResponseField name="verification_type" type="string">What the person is asked for by default: `selfie` or `documents_and_liveness`.</ResponseField>
<ResponseField name="redirect_url" type="string | null">The default redirect URL after the final screen.</ResponseField>
<ResponseField name="webhook_url" type="string | null">Where decision webhooks are sent: a public `http` or `https` URL, see [the webhook URL](/integration/webhooks#the-webhook-url).</ResponseField>
<ResponseField name="allow_expired_documents" type="boolean">Whether expired identity documents are accepted.</ResponseField>
<ResponseField name="allow_duplicate_accounts" type="boolean">Whether one face may verify under several `external_id`s.</ResponseField>


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