# Guide to AiPrise Webhooks: Features and Best Practices

AiPrise webhooks (callbacks) notify your systems about identity and business-verification outcomes: a verification is approved, declined, or flagged for review. If you're building KYB/KYC on AiPrise, callbacks are how you react to verification results without polling.

This guide covers how AiPrise callbacks work, how to verify the `X-HMAC-SIGNATURE` (with an unusual key), the outcome model (there are no typed events), and the best practices for production.

## What are AiPrise webhooks?

AiPrise callbacks are HTTP POSTs delivered to a callback URL, each signed with an HMAC-SHA256 in the `X-HMAC-SIGNATURE` header (lowercase hex). The unusual detail: the HMAC key is your AiPrise API private key directly, there's no separate signing or endpoint secret.

There's also no rich typed-event system, the callback outcome is the `verification_result` value.

## AiPrise webhook features

| Feature | Details |
| --- | --- |
| Configuration | Callback URL at template level (Dashboard > View Templates), or per-request `callback_url` / `events_callback_url` |
| Signature header | `X-HMAC-SIGNATURE` (lowercase hex) |
| Signature scheme | HMAC-SHA256 over the raw body |
| Key | Your AiPrise API private key directly (no separate secret) |
| Outcome | `aiprise_summary.verification_result`: `APPROVED` / `DECLINED` / `REVIEW` / `UNKNOWN` |
| Correlation | `verification_session_id` (+ optional `client_reference_id`) |
| Retries | Not documented |
| SDK | Client/mobile SDKs only; no server verification SDK |

## The outcome model

There are no discrete event names. A verification callback carries `aiprise_summary.verification_result` plus a process status:

| verification_result | Meaning |
| --- | --- |
| `APPROVED` | Verification passed |
| `DECLINED` | Verification failed |
| `REVIEW` | Needs manual review |
| `UNKNOWN` | Indeterminate |

The process status (`COMPLETED`, `PENDING`, `FAILED`, ...) tells you where the verification is. Correlate callbacks via `verification_session_id` and your optional `client_reference_id`.

## Setting up AiPrise webhooks

Set the callback URL at the template level (Dashboard > View Templates > {TemplateID}), or override per request via `callback_url` (verification result) and `events_callback_url` (business-profile change events). Your AiPrise API private key is the HMAC key, there's no separate secret to configure.

## Securing AiPrise webhooks

The `X-HMAC-SIGNATURE` header is a lowercase-hex HMAC-SHA256 of the raw request body, keyed with your AiPrise API private key. Compute over the exact raw bytes and compare in constant time.

```javascript
const crypto = require("crypto");

// The HMAC key is your AiPrise API private key (no separate webhook secret)
const API_PRIVATE_KEY = process.env.AIPRISE_API_PRIVATE_KEY;

function verify(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", API_PRIVATE_KEY)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-hmac-signature"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge fast
  const body = JSON.parse(req.body);
  processQueue.add({
    sessionId: body.verification_session_id,
    result: body.aiprise_summary?.verification_result,
  });
});

```

The same check in Python:

```python
import hashlib
import hmac
import os

API_PRIVATE_KEY = os.environ["AIPRISE_API_PRIVATE_KEY"].encode()

def verify(raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(API_PRIVATE_KEY, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

```

## AiPrise webhook limitations and pain points

### The HMAC key is your API private key

The Problem: There's no separate webhook secret, the HMAC is keyed with your AiPrise API private key. That couples callback verification to a broadly-scoped credential, and rotating the API key also rotates webhook signing.

Why It Happens: AiPrise keys callbacks on the API private key rather than issuing a dedicated secret.

Workarounds:

* Use the API private key as the HMAC key, store it securely, and account for the coupling when rotating.

How Hookdeck Can Help: Hookdeck verifies the `X-HMAC-SIGNATURE` at the edge, so the API private key lives in one place rather than in every handler.

### No typed events, the result is the outcome

The Problem: There's no event-name system; you read `aiprise_summary.verification_result`. Handlers built to switch on event names have nothing to switch on.

Why It Happens: AiPrise models the outcome as a result value, not typed events.

Workarounds:

* Branch on `verification_result` (`APPROVED`/`DECLINED`/`REVIEW`/`UNKNOWN`) and the process status.

How Hookdeck Can Help: Hookdeck's filters can route on `verification_result`, giving you event-like routing.

### Raw-body sensitivity

The Problem: The HMAC is over the exact raw body bytes. Re-serializing the JSON breaks verification.

Why It Happens: The signed content is the raw bytes.

Workarounds:

* Capture the raw body before parsing and verify against it.

How Hookdeck Can Help: Hookdeck verifies against the bytes as received.

### Undocumented retries

The Problem: Retry behavior isn't documented, so you can't assume a failed callback will be retried.

Why It Happens: AiPrise doesn't publish the retry policy.

Workarounds:

* Persist callbacks on receipt and reconcile via the API using `verification_session_id`.

How Hookdeck Can Help: Hookdeck durably stores and retries deliveries on a schedule you control.

## Best practices

### Verify with the API private key over the raw body

Compute lowercase-hex HMAC-SHA256 over the raw body with your AiPrise API private key and compare in constant time.

### Branch on verification_result and correlate

Read `aiprise_summary.verification_result` and the process status, and correlate via `verification_session_id` / `client_reference_id`.

### Acknowledge fast, process asynchronously

Return 200 immediately and defer work to a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Persist and reconcile

Since retries aren't documented, log callbacks and reconcile via the API.

## Conclusion

AiPrise callbacks verify with an `X-HMAC-SIGNATURE` HMAC-SHA256 over the raw body, keyed with your API private key (there's no separate secret), and the outcome is the `verification_result` value, not a typed event. Verify against the raw body, branch on the result, correlate via `verification_session_id`, and persist since retries aren't documented.

[Hookdeck](https://hookdeck.com) verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified verification results.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle AiPrise webhooks reliably in minutes.