# Testing webhooks

Local development, replays and common failure modes.

## Local development

Expose your local server (`ngrok`, `cloudflared`) and register the HTTPS URL as a **test** endpoint. `http://localhost` is only accepted for OAuth redirects, never for webhooks.

## Trigger events

- from the environment's ledger tools (real state changes), or
- with the event simulator (synthetic payloads for any event type).

## Read the logs

Each delivery shows request headers, raw body, your response code and body, latency and every retry. Filter by event type or status to find a bad handler quickly.

## Common failures

| Symptom | Cause |
| --- | --- |
| Signature mismatch | Body parsed before verification, or wrong endpoint secret |
| Duplicate side effects | No idempotency on `event.id` |
| Timeouts | Work done inline instead of queued |
| Missing events | Event type not subscribed on that endpoint |
| 403 from your own WAF | Our delivery IPs blocked, or bot protection on the path |

## Checklist before launch

- [ ] Signature verified against the raw body, with a 5-minute tolerance
- [ ] `event.id` deduplicated for at least 30 days
- [ ] Handler returns `2xx` in under 10 seconds
- [ ] Unknown event types ignored without erroring
- [ ] Alerting on delivery failure rate
