1. Order Webhook
We POST your Callback URL once when an order is created. One event: order.created.
Setup
Settings→Developer→ add a Callback URL + a Secret you choose (8–255 chars).- Click Test. The webhook will go live after successful test.
Verify the endpoint (Test handshake)
Test sends:
{ "event": "webhook.test", "data": { "business_id": "...", "challenge": "aB3xY..." } }
Your endpoint must reply 200 with the same challenge:
{ "challenge": "aB3xY..." }
Handle it before your normal logic:
if (req.body.event === "webhook.test")
return res.status(200).json({ challenge: req.body.data.challenge });
Webhook Request
Headers:
Content-Type: application/json
X-Webhook-Event: order.created
X-Webhook-Signature: <hex HMAC-SHA256 of the raw body, keyed with your Secret>
Body:
{
"event": "order.created",
"data": {
"order_id": "unique uuid of the order",
"business_id": "uuid of your business",
"customer_id": "uuid of the customer",
"total": 1200,
"items": [
{ "code": "SKU-100", "amount": 1200, "qty": 1, "meta_data": { "name": "product/service name", "price": 1200, "stock": 39}
}
],
"order": {
"code": "9d1f8a2c", "state": "confirmed", "is_test": false,
"name": "customer name", "phone": "+8801...", "email": "[email protected]", "address": "Dhaka",
"payment_method": "cod", "additional_charge": 60, "amount": 1200,
"note": "customer's note", "ai_note": "noted by ai", "weight": 0.5,
"delivery_type": "normal delivery", "item_type": "parcel", "appointment_at": null,
"created": "2026-07-13T10:00:00+00:00", "updated": "2026-07-13T10:00:00+00:00"
}
}
}
items[].codeis catalog code.is_test: true→ sandbox/test order.
Verify the signature
HMAC-SHA256 the raw request body (not re-serialized JSON) with your Secret, hex-encode, compare to X-Webhook-Signature.
const crypto = require("crypto");
app.use(express.json({ verify: (req, _res, buf) => { req.rawBody = buf; } }));
app.post("/webhooks/orders", (req, res) => {
const expected = crypto.createHmac("sha256", process.env.WEBHOOK_SECRET)
.update(req.rawBody).digest("hex");
const got = req.get("X-Webhook-Signature") || "";
if (expected.length !== got.length ||
!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(got)))
return res.status(401).send("bad signature");
res.sendStatus(200); // ack fast, process async
});
Python: hmac.new(secret.encode(), request.body, hashlib.sha256).hexdigest().
Notes
- 10s timeout, redirects not followed, any
2xx= success. - Max 3 retries
2. Catalog Sync API
Push create / update / delete of catalog items.
POST https://chatcopilot.app/api/webhook/catalog/sync/
X-API-Key: <your key>
Content-Type: application/json
Prerequisites: an active catalog with a code column set. Sync matches items by that code.
Request
{ "items": [
{ "action": "create", "code": "SKU-100", "price": 1200, "stock": 40 },
{ "action": "update", "code": "SKU-100", "stock": 35 },
{ "action": "delete", "code": "SKU-999" }
]}
actionrequired:create|update|delete.coderequired: on every item; the value of your catalog's code column.- Any other keys map to your catalog column names (case-insensitive). Unknown keys are ignored.
- create fails if
codeexists. - update merges the fields you send (omitted fields untouched); fails if
codenot found. - delete removes the item.
Response:
- Always
200on a valid request
{ "synced": 3, "created": 1, "updated": 1, "deleted": 1, "errors": [] }
Items are processed independently; failures land in errors (the rest still apply):
{ "synced": 2, "created": 1, "updated": 0, "deleted": 0,
"errors": [ { "index": 1, "code": "SKU-100", "error": "not found" } ] }
Per-item errors: missing code, code already exists, not found, unknown action: <x>.
Request-level errors
| Status | Body | Cause |
|---|---|---|
| 401 | Invalid or missing API key |
bad/missing X-API-Key |
| 400 | Invalid JSON |
body isn't valid JSON |
| 400 | items must be a list |
items missing / not an array |
| 400 | No active catalog for this business |
no active catalog |
| 400 | This catalog has no code column... |
code column not set |
curl
curl -X POST https://chatcopilot.app/api/webhook/catalog/sync/ \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"items":[{"action":"create","code":"SKU-100","price":1200,"stock":40}]}'