# Webhook workflow
Endpoints are created in **Settings, Webhooks**. These commands cover the part a browser tab
cannot do: the loop you run while a receiver is not working yet.
## Find your endpoint
```bash
mercemur webhook endpoints
```
```
whe_01M0AMCJ0T... active https://hooks.example.com/mercemur
[orders/paid]
```
An endpoint that failed repeatedly shows as **auto-disabled**, which is not the same as one
somebody switched off. It stays silent until it is re-enabled.
## Generate a receiver
```bash
mercemur webhook scaffold --lang node --out ./receiver
```
Writes a small receiver with signature verification already correct. `--lang` takes `node` or
`python`; `verify.*` is separate from the server so it drops into an application that already has
its own HTTP layer.
The signing secret is base64 **after** its `whsec_` prefix, and the HMAC key is the decoded
bytes, not the string as handed to you. Getting that wrong is the most common way a receiver
fails, and it fails for *every* delivery rather than some, so it reads like the sender is
broken. The generated code gets it right.
It also handles three things people discover the hard way: the signed content is
`id.timestamp.rawBody` rather than the body alone, the header can carry several space-separated
signatures during a secret rotation (accept if any verifies), and deliveries are at-least-once so
the same `webhook-id` can arrive twice.
Existing files are never overwritten without `--force`.
## Send a test event
```bash
mercemur webhook test whe_01M0AMCJ0T...
```
Sends a signed sample event so you can build and verify a receiver before a real order exists.
It lands in your delivery log, so a test is auditable rather than invisible.
This **exits non-zero when your endpoint does not accept the delivery**. The API call
succeeding is not the question being asked; whether your receiver works is. That makes it
usable as a CI health check.
```
Your endpoint did not accept the test: HTTP 405
```
## Watch deliveries arrive
```bash
mercemur webhook tail
```
Polls and prints each delivery once. Ctrl-C to stop. `--json` is refused here, because this is a
stream for a person; for scripted output run `mercemur webhook deliveries --json` in your own
loop.
## Look at what happened
```bash
mercemur webhook deliveries --status failed
```
```
whd_01M0AMCZ... orders/paid failed 3 attempt(s) HTTP 500
whd_01M0AMD1... orders/paid failed 2 attempt(s) no response (connection refused)
```
Three outcomes read differently on purpose:
| Shown | Means |
|---|---|
| `HTTP 500` | Your receiver answered, and refused |
| `no response (...)` | Your receiver was never reached |
| `no response yet` | Nothing has been attempted |
A receiver that was never reached and one that returned a 500 need completely different fixes, so
they are never collapsed into one line.
| Flag | Default | |
|---|---|---|
| `--status` | all | `pending`, `succeeded` or `failed` |
| `--endpoint` | all | Only deliveries to one endpoint |
| `--limit` | `20` | How many to fetch |
## Replay a failure
```bash
mercemur webhook replay whd_01M0AMCZ...
```
Sends a past delivery again, to the endpoint **as it is configured now**. So a receiver you have
just fixed can be re-run against the event that broke it, without waiting for another order.
Deliveries produced by `webhook test` are marked `(test)` and cannot be replayed, because
`webhook.test` is not an event the platform emits. The listing only suggests replay when
something in it can actually be replayed.
## Scopes
`read_webhooks` for `endpoints` and `deliveries`. `write_webhooks` for `test` and `replay`.
Subscribing an endpoint to an event also requires the scope for that event's data. Subscribing to
`orders/paid` needs `read_orders`, so a key cannot receive order payloads it would not be allowed
to read.