Webhook Setup
Receive real-time notifications when events occur in your Commerce Index account. Configure webhooks to keep your systems in sync.
Registering Webhooks
You can register webhook endpoints through the Commerce Index dashboard or via the API. In the dashboard, navigate to Settings → Webhooks → Add Endpoint and enter your URL and select the events you want to receive.
Alternatively, register programmatically via the API:
// Register a webhook via API
const response = await fetch('https://api.commerceindex.ai/v1/webhooks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CI-Api-Key': 'your_api_key',
'X-CI-Merchant-Id': 'merch_xyz789'
},
body: JSON.stringify({
url: 'https://your-server.com/webhooks/ci',
events: [
'product.updated',
'order.created',
'deal.created',
'health.score_change'
],
secret: 'whsec_your_webhook_secret'
})
});
const webhook = await response.json();
console.log('Webhook registered:', webhook.id);Event Types
Subscribe to any combination of the following event types:
| Event | Description |
|---|---|
product.created | A new product is added to your catalog |
product.updated | Product details (price, title, inventory) are modified |
product.deleted | A product is removed from the catalog |
order.created | A new order is placed |
order.paid | Payment is confirmed for an order |
order.fulfilled | An order has been shipped or fulfilled |
inventory.low | Stock falls below the configured threshold |
deal.created | An AI agent creates a new deal |
deal.expired | A deal reaches its expiration date |
agent.decision | An agent makes a decision requiring notification |
health.score_change | Your CI Health Score changes significantly |
Payload Format
All webhook payloads follow a consistent JSON structure with the event type, timestamp, event data, and your merchant ID:
{
"event": "product.updated",
"timestamp": "2026-01-15T10:30:00Z",
"data": {
"product_id": "prod_abc123",
"title": "Wireless Bluetooth Headphones",
"price": 79.99,
"previous_price": 99.99,
"inventory": 42,
"changes": ["price", "inventory"]
},
"merchant_id": "merch_xyz789",
"webhook_id": "wh_evt_def456"
}Signature Verification
Every webhook request includes an X-CI-Signature header containing an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the webhook is authentic and hasn't been tampered with.
- 1. Extract the
X-CI-Signatureheader from the request - 2. Compute HMAC-SHA256 of the raw request body using your webhook secret
- 3. Compare the computed hash with the received signature using a timing-safe comparison
- 4. Reject the request if signatures don't match
Retry Policy
If your endpoint returns a non-2xx status code or times out, Commerce Index will retry delivery up to 3 times with exponential backoff:
1 second
Retry 1
Immediate retry for transient failures
5 seconds
Retry 2
Short delay for temporary outages
30 seconds
Retry 3
Final attempt before marking as failed
Your endpoint must respond within 10 seconds. After all retries are exhausted, the event is logged as failed and visible in your dashboard under Webhooks → Failed Deliveries.
Node.js Handler Example
// Node.js webhook handler (Express)
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.raw({ type: 'application/json' }));
const WEBHOOK_SECRET = process.env.CI_WEBHOOK_SECRET;
function verifySignature(payload, signature) {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
app.post('/webhooks/ci', (req, res) => {
const signature = req.headers['x-ci-signature'];
if (!verifySignature(req.body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
switch (event.event) {
case 'product.updated':
console.log('Product updated:', event.data.product_id);
// Sync product changes to your system
break;
case 'order.created':
console.log('New order:', event.data.order_id);
break;
case 'deal.created':
console.log('New deal:', event.data.deal_id);
break;
case 'health.score_change':
console.log('Health score changed:', event.data.new_score);
break;
default:
console.log('Unhandled event:', event.event);
}
res.status(200).json({ received: true });
});
app.listen(3000, () => console.log('Webhook server running on port 3000'));Python Handler Example
# Python webhook handler (Flask)
import hmac
import hashlib
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get('CI_WEBHOOK_SECRET')
def verify_signature(payload, signature):
expected = hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
@app.route('/webhooks/ci', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-CI-Signature')
if not verify_signature(request.data, signature):
return jsonify({'error': 'Invalid signature'}), 401
event = request.get_json()
if event['event'] == 'product.updated':
print(f"Product updated: {event['data']['product_id']}")
# Sync product changes
elif event['event'] == 'order.created':
print(f"New order: {event['data']['order_id']}")
elif event['event'] == 'deal.created':
print(f"New deal: {event['data']['deal_id']}")
elif event['event'] == 'health.score_change':
print(f"Health score: {event['data']['new_score']}")
return jsonify({'received': True}), 200
if __name__ == '__main__':
app.run(port=3000)