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

# Webhooks

> Complete reference for the verification.face-match.completed and verification.id-check.completed webhook payloads

## Payload Structure

When a Face Match or ID Check session completes, Verisoul sends a `POST` request to your configured webhook endpoint. A webhook fires for every session outcome: pass, fail, or abandonment.

<CardGroup cols={2}>
  <Card title="verification.face-match.completed" icon="face-smile">
    Fires when a Face Match session completes
  </Card>

  <Card title="verification.id-check.completed" icon="id-card">
    Fires when an ID Check session completes
  </Card>
</CardGroup>

<Tip>
  **If your flow has a client-side dependency** (the user is waiting in your UI for a pass/fail result), **await the [redirect](/verifications/face-match/integration/navigate-to-facematch#on-completion)**. It fires the moment the session completes.

  **Otherwise, use the webhook.** It typically arrives within a few seconds of session completion, though delivery can slow during high-traffic bursts. It is the better fit for backend record-keeping and async processing.

  You can also use both: await the redirect for the immediate result and treat the webhook as the durable record.
</Tip>

<Tip>
  Every webhook Verisoul sends uses the same signing scheme, including these. See [Webhook Signature Verification](/email-intelligence/webhook-signature-verification) to validate the `x-signature` header before processing a payload.
</Tip>

***

## Example Success Payload: Face Match

On success, `data` is exactly what [`POST /liveness/verify-face`](/api-reference/face-match/verify-face) would return for the session.

```json theme={null}
{
  "request_id": "fd4c7dc1-32f7-4dfb-99c2-a6daafeaad94",
  "session_id": "000575be-fa64-4274-b810-89def4fce2f7",
  "project_id": "ed8fe5d7-ead9-4b81-b1b0-74190582341c",
  "event_type": "verification.face-match.completed",
  "created_at": "2026-08-24T20:43:20.921Z",
  "completed_at": "2026-08-24T20:44:04.246Z",
  "status": "success",
  "data": {
    "metadata": {
      "project_id": "ed8fe5d7-ead9-4b81-b1b0-74190582341c",
      "session_id": "000575be-fa64-4274-b810-89def4fce2f7",
      "account_id": null,
      "referring_session_id": null,
      "request_id": "fd4c7dc1-32f7-4dfb-99c2-a6daafeaad94",
      "timestamp": "2026-08-24T20:43:30.655Z"
    },
    "decision": "Fake",
    "risk_score": 1,
    "risk_flags": ["repeat_face", "repeat_device"],
    "lists": [],
    "rules": [],
    "device_network_signals": {
      "device_risk": 0.4584,
      "proxy": 0.4033,
      "vpn": 0,
      "datacenter": 0,
      "tor": 0,
      "spoofed_ip": 0,
      "recent_fraud_ip": 0,
      "device_network_mismatch": 0.0174,
      "location_spoofing": 0
    },
    "referring_session_signals": {
      "impossible_travel": 0,
      "ip_mismatch": 0,
      "user_agent_mismatch": 0,
      "device_timezone_mismatch": 0,
      "ip_timezone_mismatch": 0
    },
    "photo_urls": {
      "face": "https://storage.googleapis.com/facematch-prod/000575be.../face.jpg?X-Goog-Signature=..."
    },
    "session_data": {
      "true_country_code": "US",
      "network": {
        "ip_address": "203.0.113.42",
        "service_provider": "Google Fiber Inc.",
        "connection_type": "isp"
      },
      "location": {
        "continent": "NA",
        "country_code": "US",
        "state": "Texas",
        "city": "Austin",
        "zip_code": "78701",
        "timezone": "America/Chicago",
        "latitude": 30.2672,
        "longitude": -97.7431
      },
      "browser": {
        "type": "Chrome",
        "version": "150.0.7871.124",
        "language": "en-US",
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
        "timezone": "America/Chicago"
      },
      "device": {
        "category": "desktop",
        "type": "Mac",
        "os": "macOS 26.4.1",
        "cpu_cores": 18,
        "memory": 32,
        "gpu": "ANGLE (Apple, ANGLE Metal Renderer: Apple M5 Pro, Unspecified Version)"
      }
    },
    "matches": {
      "num_accounts_linked": 3,
      "accounts_linked": [
        { "account_id": "acct_sandbox_test", "match_types": ["face", "browser"] },
        { "account_id": "sandbox_test_2", "match_types": ["face", "browser"] },
        { "account_id": "ben-test-1", "match_types": ["face", "browser"] }
      ]
    },
    "video_urls": [
      "https://storage.googleapis.com/facematch-prod/000575be.../session_video_1787604230036.webm?X-Goog-Signature=..."
    ]
  },
  "error": null
}
```

***

## Example Success Payload: ID Check

On success, `data` is exactly what [`POST /liveness/verify-id`](/api-reference/id-check/verify-id) would return for the session. It uses the same shape as Face Match plus `document_signals` and `document_data`; `photo_urls` nests inside `document_data` alongside the ID scan images.

```json theme={null}
{
  "request_id": "dab2f1f9-4b49-4595-9635-64c02228306f",
  "session_id": "00035b3e-5b99-4c40-ad07-178c154018ca",
  "project_id": "ed8fe5d7-ead9-4b81-b1b0-74190582341c",
  "event_type": "verification.id-check.completed",
  "created_at": "2026-08-24T20:45:17.691Z",
  "completed_at": "2026-08-24T20:48:50.489Z",
  "status": "success",
  "data": {
    "metadata": {
      "project_id": "ed8fe5d7-ead9-4b81-b1b0-74190582341c",
      "session_id": "00035b3e-5b99-4c40-ad07-178c154018ca",
      "account_id": null,
      "referring_session_id": null,
      "request_id": "dab2f1f9-4b49-4595-9635-64c02228306f",
      "timestamp": "2026-08-24T20:45:29.923Z"
    },
    "decision": "Fake",
    "risk_score": 1,
    "risk_flags": ["moderate_id_face_match_score", "repeat_face", "repeat_id", "repeat_device"],
    "document_signals": {
      "id_age": 29,
      "face_age_estimate": "over25",
      "id_face_match_score": 0.45,
      "id_barcode_status": "success",
      "id_face_status": "likely_original_face",
      "id_text_status": "likely_original_text",
      "is_id_digital_spoof": "likely_physical_id",
      "is_full_id_captured": "full_id_detected",
      "id_validity": "likely_authentic_id"
    },
    "device_network_signals": {
      "ip_document_country_mismatch": false,
      "device_risk": false,
      "proxy": false,
      "vpn": false,
      "datacenter": false,
      "tor": false,
      "spoofed_ip": false,
      "recent_fraud_ip": false,
      "device_network_mismatch": false
    },
    "referring_session_signals": {
      "impossible_travel": false,
      "ip_mismatch": false,
      "user_agent_mismatch": false
    },
    "document_data": {
      "template_info": {
        "document_country_code": "US",
        "document_country": "United States",
        "document_state": "Massachusetts",
        "template_type": "driver_license"
      },
      "user_data": {
        "first_name": "Jane",
        "last_name": "Doe",
        "date_of_birth": "01/15/1995",
        "date_of_expiration": "01/15/2029",
        "date_of_issue": "01/10/2024",
        "id_number": "X12345678",
        "id_number2": null,
        "address": {
          "city": "Boston",
          "country": "US",
          "postal_code": "02108",
          "state": "MA",
          "street": "1 Main St"
        }
      },
      "photo_urls": {
        "face": "https://storage.googleapis.com/facematch-prod/00035b3e.../face.jpg?X-Goog-Signature=...",
        "id_scan_back": "https://storage.googleapis.com/facematch-prod/00035b3e.../id_scan_back.jpg?X-Goog-Signature=...",
        "id_scan_front": "https://storage.googleapis.com/facematch-prod/00035b3e.../id_scan_front.jpg?X-Goog-Signature=..."
      }
    },
    "session_data": {
      "network": {
        "ip_address": "203.0.113.42",
        "service_provider": "Google Fiber Inc.",
        "connection_type": "isp"
      },
      "location": {
        "continent": "NA",
        "country_code": "US",
        "state": "Texas",
        "city": "Austin",
        "zip_code": "78701",
        "timezone": "America/Chicago",
        "latitude": 30.2672,
        "longitude": -97.7431
      },
      "browser": {
        "type": "Chrome",
        "version": "150.0.7871.124",
        "language": "en-US",
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
        "timezone": "America/Chicago"
      },
      "device": {
        "category": "desktop",
        "type": "Mac",
        "os": "macOS 26.4.1",
        "cpu_cores": 18,
        "memory": 32,
        "gpu": "ANGLE (Apple, ANGLE Metal Renderer: Apple M5 Pro, Unspecified Version)"
      }
    },
    "matches": {
      "num_accounts_linked": 3,
      "accounts_linked": [
        { "account_id": "acct_sandbox_test", "match_types": ["face", "browser"] },
        { "account_id": "sandbox_test_2", "match_types": ["face", "browser", "document_number", "document_name"] },
        { "account_id": "ben-test-1", "match_types": ["face", "browser", "document_number"] }
      ]
    },
    "video_urls": [
      "https://storage.googleapis.com/facematch-prod/00035b3e.../session_video_1787604461595.webm?X-Goog-Signature=...",
      "https://storage.googleapis.com/facematch-prod/00035b3e.../session_video_1787604521797.webm?X-Goog-Signature=..."
    ]
  },
  "error": null
}
```

<Warning>
  Success payloads contain PII (name, date of birth, ID number, address for ID Check) and signed, time-limited URLs to photo/video evidence. URLs are signed at the moment the webhook is sent, so their expiration window starts when you receive them and lasts for 60 minutes. Treat the payload itself as sensitive data.
</Warning>

***

## Example Error Payload

```json theme={null}
{
  "request_id": "67c334e0-b01c-427d-8425-d32c144a2e52",
  "session_id": "00058a40-0b02-431f-9115-ee9983c10e13",
  "project_id": "ed8fe5d7-ead9-4b81-b1b0-74190582341c",
  "event_type": "verification.face-match.completed",
  "created_at": "2026-08-18T20:13:26.762Z",
  "completed_at": "2026-08-18T20:15:44.799Z",
  "status": "error",
  "data": null,
  "error": {
    "code": "face_match_failed",
    "message": "The user failed the face match check after the maximum number of attempts"
  }
}
```

***

## Field Reference

### Top-Level Fields

| Field          | Type           | Description                                                                                                           |
| -------------- | -------------- | --------------------------------------------------------------------------------------------------------------------- |
| `request_id`   | string         | Unique identifier for this webhook. On success, matches `data.metadata.request_id`.                                   |
| `session_id`   | string         | The verification session ID. Present on both success and error, so use it to correlate the webhook with your session. |
| `project_id`   | string         | Your Verisoul project ID                                                                                              |
| `event_type`   | string         | `verification.face-match.completed` or `verification.id-check.completed`                                              |
| `created_at`   | string         | When the verification session was created (ISO 8601)                                                                  |
| `completed_at` | string         | When the session completed (ISO 8601)                                                                                 |
| `status`       | string         | `success` or `error`                                                                                                  |
| `data`         | object \| null | The full verification result (present on success)                                                                     |
| `error`        | object \| null | Error details (present on failure)                                                                                    |

### `data`

The full verification result, identical to the `verify-face` or `verify-id` API response. For field-by-field definitions, see:

* [Face Match Response Signals](/verifications/face-match/integration/response-signals)
* [ID Check Response Signals](/verifications/id-check/integration/response-signals)

### `error`

| Field     | Type   | Description                                        |
| --------- | ------ | -------------------------------------------------- |
| `code`    | string | Error code (see [Error Codes](#error-codes) below) |
| `message` | string | Human-readable description of the failure          |

***

## Error Codes

| Code                            | Meaning                                                                                                                                                                                     |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `liveness_check_failed`         | The session ended in a non-success state, for example the user cancelled a step.                                                                                                            |
| `face_match_failed`             | The face match/liveness step reached the maximum number of attempts without succeeding.                                                                                                     |
| `failed_to_process_id_scan`     | The identity document scan could not be processed (ID Check only).                                                                                                                          |
| `failed_to_collect_device_data` | Required device or network data could not be collected from the user's browser.                                                                                                             |
| `max_duration_exceeded`         | The session was never completed and can no longer be completed. Sessions stay completable for 30 days after creation, so this can arrive well after the user last interacted with the flow. |

<Note>
  The redirect's `error_message` vocabulary is broader than this list. `invalid_session_id` and `session_id_not_found` appear only in a [redirect](/verifications/face-match/integration/navigate-to-facematch#error-handling); a webhook fires only for sessions that were found and valid.
</Note>

***

## Delivery Guarantees

* **Exactly once per session.** Each session fires exactly one webhook, for whichever terminal state it reaches first. This holds even if the user abandons the session or `verify-face`/`verify-id` is never called.
* **Signed.** Every payload includes an `x-signature` header. See [Webhook Signature Verification](/email-intelligence/webhook-signature-verification) for how to verify it.
* **Retried with backoff** if your endpoint returns a non-2xx response.
