Skip to main content
In a nutshell: Webhooks allow you to set up a notification system that can be used to receive updates on certain requests made to the Adhere API.
Adhere uses webhooks to notify your application when specific events occur. This allows you to build automated workflows and integrate Adhere closely with your system.

Why use Webhooks?

Generally, when you make a request to an API endpoint, you expect to get a near-immediate response. However, some requests may take a long time to process. In order to prevent a timeout error, a pending response is returned. Since your records need to be updated with the final state of the request, you need to either:
  1. Polling: Make a request for an update at regular intervals.
  2. Webhooks: Listen to events by using a webhook URL.
We recommend using webhooks over polling. Webhooks are more efficient, reduce network overhead, and ensure your system is updated immediately when an event occurs.

Setup & Integration

To start receiving webhook notifications, follow these steps to configure your environment:
1

Configure Webhook URL

Provide the endpoint on your server where Adhere will send POST requests. This should be a publicly accessible URL.
2

Set Hash Key

A secret key used to sign the webhook payload. You must keep this secure and use it to verify that requests are coming from Adhere.
3

Save Configuration

Save your settings in the Adhere dashboard under the Integrations section.
There are two independent webhook channels, each with its own URL and its own signing secret:
  • Module events (transaction monitoring, KYC) go to the URL set under Integrations, and are signed with the Hash Key you set there.
  • Identity verification (liveness.completed, sent for every Web, Android and iOS SDK run) goes to the webhook URL set on your SDK Config, and is signed with that config’s own webhook secret. It is generated for you, not set by you.
Both use the same X-Adhere-Signature header and the same HMAC-SHA256 scheme described below.

Security & Verification

All webhook requests from Adhere include an X-Adhere-Signature header in the form sha256=<signature>, where the signature is the lowercase hex HMAC-SHA256 digest of the request body.

Verifying Signatures

To ensure that a webhook request is genuinely from Adhere, you should verify the signature before processing the payload.
Compute the HMAC over the raw request body bytes, exactly as received. Adhere signs a compact JSON serialisation with no whitespace between separators, so re-serialising a parsed payload produces different bytes and the signature will never match.
For liveness.completed, the signing key is your SDK Config’s webhook secret with the dashes removed — a 32-character hex string, not the dashed UUID form. The examples below strip them for you.

Getting your SDK Config’s webhook secret

The secret is generated for you when the SDK Config is created. Get it from the Adhere Dashboard: go to Settings → Integrations → SDK, open the menu on your config’s row and choose Integration. The secret is shown there with your Config ID, with a button to copy it. Treat it like a password. Anyone holding it can forge a validly signed webhook carrying fabricated verification results, so store it the way you store any other server-side credential and never ship it in client code.
Always verify the X-Adhere-Signature header to prevent unauthorized requests from interacting with your server.

Event Payload Structure

Module events follow a consistent JSON structure: Identity verification does not use this envelope. Its payload is flat, with the verification fields at the top level.

Supported Events

Transaction Monitoring

  • Module: transaction_monitoring
  • Events:
    • suspicious_transaction: Triggered when a transaction is processed and deemed suspicious.

Identity Verification

  • Sent by: every Web, Android and iOS SDK verification
  • Delivered to: the webhook URL on your SDK Config (not the Integrations URL)
  • Events:
    • liveness.completed: fired once per verification, after face matching and, for document runs, OCR have finished.
This payload is flat — there is no success / module / data wrapper.
verification_type is data_verification or document_verification. For a document run the same top-level shape applies, with a document block (OCR fields plus a document-to-selfie face match) in place of customer_profile:
document also carries place_of_birth, place_of_issue, address, district, division, location, sub_location, serial_number, and barcode_numbernull unless the specific document type carries that field. face_match.reason is populated with a user-facing explanation when verified is false or the match was skipped.
face_match.decision ("MATCH", "NO_MATCH", or "REJECTED") is what verified is actually derived from — read decision rather than comparing confidence_percentage against a threshold of your own, since our internal match threshold isn’t part of this payload and may change over time. "REJECTED" means liveness/anti-spoofing failed before any comparison ran, so confidence_percentage can be absent or 0 alongside verified: false for a reason unrelated to how similar the faces looked.
status: "passed" means the check ran to completion — not that the person matched. A face mismatch, a low confidence score, or an expired document still reports status: "passed", with the real outcome recorded in biometrics.face_match.verified (and document.is_expired for document verification). status: "failed" is reserved for cases where the check itself could not run: a service error, no selfie captured, or a government database rejection. Never gate access on status alone — always check face_match.verified.
verification_id matches the entryId returned by the SDK’s completion callback.

Testing Locally

Before deploying to production, we recommend testing your webhook implementation locally.
  1. Use ngrok: Use ngrok to create a secure tunnel to your local server.
  2. Set Webhook URL: Update your Adhere dashboard with the ngrok URL (e.g., https://your-subdomain.ngrok-free.app/webhooks).
  3. Inspect Requests: Use the ngrok dashboard or Webhook.site to inspect the payloads and headers sent by Adhere.

Best Practices

  • Acknowledge Immediately: Your server should return a 200 OK response as quickly as possible. Heavy processing should be handled asynchronously using a task queue.
  • Handle Retries: Adhere will retry failed webhook deliveries (non-2xx responses) up to 3 times, with an exponential backoff that caps at 10 minutes.
  • Use Idempotency: Ensure your system can handle the same webhook multiple times safely. There is no separate delivery ID — de-duplicate on the resource identifier in the payload (verification_id for liveness.completed, data.id for module events).
If your server does not return a 2xx response, Adhere will consider the delivery as failed and will attempt to retry.