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

# Webhook Verification

## Delivery

Every webhook is a `POST` request with this envelope:

```json theme={null}
{
  "event": "<event name>",
  "data": { /* event-specific payload */ },
  "timestamp": 1732718962
}
```

Your endpoint must respond `200 OK` with an empty body. If it doesn't,
delivery is retried with exponential backoff until it succeeds.

<Tip>
  Deliveries are idempotent per underlying event id, but a redelivery can
  still reach your endpoint more than once during a retry window. Deduplicate
  on the payload's activity/entity id, not on delivery count.
</Tip>

## Verifying the signature

Each delivery carries the same `x-signature` / `x-timestamp` headers used for
[request authentication](/api-reference/authentication), computed over the
**entire request body** (not just `data`) using your webhook secret.

```javascript theme={null}
const crypto = require('crypto');

function verifyOnboardWebhook(rawBody, signatureHeader, timestampHeader, webhookSecret) {
  const signaturePayload = `t=${timestampHeader}&${rawBody}`;
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(signaturePayload)
    .digest('hex');

  return expectedSignature === signatureHeader;
}
```

<Warning>
  Use the **raw request body string** exactly as received, not a re-serialized
  version of the parsed JSON. Re-serializing with `JSON.stringify` can reorder
  or reformat keys differently than the sender did, which silently breaks
  verification.
</Warning>

Your webhook secret is generated alongside your webhook URL in the
[Business Dashboard](https://business.onboard.xyz) — see
[Configuring and Verifying Webhooks](/guides/api-concepts/webhooks-setup).
