# 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.