Webhooks Guide
Webhooks let your application receive real-time HTTP notifications when events occur in your project — orders placed, inventory updated, customers created, and more.
How webhooks work
- You register an endpoint URL and choose which event types to subscribe to.
- When a matching event occurs, the API sends a
POSTrequest to your endpoint with a JSON payload. - Your endpoint processes the payload and returns a
2xxstatus to acknowledge receipt.
Creating a webhook
curl -X POST https://api.hydrajs.dev/v1/webhooks \
-H "Authorization: Bearer sk_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/hydra",
"events": ["order.created", "order.paid"]
}'
The response includes a secret field — this is your signing secret, shown once. Store it securely. You’ll use it to verify webhook signatures.
{
"data": {
"id": "wh_abc123def456ghij",
"url": "https://example.com/webhooks/hydra",
"events": ["order.created", "order.paid"],
"secret": "whsec_abc123...",
"active": true,
"created_at": "2026-08-24T10:00:00.000Z"
}
}
Payload format
Each delivery sends a JSON body with the event type and the resource data:
{
"type": "order.created",
"data": {
"id": "ord_abc123def456ghij",
"status": "pending",
"total": 4999,
"currency": "USD"
}
}
Verifying signatures
Every webhook delivery includes an X-Hydra-Signature header containing an HMAC-SHA256 hex digest of the request body, signed with your webhook secret.
Always verify this signature before processing the payload:
import { createHmac } from 'crypto';
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret)
.update(body)
.digest('hex');
return expected === signature;
}
// In your webhook handler
app.post('/webhooks/hydra', (req, res) => {
const body = req.rawBody; // must be the raw string, not parsed JSON
const signature = req.headers['x-hydra-signature'];
if (!verifyWebhook(body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(body);
// Process the event...
res.status(200).send('OK');
});
⚠Use the raw body
Signature verification requires the raw request body as a string. If your framework parses JSON automatically, configure it to preserve the raw body. Re-serializing parsed JSON may change whitespace or key order, causing verification to fail.
Retry policy
If your endpoint returns a non-2xx status or doesn’t respond within 10 seconds, the delivery is marked as failed and retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 8 hours |
After 5 failed attempts, the delivery is abandoned. Failed deliveries are retained for 7 days; successful deliveries for 30 days. You can inspect delivery history via the API.
Available event types
Products
product.created— A product was createdproduct.updated— A product was updatedproduct.deleted— A product was deleted
Orders
order.created— An order was placedorder.paid— Payment was capturedorder.fulfilled— All items shippedorder.cancelled— The order was cancelledorder.refunded— A refund was issued
Customers
customer.created— A customer account was createdcustomer.updated— Customer details were updated
Fulfillment
fulfillment_order.created— A fulfillment order was createdfulfillment.created— A fulfillment was created (items shipped)fulfillment.updated— Tracking or status updatedfulfillment.cancelled— A fulfillment was cancelled
Inventory
inventory.low— Stock fell below the low-stock threshold
Discounts
discount.created— A discount code was createddiscount.updated— A discount was updateddiscount.deleted— A discount was deleted
Shipping
shipping.zone.created— A shipping zone was createdshipping.zone.updated— A shipping zone was updatedshipping.zone.deleted— A shipping zone was deleted
Promotions
promotion.created— A promotion was createdpromotion.updated— A promotion was updatedpromotion.deleted— A promotion was deleted
Draft orders
draft_order.created— A draft order was createddraft_order.updated— A draft order was updateddraft_order.completed— A draft order was converted to an orderdraft_order.deleted— A draft order was deleted
Companies
company.created— A company was createdcompany.updated— A company was updatedcompany.deleted— A company was deleted
Purchase orders
purchase_order.created— A purchase order was createdpurchase_order.updated— A purchase order was updatedpurchase_order.received— Inventory was receivedpurchase_order.cancelled— A purchase order was cancelled
Returns & refunds
refund.created— A refund was initiatedrefund.succeeded— A refund was processed successfullyrefund.failed— A refund failedreturn.requested— A return was requestedreturn.approved— A return was approvedreturn.received— Returned items were receivedreturn.rejected— A return was rejectedreturn.cancelled— A return was cancelledreturn.closed— A return was closed
Navigation
navigation.created— A navigation menu was creatednavigation.updated— A navigation menu was updatednavigation.deleted— A navigation menu was deleted
Store credit
store_credit.issued— Store credit was issued to a customerstore_credit.used— Store credit was applied to an orderstore_credit.expired— Store credit expired
Usage
usage.warning— Monthly API quota hit 80%, 90%, or 100%
Best practices
- Respond quickly. Return a
2xxwithin a few seconds. If processing takes longer, queue the work and acknowledge immediately. - Handle duplicates. In rare cases (network timeouts, retries), you may receive the same event twice. Use the event data to make your handler idempotent.
- Use HTTPS. Webhook URLs must use
https://in production. - Verify signatures. Always validate the
X-Hydra-Signatureheader before trusting the payload. - Monitor deliveries. Use
GET /v1/webhooks/{id}/deliveriesto check for failed deliveries and diagnose issues.