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

# GET /v3/confirmations/{token}

> Exchange a return token for the authoritative verification result.

When a user returns from the hosted flow, they carry a `vyt` **token**. `GET /v3/confirmations/{token}` exchanges that token for the **authoritative** result. This is the check that matters: run it on your backend with your secret key before you grant access. The `vyc` value the client sees is only a UI hint.

```http theme={null}
GET https://trust.verifyyou.com/v3/confirmations/{token}
Authorization: Bearer sk_…
```

<Warning>
  **Secret key only.** Never confirm from the browser, that would expose your
  secret key. Use the secret key from the same pair as the publishable key that
  started the flow: test with test, live with live.
</Warning>

<Tip>
  This page is the guided walkthrough. To see every endpoint and **try it out**
  against the live API, use the [API Reference](/api-reference). It is generated
  from the same spec, so it never drifts.
</Tip>

## Path parameter

<ParamField path="token" type="string" required>
  The `vyt` confirmation token the user returned with after verifying. Read it
  off the URL or the embedded flow's result with [`vyget()`](/v3/sdk/vyget).
</ParamField>

## Response

<ResponseField name="verified" type="boolean">
  Authoritative result. `true` only if the person passed the
  [liveness and uniqueness](/v3/concepts/humanness-and-uniqueness) checks and the
  confirmation isn't locked. **Gate access on this field.**
</ResponseField>

<ResponseField name="id" type="string | null">
  The confirmation id for this person's pass through the verification.
</ResponseField>

<ResponseField name="status" type="string | null">
  Verdict detail: `pending`, `approved`, `flagged`, or `denied`. `flagged`
  means the check passed but carries findings for you to review.
</ResponseField>

<ResponseField name="reasons" type="string[]">
  The rule findings behind the status, for example `collision` when the same
  face was seen under someone else, or `identity_mismatch`. Empty on a clean
  pass.
</ResponseField>

<ResponseField name="confirmed_at" type="string | null">
  When the latest settled verdict landed, as an ISO timestamp.
</ResponseField>

<ResponseField name="identity" type="object | null">
  The email or phone the flow verified, present only when the verification's
  identity configuration has sharing enabled in the dashboard.

  <Expandable title="identity">
    <ResponseField name="email" type="string | null">
      The verified email, if one was collected.
    </ResponseField>

    <ResponseField name="phone" type="string | null">
      The verified phone, if one was collected.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="external_id" type="string | null">
  The `external_id` attached to the session at [initialize](/v3/api/initialize), if any.
</ResponseField>

<ResponseField name="verification" type="object">
  A reference to the verification this confirmation belongs to.

  <Expandable title="verification">
    <ResponseField name="external_id" type="string | null">
      Your external id for the verification, if one was set.
    </ResponseField>

    <ResponseField name="external_tenant_id" type="string | null">
      Your external tenant id for the verification, if one was set.
    </ResponseField>
  </Expandable>
</ResponseField>

```json Response theme={null}
{
  "verified": true,
  "id": "cnf_9f3eb5",
  "status": "approved",
  "reasons": [],
  "confirmed_at": "2026-07-07T22:32:12Z",
  "identity": null,
  "external_id": "user_123",
  "verification": {
    "external_id": "signup-flow",
    "external_tenant_id": null
  }
}
```

## Example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://trust.verifyyou.com/v3/confirmations/<token> \
    -H "Authorization: Bearer sk_test_…"
  # => { "verified": true, "status": "approved", ... }
  ```

  ```ts Node theme={null}
  const res = await fetch(
    `https://trust.verifyyou.com/v3/confirmations/${token}`,
    { headers: { Authorization: `Bearer ${process.env.VY_SK}` } },
  );
  const { verified } = await res.json();
  if (verified) {
    // grant access
  }
  ```
</CodeGroup>

Confirm each token exactly once. If your framework can double-fire the confirming code (React StrictMode does this in development), add a guard so the same token isn't confirmed twice.

## Errors

Errors return the matching HTTP status and a JSON body with a `detail` code:

```json theme={null}
{ "detail": "invalid_confirmation_token" }
```

| Status | `detail`                     | Cause                                                                                    |
| ------ | ---------------------------- | ---------------------------------------------------------------------------------------- |
| `401`  | `invalid_key_type`           | Not a secret key. Confirmations require the secret key, not the publishable one.         |
| `401`  | `invalid_or_revoked_key`     | The secret key is unknown or has been revoked.                                           |
| `401`  | `invalid_confirmation_token` | The `token` is malformed or can't be verified.                                           |
| `404`  | `not found`                  | No confirmation for this token, or it belongs to a different key or mode (test vs live). |

A declined or locked confirmation is **not** an error. It returns `200` with `verified: false`.

## Lock a confirmation (optional)

For true one-shot flows, where each person should pass exactly once and never again, **lock** the confirmation once you've accepted it. After locking, `GET /v3/confirmations/{token}` returns `verified: false` for that confirmation.

```http theme={null}
POST https://trust.verifyyou.com/v3/confirmations/{token}/lock
Authorization: Bearer sk_…
```

<ResponseField name="locked" type="boolean">
  `true` once the confirmation is locked. A locked confirmation can't be
  confirmed again.
</ResponseField>

```json Response theme={null}
{ "locked": true }
```

<Warning>
  Locking applies to the **person's confirmation** on this verification, not
  just the token in your hand. Once locked, that person's future passes come
  back denied too. Use it only when one pass per person, ever, is exactly what
  you want. For caps like once per week or once per event, configure a
  verification limit in the dashboard instead. And don't lock while you're
  testing, or you will deny your own test user on every retry.
</Warning>
