Webhooks API
Configure event notifications, verify signatures, and manage delivery
The Webhooks API lets you register HTTP endpoints to receive real-time notifications when events occur in your store. Webhooks use POST requests with JSON payloads signed for verification.
Webhook Structure
{
"id": "whk_abc123",
"url": "https://yourserver.com/webhooks/hanzo",
"events": [
"order.created",
"order.updated",
"payment.captured"
],
"secret": "whsec_abc123def456ghi789",
"status": "active",
"metadata": {
"environment": "production"
},
"createdAt": "2024-01-10T08:00:00Z",
"updatedAt": "2024-06-15T10:00:00Z"
}Event Types
Order Events
| Event | Description |
|---|---|
order.created | New order placed |
order.updated | Order details changed |
order.cancelled | Order cancelled |
order.fulfilled | Order fully fulfilled |
order.partially_fulfilled | Some items fulfilled |
Payment Events
| Event | Description |
|---|---|
payment.authorized | Payment authorized |
payment.captured | Payment captured |
payment.failed | Payment attempt failed |
payment.refunded | Payment refunded |
Product Events
| Event | Description |
|---|---|
product.created | New product created |
product.updated | Product details changed |
product.deleted | Product deleted |
Customer Events
| Event | Description |
|---|---|
customer.created | New customer registered |
customer.updated | Customer profile changed |
Inventory Events
| Event | Description |
|---|---|
inventory.low_stock | Stock below threshold |
inventory.out_of_stock | Stock reached zero |
inventory.restocked | Stock replenished |
Endpoints
List Webhooks
GET /webhook
curl "https://api.hanzo.ai/webhook" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{
"count": 3,
"models": [
{
"id": "whk_abc123",
"url": "https://yourserver.com/webhooks/hanzo",
"events": ["order.created", "payment.captured"],
"status": "active",
"createdAt": "2024-01-10T08:00:00Z"
}
]
}Create Webhook
POST /webhook
curl -X POST https://api.hanzo.ai/webhook \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourserver.com/webhooks/hanzo",
"events": [
"order.created",
"order.updated",
"payment.captured",
"payment.refunded"
],
"metadata": {
"environment": "production"
}
}'Response: 201 Created
{
"id": "whk_abc123",
"url": "https://yourserver.com/webhooks/hanzo",
"events": ["order.created", "order.updated", "payment.captured", "payment.refunded"],
"secret": "whsec_abc123def456ghi789",
"status": "active",
"createdAt": "2024-01-10T08:00:00Z"
}The secret is only returned once at creation time. Store it securely for signature verification. If lost, rotate the secret using the update endpoint.
Update Webhook
PUT /webhook/:id
curl -X PUT https://api.hanzo.ai/webhook/whk_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"events": [
"order.created",
"order.updated",
"order.cancelled",
"payment.captured",
"payment.refunded"
]
}'Delete Webhook
DELETE /webhook/:id
curl -X DELETE https://api.hanzo.ai/webhook/whk_abc123 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response: 204 No Content
Payload Structure
Webhook payloads follow a consistent structure:
{
"id": "evt_abc123",
"type": "order.created",
"timestamp": "2024-06-15T10:00:00Z",
"data": {
"id": "order_abc123",
"displayId": "#1042",
"status": "confirmed",
"email": "[email protected]",
"total": 5638,
"currency": "USD",
"createdAt": "2024-06-15T10:00:00Z"
}
}Signature Verification
Every webhook request includes a signature header for verification. Always verify signatures to ensure the payload was sent by Hanzo and has not been tampered with.
Headers:
| Header | Description |
|---|---|
X-Hanzo-Signature | HMAC-SHA256 signature of the request body |
X-Hanzo-Timestamp | Unix timestamp of when the event was sent |
X-Hanzo-Event | Event type (e.g. order.created) |
X-Hanzo-Delivery | Unique delivery ID |
Verification Algorithm
The signature is computed as:
HMAC-SHA256(webhook_secret, timestamp + "." + request_body)Node.js Verification Example
import crypto from 'crypto'
function verifyWebhookSignature(
body: string,
signature: string,
timestamp: string,
secret: string
): boolean {
const payload = `${timestamp}.${body}`
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
)
}
// Express middleware
app.post('/webhooks/hanzo', (req, res) => {
const signature = req.headers['x-hanzo-signature']
const timestamp = req.headers['x-hanzo-timestamp']
if (!verifyWebhookSignature(req.body, signature, timestamp, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature')
}
const event = JSON.parse(req.body)
// Process event...
res.status(200).send('OK')
})Go Verification Example
func verifySignature(body []byte, signature, timestamp, secret string) bool {
payload := []byte(timestamp + "." + string(body))
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}Retry Policy
Failed webhook deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 8 hours |
| 7 | 24 hours |
A delivery is considered failed if your endpoint:
- Returns a non-2xx HTTP status code
- Does not respond within 30 seconds
- Is unreachable
After 7 failed attempts, the webhook is marked as failing. It will be automatically disabled after 3 consecutive days of failures. You will receive an email notification before disabling.
Rotate Secret
POST /webhook/:id/rotate-secret
Generate a new signing secret. The old secret remains valid for 24 hours to allow for a graceful rotation.
curl -X POST https://api.hanzo.ai/webhook/whk_abc123/rotate-secret \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response:
{
"secret": "whsec_new789jkl012mno345",
"previousSecretExpiresAt": "2024-06-16T10:00:00Z"
}Test Webhook
POST /webhook/:id/test
Send a test event to verify your endpoint is configured correctly.
curl -X POST https://api.hanzo.ai/webhook/whk_abc123/test \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event": "order.created"
}'SDK Examples
JavaScript
import { Commerce } from '@hanzo/commerce'
const commerce = new Commerce({ apiKey: 'your_key' })
// Create webhook
const webhook = await commerce.webhooks.create({
url: 'https://yourserver.com/webhooks/hanzo',
events: ['order.created', 'payment.captured']
})
// List webhooks
const webhooks = await commerce.webhooks.list()
// Update events
await commerce.webhooks.update(webhook.id, {
events: ['order.created', 'order.cancelled', 'payment.captured']
})
// Send test event
await commerce.webhooks.test(webhook.id, { event: 'order.created' })
// Rotate secret
const rotated = await commerce.webhooks.rotateSecret(webhook.id)
// Delete webhook
await commerce.webhooks.delete(webhook.id)Go
webhook, err := client.Webhooks.Create(ctx, &sdk.WebhookInput{
URL: "https://yourserver.com/webhooks/hanzo",
Events: []string{"order.created", "payment.captured"},
})
webhooks, err := client.Webhooks.List(ctx, nil)
err = client.Webhooks.Update(ctx, webhook.ID, &sdk.WebhookInput{
Events: []string{"order.created", "order.cancelled", "payment.captured"},
})
err = client.Webhooks.Test(ctx, webhook.ID, "order.created")
rotated, err := client.Webhooks.RotateSecret(ctx, webhook.ID)How is this guide?
Last updated on