# Webhooks — BrandApp Playground

> Signed webhook handling with replay protection, exercised by an in-browser HMAC simulator.

- Canonical page: https://brandapp-playground.vercel.app/webhooks
- Markdown: https://brandapp-playground.vercel.app/webhooks.md
- SDK module: `@reopt-ai/brandapp-sdk/webhooks`

## The handler

`createWebhookHandler()` verifies an HMAC-SHA256 signature over `{timestamp}.{body}`,
sent as `x-reopt-signature` with `x-reopt-timestamp` and `x-reopt-event`.

## Branches the simulator exercises

| Scenario | Response |
| --- | --- |
| valid signature | `200` |
| replay, 6 minutes old | `410` (default `toleranceMs` is 5 minutes) |
| tampered — wrong secret | `401` |
| missing `x-reopt-timestamp` | `401` |
| same delivery id twice | `200`, then `200 { deduplicated: true }` when `idempotency` is on |

The 500 handler-error branch and the `onError` / `x-reopt-delivery-attempt` contract are
covered by the code examples rather than the simulator.

## Related

- [Error Handling](https://brandapp-playground.vercel.app/errors.md)
- [Testing Utilities](https://brandapp-playground.vercel.app/testing.md)

---

Index of every document: https://brandapp-playground.vercel.app/llms.txt
Single-file copy: https://brandapp-playground.vercel.app/llms-full.txt
HTTP API description: https://brandapp-playground.vercel.app/openapi.json
