Webhook Documentation
Receive real-time notifications when events occur in connected stores
Event Types
ZaLinkAI dispatches webhooks for the following events. Each event includes a JSON payload with the event type, timestamp, store ID, and event-specific data.
order.createdA new order has been placed
order.updatedAn order status or details changed
order.completedAn order has been fulfilled and completed
order.cancelledAn order was cancelled by merchant or customer
product.createdA new product was added to the catalog
product.updatedProduct details, price, or stock changed
product.deletedA product was removed from the catalog
customer.createdA new customer account was created
customer.updatedCustomer profile information changed
app.uninstalledThe merchant disconnected ZaLinkAI
Payload Examples
Select an event to see its payload schema.
{
"event": "order.created",
"timestamp": "2026-01-27T12:00:00Z",
"storeId": "store_abc123",
"data": {
"orderId": "ord_789",
"status": "pending",
"total": 249.99,
"currency": "SAR",
"items": [
{ "productId": "prod_1", "name": "Wireless Headphones", "qty": 1, "price": 249.99 }
],
"customer": { "id": "cust_456", "email": "customer@example.com" }
}
}Signature Verification
Every webhook request includes an X-ZaLink-Signature header containing an HMAC-SHA256 hex digest. Verify this signature to ensure the request originated from ZaLinkAI.
Get the raw request body as a string
Compute HMAC-SHA256 using your webhook secret
Compare the computed hash with X-ZaLink-Signature header
Use constant-time comparison to prevent timing attacks
Retry Policy
If your endpoint returns a non-2xx status code or times out (30s), ZaLinkAI retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 6 hours |
After 5 failed attempts, the webhook is marked as failed and the merchant is notified via email.
Webhook Handler Examples
Complete webhook handler implementations with signature verification.
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const computed = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(computed)
);
}
// Express handler
app.post('/webhooks/zalink', (req, res) => {
const signature = req.headers['x-zalink-signature'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) return res.status(401).send('Invalid signature');
const { event, data } = req.body;
switch (event) {
case 'order.created':
handleNewOrder(data);
break;
case 'product.updated':
syncProduct(data);
break;
}
res.json({ received: true });
});Best Practices
Return a 200 response immediately, then process the event asynchronously
Implement idempotency using the event ID to avoid duplicate processing
Store the raw payload for debugging and audit purposes
Set up monitoring and alerts for webhook failures
Use HTTPS endpoints with valid TLS certificates