> For the complete documentation index, see [llms.txt](https://developers.bead.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.bead.xyz/reference-guide/operational-guides/webhook-event-reference.md).

# Webhook Event Reference

Webhook notifications let your backend react to changes without polling. This page is the shared operational reference for Bead webhook delivery.

Use this page for:

* delivery behavior and retries
* signature verification
* idempotent processing
* ordering and replay safety
* production operating guidance

Use the product-specific webhook page for:

* setup and registration steps
* payload fields and examples
* event types or status meanings
* product-specific business handling

**What this page does and does not define**

This page defines the shared operational model for webhook handling.

This page does **not** define a single canonical payload schema for every webhook family. Payload shape is product-specific. Do not assume a shared cross-product envelope unless the relevant product page explicitly documents one.

**Where events go**

**Payments**

Bead supports two payment webhook delivery paths:

* **Terminal default webhook** — configured at the terminal level. Applies to all payments created by that terminal.
* **Per-payment webhook URLs** — supplied through `webhookUrls` on `POST /Payments/crypto`. Applies only to that individual payment.

When both are present, Bead fans out the same payment event to the terminal webhook and to each URL listed in `webhookUrls`.

**Other webhook families**

Other product areas may define their own webhook registration path and event selection model. Use the product-specific webhook page for setup details.

**Configure the terminal default webhook**

Payment webhooks are configured at the terminal level. The following endpoints manage the terminal's webhook URL:

* `PUT /Terminals/{id}/webhook` — set or update the terminal webhook URL
* `DELETE /Terminals/{id}/webhook` — remove the terminal webhook URL

**Authentication**

Use your **admin API key** in the `X-Api-Key` header for terminal webhook management. These endpoints do not accept a terminal payments API key. Using a terminal payments API key will return `403 Forbidden`.

**Request headers**

<table><thead><tr><th width="170">Header</th><th>Value</th></tr></thead><tbody><tr><td><code>X-Api-Key</code></td><td><code>{adminApiKey}</code></td></tr><tr><td><code>Content-Type</code></td><td><code>application/json</code></td></tr><tr><td><code>Accept</code></td><td><code>application/json</code></td></tr></tbody></table>

**Request body**

<table><thead><tr><th width="80">Field</th><th width="126">Type</th><th width="141">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>url</code></td><td>string (URI)</td><td>Yes</td><td>Fully qualified HTTPS URL where terminal-level payment status events should be delivered. Maximum 512 characters.</td></tr></tbody></table>

**Example request**

```bash
curl --request PUT "https://api.test.devs.beadpay.io/Terminals/{id}/webhook" \
  --header "X-Api-Key: {adminApiKey}" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json" \
  --data '{
    "url": "https://yourapp.com/webhooks/payment-status"
  }'
```

**Example response**

```json
{
  "url": "https://yourapp.com/webhooks/payment-status",
  "signingSecret": "{base64EncodedSecret}"
}
```

Store `signingSecret` securely. Use it to verify the `x-webhook-signature` header on all incoming webhook deliveries for this terminal. Treat it like a password and do not log it or expose it in client-side code.

**Notes**

* Use an HTTPS endpoint.
* The request body field is `url`, not `webhookUrl`.
* If you do not manage terminal configuration directly, coordinate with Bead support using your `terminalId` and the desired webhook URL.

**Signature header**

The `x-webhook-signature` header is included on terminal-level webhook deliveries. It is **not** included on per-payment deliveries sent to `webhookUrls`.

Example:

```
x-webhook-signature: t=1781811428956,s=FK/SW9lIK0CXpNnfweTN3ZbJ8Nvbm1RF69Nm6XE8w3O=
```

<table><thead><tr><th width="98">Field</th><th>Description</th></tr></thead><tbody><tr><td><code>t</code></td><td>Unix epoch timestamp in <strong>milliseconds</strong> when Bead generated the event</td></tr><tr><td><code>s</code></td><td><strong>Base64-encoded</strong> HMAC-SHA256 digest of the signed message <code>t + "." + rawBody</code>, computed using the decoded bytes of the terminal's <code>signingSecret</code></td></tr></tbody></table>

Always verify the signature against the **raw request body** before parsing or processing JSON.

**Signature verification flow**

1. Read the raw request body exactly as received. Do not parse or reserialize JSON before this step.
2. Read the `x-webhook-signature` header and parse the `t` and `s` values.
3. Validate the timestamp by confirming `t` (in milliseconds) is within your allowed freshness window. A 5-minute skew limit is recommended to prevent replay attacks.
4. Decode `signingSecret` from base64 to raw bytes. Use those bytes as the HMAC key.
5. Construct the signed message by concatenating `t`, a literal period, and the raw request body: `message = t + "." + rawBody`.
6. Compute `HMAC-SHA256(key=decodedSecretBytes, message=message)` and base64-encode the digest.
7. Compare your computed base64 digest to `s` using a constant-time comparison. Reject the request if they do not match.
8. Only then parse and process the JSON body.

For a full code example, see [How do I verify that a webhook really came from Bead?](/faqs-and-troubleshooting/webhooks-and-error-codes/how-do-i-verify-that-a-webhook-really-came-from-bead.md)

**Delivery mechanics**

* **Method:** HTTP `POST`
* **Content type:** `application/json`
* **Success condition:** a `2xx` response, preferably `200 OK`
* **Timeout:** 10 seconds per attempt
* **Retry behavior:** exponential backoff for up to approximately 24 hours if a `2xx` response is not returned
* **Delivery model:** at least once
* **Ordering:** do not assume perfect ordering
* **One event per change:** webhook families such as Payments send one event per status change

If your endpoint does not return a `2xx` response quickly, Bead may retry the same event.

**Recommended processing model**

1. Accept the request.
2. Preserve the raw body and headers.
3. Verify the signature.
4. Parse the JSON payload.
5. Build an idempotency key from the identifiers in the payload.
6. Persist or enqueue the event.
7. Apply business logic asynchronously if needed.
8. Return a `2xx` response quickly.

**Idempotency and duplicate handling**

Treat webhook delivery as at least once.

If a webhook family does not provide a dedicated `eventId`, derive idempotency from the identifiers in the payload.

For payment webhooks, a practical idempotency key is `trackingId` + `statusCode`, with `receivedTime` optionally included for debugging.

Do not treat duplicate deliveries as errors. They are a normal part of retry-safe webhook delivery.

**Ordering and state convergence**

Do not assume events arrive in perfect order.

Your handler should compare the incoming event to your current known state, ignore older or duplicate state transitions when appropriate, and converge on the latest valid state.

If you need to confirm the latest current state, call the relevant read endpoint for that product area.

**Security checklist**

* Use HTTPS webhook endpoints.
* Verify `x-webhook-signature` on every request where it is present.
* Treat the signing secret like a password.
* Reject malformed or unsigned requests.
* Reject stale timestamps to reduce replay risk.
* Avoid logging secrets or full sensitive headers.
* Keep development, staging, and production webhook URLs separate.
* Do not rely on source IP allowlisting alone. Signature verification should be your primary trust control.

**Operational checklist**

* Return a `2xx` response quickly.
* Queue or persist work before heavy downstream processing.
* Instrument request logging and delivery failures.
* Track retry volume and error rates.
* Build idempotent consumers.
* Test both happy-path and non-happy-path events in sandbox.

**Product-specific pages**

Use the product-area webhook page for payload fields, examples, and business handling:

* [Payment Webhooks](/payments/payment-webhooks.md)
* [Webhooks for Application Events](/onboarding/webhooks-for-application-events.md)

**Forward compatibility**

New webhook families may add dedicated event types, wrapper envelopes, or event IDs. Always validate against the product-specific documentation for that webhook family rather than assuming the same body shape across all products.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developers.bead.xyz/reference-guide/operational-guides/webhook-event-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
