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

# Webhooks

> Signed ScripX webhook deliveries for order and settlement events: HMAC SHA-256 signature verification, the delivery headers, retry and dead-letter semantics, and replaying failed deliveries.

Polling tells you when you ask. Webhooks tell you when it happens. Register a URL once and ScripX POSTs you every milestone: match, settlement, payout.

## Register

```bash theme={"dark"}
curl -X POST "$BASE_URL/v1/webhooks" \
  -H "X-ScripX-Key: $SCRIPX_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/hooks/scripx", "event": "*"}'
```

The response returns your `signing_secret` (`whsec_…`) **exactly once**. Store it like a password; every later view of the webhook is masked. Subscribe to one event or `*` for all. The current event catalog is in the [Webhook events annexure](/annexure/webhook-events) and is also returned live by [`GET /v1/webhooks`](/api-reference/webhooks/list).

## Every delivery is signed

Each POST carries:

| Header               | Meaning                                                                      |
| -------------------- | ---------------------------------------------------------------------------- |
| `X-ScripX-Signature` | `sha256=<hmac-hex>` over the exact body bytes, keyed by your `whsec_` secret |
| `X-ScripX-Event`     | The event name, e.g. `settlement.settled`                                    |
| `X-ScripX-Delivery`  | Stable delivery id per (event, subscription, entity), your dedupe key        |
| `User-Agent`         | `ScripX-Webhooks/1`                                                          |

Verify before you trust:

<CodeGroup>
  ```python Python theme={"dark"}
  import hmac, hashlib

  def verify(secret: str, body: bytes, signature: str) -> bool:
      mac = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
      return hmac.compare_digest(f"sha256={mac}", signature)
  ```

  ```javascript Node theme={"dark"}
  const crypto = require("crypto");

  function verify(secret, rawBody, signature) {
    const mac = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
    return crypto.timingSafeEqual(Buffer.from(`sha256=${mac}`), Buffer.from(signature));
  }
  ```
</CodeGroup>

<Note>
  Sign-check the **raw body bytes**, before any JSON parsing or re-serialisation. A pretty-printer changes the bytes and the signature stops matching.
</Note>

## Retries and dead-letters

* **Transient failures** (network, 5xx, 429) retry with backoff up to the attempt cap.
* **Permanent failures** (any other 4xx) skip retries and dead-letter immediately.
* **Exhausted retries** dead-letter too.

Nothing is lost: list dead-letters with [`GET /v1/webhooks/failures`](/api-reference/webhooks/failures) and push them again with [`POST /v1/webhooks/replay`](/api-reference/webhooks/replay). Deliveries can arrive more than once; dedupe on `X-ScripX-Delivery`.

## Receiver rules of thumb

1. Return 2xx fast; do the work async. Slow receivers look like failures and get retried.
2. Verify the signature, then dedupe on the delivery id.
3. Treat the event as a hint and re-read the resource for authoritative state.

## Related

* [Webhook events](/annexure/webhook-events), the catalog and payload conventions.
* [Errors](/errors), API-side error semantics.
