Webhooks Reference
Receive signed, real-time notifications when offline repairs and trade returns change state in ReturnMate.
ReturnMate sends outbound webhooks to your systems for RMAs that were created through the External API — offline repairs and trade returns. Webhooks are not emitted for standard portal- or staff-created returns.
Configuring a webhook endpoint
- Go to Settings → External API (Admin only).
- Create an API client, or edit an existing one, and set its Webhook URL (HTTPS recommended).
- Note the webhook secret shown when the client is created — deliveries are signed with it.
One endpoint per API client. Events for an RMA are delivered to the client that created it (trade-return events fall back to your shop's active client with a webhook URL).
Events
| Event | Fired when |
|---|---|
offline_repair.created | An offline repair is created via the API |
offline_repair.tracking_uploaded | Return tracking is lodged |
offline_repair.status_changed | The repair moves between lifecycle stages |
offline_repair.disposition_set | A disposition is set at resolution |
offline_repair.closed | The repair is closed |
trade_return.created | A trade (B2B) return is created |
trade_return.status_changed | A trade return moves between lifecycle stages |
trade_return.credit_requested | A credit is requested for a trade return |
trade_return.resolved | A trade return is resolved |
Payload
Deliveries are POST requests with a JSON body:
{
"event": "offline_repair.status_changed",
"timestamp": "2026-07-14T03:21:45.000Z",
"data": {
"id": "cln_…",
"rmaNumber": "#WAR1234A",
"externalRef": "TICKET-4821",
"externalSource": "my-pos",
"status": "OFFLINE_REPAIR"
}
}
Every payload's data object includes the RMA id, rmaNumber, and your externalRef/externalSource, plus event-specific fields (for example the new status, tracking details, or the disposition).
Signature verification
If your client has a webhook secret, each delivery includes:
X-Webhook-Signature: <hex HMAC-SHA256 of the raw request body>
Verify it by computing HMAC-SHA256 over the raw request body with your webhook secret and comparing constant-time:
const crypto = require('crypto');
function verify(rawBody, signature, secret) {
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Compute the HMAC over the exact bytes received, before any JSON parsing or re-serialisation, or signatures will not match.
Delivery, timeouts and retries
- Requests time out after 10 seconds.
- Respond with any 2xx status to acknowledge. Non-2xx responses and timeouts are retried.
- Up to 4 attempts with backoff: immediately, then after 1 s, 4 s and 16 s. After the final failure the delivery is dropped (it is logged server-side but not exposed for replay).
Idempotency
Deliveries are at-least-once — your endpoint may occasionally receive the same event more than once (for example if your 2xx response was slow). De-duplicate on (event, data.id, timestamp) or on your own externalRef plus the event name, and make handlers idempotent.
Best practice
- Acknowledge quickly (enqueue work, return 200) to stay inside the 10-second timeout.
- Don't rely on webhook ordering; fetch the current state via
GET /offline-repairs/:idwhen in doubt. - Rotate exposure by revoking and recreating the API client if a secret leaks.