Webhooks
Webhooks let your server react to events the moment they happen. New order, cart abandoned, product updated, customer registered. Subscribe once, get a POST every time.
Quick start
- Open the dashboard, Settings, Webhooks.
- Add an endpoint. Paste the URL on your server that should receive events.
- Pick the event types you want.
- Copy the signing secret shown after creation. You will need it to verify deliveries.
Payload shape
Every delivery is a POST with a JSON body. The envelope is consistent across event types, only the data field changes.
{
"id": "evt_01HXYZABC...",
"type": "order.paid",
"createdAt": "2026-04-20T13:24:11Z",
"storeId": "str_01HXY...",
"data": {
"id": "ord_01HXYZDEF...",
"orderNumber": "1024",
"currency": "GBP",
"totals": {
"total": { "amount": 12400, "currency": "GBP", "formatted": "£124.00" }
},
"customer": { "email": "[email protected]", "firstName": "Sam", "lastName": "Buyer" },
"items": [
{
"id": "oitm_01HXY...",
"productId": "prod_01HXY...",
"productName": "Hand-poured candle",
"quantity": 2,
"price": { "amount": 2400, "currency": "GBP", "formatted": "£24.00" },
"subtotal": { "amount": 4800, "currency": "GBP", "formatted": "£48.00" }
}
]
}
}
Always check the type field first. We add new event types over time and your handler should ignore types it doesn't recognise rather than 500.
Event types
| เหตุการณ์ | When it fires |
|---|---|
order.created | Order placed (may not be paid yet, e.g. bank transfer pending) |
order.paid | Payment confirmed |
order.fulfilled | Marked shipped or digital download delivered |
order.refunded | Full or partial refund issued |
order.cancelled | Order cancelled before fulfilment |
cart.abandoned | Cart inactive for 30 minutes with at least one item |
customer.created | New customer record (registered or first checkout) |
product.created | Product added to catalogue |
product.updated | Product details, price, or stock changed |
product.deleted | Product removed from catalogue |
Verifying signatures
Every request includes an X-Shoprocket-Signature header. Verify it before trusting the payload, otherwise anyone who knows your URL can fake events.
X-Shoprocket-Signature: t=1745158451,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
The signature is HMAC-SHA256 over the string {timestamp}.{raw_body}, keyed with your endpoint signing secret.
Node example:
import crypto from 'node:crypto';
function verify(rawBody, header, secret) {
const [tPart, vPart] = header.split(',');
const timestamp = tPart.split('=')[1];
const sent = vPart.split('=')[1];
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
return crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(expected));
}
PHP example:
function verify(string $rawBody, string $header, string $secret): bool {
[$t, $v] = array_map(fn($p) => explode('=', $p, 2)[1], explode(',', $header));
$expected = hash_hmac('sha256', "{$t}.{$rawBody}", $secret);
return hash_equals($expected, $v);
}
Retries and idempotency
Your endpoint must return a 2xx status within 10 seconds. Anything else is treated as a failure and the delivery enters retry.
Retry schedule: immediate, then 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 24 hours. After 24 hours we give up and the delivery is marked failed in your dashboard.
Securing your endpoint
The right defence is signature verification, covered above. HMAC checks every delivery against your endpoint secret. Anyone with your URL but not your secret is rejected.
If your security policy also requires an IP allowlist, contact support after launch and we will work with you on a fixed-IP delivery setup. The default delivery infrastructure uses dynamic source IPs that change with scaling, so signature verification is the only universally reliable check.
อัปเดตล่าสุด: 27 เมษายน 2026
Full changelog →Stuck on something?
Our dev team replies fast. Email us with code samples or open chat for a quick question.
dev@shoprocket.io