# Requesty Webhooks

[Requesty](https://www.requesty.ai) is an LLM routing gateway. Your organisation's webhooks carry
[spending alerts](https://docs.requesty.ai/features/alerts.md) (JSON, Slack or Teams formats) and mirrored
[sample requests](https://docs.requesty.ai/features/sample-requests). Once the organisation has a signing
secret, every delivery is signed with
Standard Webhooks headers: `webhook-id`,
`webhook-timestamp` and `webhook-signature`. The event name is in the `X-Requesty-Event` header. JSON alert
bodies are `{id, type, data}` and repeat it as `type`. Sample request bodies are
`{request_id, request, response}` and have no `type`.

## When to Use This Skill

* How do I receive Requesty webhooks?
* How do I verify Requesty webhook signatures (`webhook-signature`, `whsec_` secret)?
* How do I handle `user.budget.exceeded_percent`, `org.balance.below_absolute` or `apikey.budget.exceeded_absolute` alerts?
* How do I receive Requesty `sample.request` webhooks?
* Why is my Requesty webhook signature verification failing, or rejecting Hookdeck retries?

## Create a Signing Secret First

Hookdeck's configuration alert for this source reads: "Create a signing secret for your Requesty
organization. Until one exists, deliveries are unsigned and fail verification." Get the secret from Admin
Panel > Alerts > Webhook signing secret (admins only). Requesty also generates one the first time you save a
webhook URL. See [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/setup.md).

## Verification (core)

Signed content is `{webhook-id}.{webhook-timestamp}.{raw body}`. Requesty signs it with HMAC-SHA256 using the
base64-decoded secret (the part after `whsec_`), base64-encodes the result, and sends one or more
space-separated `v1,<signature>` entries (several during a secret rotation). Use a Standard Webhooks library
(`standardwebhooks` on npm and PyPI), which compares in constant time. Read the `webhook-*` headers, and when
`webhook-id` is absent fall back to the `svix-*` headers (`svix-id`, `svix-timestamp`, `svix-signature`), as
Hookdeck does.

```javascript
const { Webhook } = require('standardwebhooks');

const prefix = req.get('webhook-id') ? 'webhook' : 'svix';
const headers = {
  'webhook-id': req.get(`${prefix}-id`),
  'webhook-timestamp': req.get(`${prefix}-timestamp`),
  'webhook-signature': req.get(`${prefix}-signature`),
};
// rawBody: the raw Buffer/string, never re-serialized JSON
const body = new Webhook(process.env.REQUESTY_WEBHOOK_SECRET).verify(rawBody, headers);
// throws on a bad signature or a timestamp outside the window; returns the parsed body

```

```python
from standardwebhooks.webhooks import Webhook, WebhookVerificationError

wh = Webhook(os.environ["REQUESTY_WEBHOOK_SECRET"])  # "whsec_..."
body = wh.verify(raw_body, {"webhook-id": id_, "webhook-timestamp": ts, "webhook-signature": sig})

```

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/requesty-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/requesty-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/requesty-webhooks/examples/fastapi/).

Keep the full `whsec_...` value in `REQUESTY_WEBHOOK_SECRET`. There is one secret per organisation. Handlers
fail closed: with the variable unset they return 500 instead of accepting anything. A missing header or a bad
signature returns 400. Each retry is signed again with the same `webhook-id` and a new timestamp and
signature, so deduplicate on `webhook-id`.

### Hookdeck retries and replays hit the 5-minute window

The Standard Webhooks libraries always enforce a fixed 5-minute timestamp window (Requesty's instruction too:
reject timestamps "more than 5 minutes from the current time"), and it can't be configured. Hookdeck retries
and replays resend the **original** `webhook-timestamp`, so the library rejects any retry or replay that
happens more than 5 minutes after the original delivery. If you verify behind Hookdeck (or replay old
events), use the manual verifier in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/verification.md), where the
window can be dropped or widened. Deduplicate on `webhook-id` instead of relying on the timestamp.

## Common Event Types

7 events are documented, and the list may not be exhaustive. The name is the `X-Requesty-Event` header
(alert bodies repeat it as `type`). The examples handle all of these:

| Event | Triggered When |
| --- | --- |
| `user.budget.exceeded_percent` | A user's monthly spend reaches a percentage of their budget |
| `user.budget.exceeded_absolute` | A user's monthly spend reaches a dollar amount |
| `group.budget.exceeded_percent` | A group's combined spend reaches a percentage of its budget |
| `org.balance.below_absolute` | The organisation balance drops below a dollar amount |
| `apikey.budget.exceeded_percent` | An API key's monthly spend reaches a percentage of its budget |
| `apikey.budget.exceeded_absolute` | An API key's monthly spend reaches a dollar amount |
| `sample.request` | A sampled request and response are mirrored to your `webhook_url` |

Slack and Teams alert webhooks carry no event name in the body and ignore the signature headers. See
[references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/overview.md) for payload shapes and delivery behavior (15 s timeout, up
to 3 retries with exponential backoff).

## Environment Variables

```bash
# Organisation signing secret (Standard Webhooks format, starts with whsec_)
REQUESTY_WEBHOOK_SECRET=whsec_xxxxx

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 requesty --path /webhooks/requesty

```

Save the tunnel URL as your JSON alert webhook (Admin Panel > Alerts), then lower an alert threshold or send
a sampled request to see an event arrive.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/overview.md) - Alert and sample request webhooks, events, payloads, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/setup.md) - Create the signing secret, register the alert webhook, sample requests, rotation
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/requesty-webhooks/references/verification.md) - Standard Webhooks verification details, header fallback, manual verifier, timestamp window