Webhooks
Medusa v2 webhooks plugin: admin UI, HMAC-signed deliveries, durable DB-backed retries (no workflow engine / Redis dependency).
@xross-dev/medusa-webhooks
Outgoing webhooks for Medusa v2: manage them from the admin, get HMAC-signed deliveries, and automatic retries that are stored in your database and re-run by a scheduled job. No Medusa workflow-engine retries, so nothing here depends on Redis workflow scheduling.
Originally based on the MIT-licensed webhooks plugin; v3 is a rewrite.
Install
npx medusa db:migrateThe retry job (, every minute) runs wherever Medusa runs scheduled jobs: the server in the default mode, or the worker instance when you use .
Send events
never throws. It returns .
HTTP methods
Each webhook has a method: (default), , , or .
| Method | Body | Event data |
|---|---|---|
| JSON (the event data) | body, and optionally URL placeholders | |
| none | URL placeholders only |
and carry no body (HTTP semantics; rejects a body). Put the data you need in the URL with filled from the event data, URL-encoded:
- Placeholders work in the path and query only, not the host (SSRF guard).
- If a value is missing from the event data the delivery fails immediately with a clear error and no request is sent.
- Send test uses for . For // the admin asks for confirmation first, because the test is a real request.
- Duplicates are checked on event + method + URL.
How delivery works
- Each active webhook for the event gets one request, in parallel.
- 2xx = success. Timeouts, network errors, 408, 429 and 5xx are retried after 1, 5 and 30 minutes (first attempt + 3 retries). Other 4xx, redirects and blocked targets fail immediately.
- State lives in the table ( / / ). The payload is kept only while a retry may still happen and is cleared on success. It is never returned by the API.
- Retry now in the admin starts a fresh cycle for a failed delivery.
- Deleting or deactivating a webhook ends its pending retries.
Receiver headers: , , (same value on every retry of one delivery), , . Body is the raw event data (empty for /, in which case the signature is computed over an empty body).
Admin API ()
| Method & path | Notes |
|---|---|
| , , , , , , | |
| returns (shown once) | |
| · · | accepts . also removes its deliveries |
| , , , | |
| manual retry | |
| events grouped for the UI | |
| sends a real, signed test request, not stored |
Notes
- Upgrading from 3.0: run (adds a column, default ; existing webhooks are unchanged).
- Upgrading from : the migration keeps the table and adds columns. Old webhooks have no secret until you click Rotate.
- SSRF guard is best-effort against DNS rebinding; also restrict egress at the network level.
- Two instances running the job at the same moment could double-send one retry (rows are leased for 5 min to make this unlikely). Receivers should be idempotent, especially for /, which a retry after a timeout can repeat: dedupe on .

