Webhooks
Outbound webhooks
Wijha POSTs (or PUT/PATCH/GET) JSON events to your URL when store data changes. Optional secrets sign every delivery so you can verify authenticity.
Manage endpoints
Open Merchant Portal → Store settings → Developers → Webhooks. Each endpoint has:
- Nickname — optional label shown in the portal.
- URL — HTTPS destination for deliveries.
- Method — POST (default), PUT, PATCH, or GET.
- Event — one or more event types (or * for all).
- Secret — optional shared secret (min 8 characters) used for HMAC signatures.
You can also create endpoints via POST /api/v1/webhooks/endpoints with the webhooks.write scope.
Delivery behavior
When a subscribed event fires, Wijha enqueues a delivery and sends it promptly:
- Up to 6 attempts total (1 initial + 5 retries).
- Retry delays: 30s → 2m → 10m → 1h → 6h.
- Continuous failure for about one week deactivates the endpoint and notifies the store.
Update the URL or reactivate under Developers → Webhooks after fixing your receiver.
Request headers
Every delivery includes:
- Content-Type: application/json
- User-Agent: Wijha-Webhooks/1.0
- X-Wijha-Timestamp — Unix milliseconds at send time
- X-Wijha-Signature — present only when a secret is set: HMAC-SHA256 hex of `${timestamp}.${rawBody}`
const crypto = require("crypto");
function verifyWijhaSignature({ secret, timestamp, rawBody, signature }) {
const expected = crypto
.createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "utf8"),
Buffer.from(signature, "utf8"),
);
}
// Express example — keep the raw body for HMAC verification
app.post("/webhooks/wijha", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-Wijha-Timestamp");
const signature = req.get("X-Wijha-Signature");
const rawBody = req.body.toString("utf8");
if (!verifyWijhaSignature({
secret: process.env.WIJHA_WEBHOOK_SECRET,
timestamp,
rawBody,
signature,
})) {
return res.status(401).send("invalid signature");
}
const event = JSON.parse(rawBody);
// handle event.eventType + event.data / event.changes
res.status(200).send("ok");
});Delivery JSON
Each attempt sends this envelope. data holds full entity snapshots; changes holds the sparse event fields.
{
"id": "del_01JABCXYZ",
"event": "OrderCreated",
"eventType": "OrderCreated",
"storeId": "str_01JSTORE",
"occurredAt": "2026-10-08T17:12:04.128Z",
"data": {
"order": {
"id": "ord_01JABC",
"storeId": "str_01JSTORE",
"number": "10042",
"channel": "portal",
"status": "pending",
"customerId": "cus_01JDEF",
"customerName": "Ahmed Hassan",
"customerPhone": "+201012345678",
"shippingAddress": {
"city": "Cairo",
"line1": "12 Nile St",
"phone": "+201012345678"
},
"lineItems": [
{
"productId": "prd_01J",
"variantId": "var_01J",
"sku": "TEE-BLK-M",
"title": "Cotton tee",
"qty": 2,
"price": 225
}
],
"payment": { "method": "cod", "codAmount": 495, "currency": "EGP" },
"totals": {
"subtotal": 450,
"shipping": 45,
"total": 495,
"currency": "EGP"
},
"createdAt": "2026-10-08T17:12:04.000Z"
},
"customer": {
"id": "cus_01JDEF",
"name": "Ahmed Hassan",
"phone": "+201012345678",
"email": "ahmed@example.com"
}
},
"changes": {
"aggregateType": "order",
"aggregateId": "ord_01JABC"
}
}Only related keys appear in data for a given event. Respond with HTTP 2xx to acknowledge.
Enrichment
Before enqueueing, Wijha expands the sparse domain event:
- id / event / eventType / storeId / occurredAt — delivery metadata.
- data — full snapshots (order, shipment, product, customer, collection, stockLevel as applicable).
- changes — original sparse facts (aggregateType, aggregateId, status transitions, actions, errors).
If a related entity cannot be loaded, that key is omitted from data; changes still arrive.
Event catalog
Subscribe to any of the following event types:
- OrderCreated — new order
- OrderUpdated — order fields changed
- OrderStatusChanged — order status transition
- ProductUpdated — product created, updated, or deleted
- CollectionUpdated — collection created, updated, or deleted
- CustomerCreated — new customer
- CustomerUpdated — customer fields changed
- ShipmentCreated — shipment booked
- ShipmentDelivered — shipment marked delivered
- ShipmentCreateFailed — booking failed
- InventoryAdjusted — stock level adjusted
- StockReceived — inbound stock received
OrderCreated example
Full order snapshot plus linked customer when available:
{
"id": "del_01JABCXYZ",
"event": "OrderCreated",
"eventType": "OrderCreated",
"storeId": "str_01JSTORE",
"occurredAt": "2026-10-08T17:12:04.128Z",
"data": {
"order": {
"id": "ord_01JABC",
"storeId": "str_01JSTORE",
"number": "10042",
"channel": "portal",
"status": "pending",
"customerId": "cus_01JDEF",
"customerName": "Ahmed Hassan",
"customerPhone": "+201012345678",
"shippingAddress": {
"city": "Cairo",
"line1": "12 Nile St",
"phone": "+201012345678"
},
"lineItems": [
{
"productId": "prd_01J",
"variantId": "var_01J",
"sku": "TEE-BLK-M",
"title": "Cotton tee",
"qty": 2,
"price": 225
}
],
"payment": { "method": "cod", "codAmount": 495, "currency": "EGP" },
"totals": {
"subtotal": 450,
"shipping": 45,
"total": 495,
"currency": "EGP"
},
"createdAt": "2026-10-08T17:12:04.000Z"
},
"customer": {
"id": "cus_01JDEF",
"name": "Ahmed Hassan",
"phone": "+201012345678",
"email": "ahmed@example.com"
}
},
"changes": {
"aggregateType": "order",
"aggregateId": "ord_01JABC"
}
}ProductUpdated example
{
"id": "del_01JPROD",
"event": "ProductUpdated",
"eventType": "ProductUpdated",
"storeId": "str_01JSTORE",
"occurredAt": "2026-10-08T16:40:00.000Z",
"data": {
"product": {
"id": "prd_01J",
"title": "Cotton tee",
"status": "active",
"price": 225,
"currency": "EGP",
"sku": "TEE-BLK-M"
}
},
"changes": {
"aggregateType": "product",
"aggregateId": "prd_01J",
"action": "updated"
}
}CustomerCreated example
{
"id": "del_01JCUS",
"event": "CustomerCreated",
"eventType": "CustomerCreated",
"storeId": "str_01JSTORE",
"occurredAt": "2026-10-08T15:05:22.000Z",
"data": {
"customer": {
"id": "cus_01JDEF",
"name": "Ahmed Hassan",
"phone": "+201012345678",
"email": "ahmed@example.com",
"createdAt": "2026-10-08T15:05:22.000Z"
}
},
"changes": {
"aggregateType": "customer",
"aggregateId": "cus_01JDEF"
}
}ShipmentCreated example
{
"id": "del_01JSHIP",
"event": "ShipmentCreated",
"eventType": "ShipmentCreated",
"storeId": "str_01JSTORE",
"occurredAt": "2026-10-08T18:20:11.000Z",
"data": {
"shipment": {
"id": "shp_01J",
"orderId": "ord_01JABC",
"trackingNumber": "WJH123456789",
"status": "booked",
"carrier": "mega"
},
"order": {
"id": "ord_01JABC",
"number": "10042",
"status": "waiting_for_pickup"
}
},
"changes": {
"aggregateType": "shipment",
"aggregateId": "shp_01J",
"orderId": "ord_01JABC"
}
}Expected responses
Your endpoint should acknowledge quickly:
- 2xx — delivery marked successful; consecutive failure window cleared.
- Other status or timeout — counted as failure; retries follow the schedule above.
- GET deliveries send the same headers; body is omitted for GET.
HTTP/1.1 200 OK
Content-Type: text/plain
ok