Developer

Webhooks

Have OrderRestro tell your other tools the moment something happens — a paid bill to your accounting software, a new delivery to WhatsApp, a booking to a spreadsheet.

Set one up

Open Settings > Webhooks, enter the URL that should receive events, tick the events you want, and choose Add webhook. The signing secret (it starts with whsec_) is shown once — copy it. Use Send test to check your receiver, and Log to see recent deliveries and why any failed.

Webhooks are the integration point for tools that don’t have a native connector: point one at an automation service (n8n, Zapier, Make) or your own server. For reading data or placing orders, see the REST API.

What you receive

Each event is an HTTP POST with a JSON body and these headers:

  • X-OrderRestro-Event — the event name
  • X-OrderRestro-Delivery — a unique id for this delivery; use it to ignore duplicates
  • X-OrderRestro-Signature — t=<unix seconds>,v1=<hex>
Body
{
  "event": "order.paid",
  "createdAt": "2026-10-05T11:42:10.512Z",
  "data": {
    "branchId": "cmui15lbm002ncv01wmaofjx1",
    "orderId": "cmuu38vk900181k2xtb4e35mk",
    "orderNumber": "20261005-0014",
    "type": "TAKEAWAY",
    "total": 480,
    "tip": 0,
    "discount": 0,
    "couponCode": null,
    "payments": [{ "method": "UPI", "amount": 480 }]
  }
}

Answer with any 2xx status to accept. The response body is ignored.

Events

EventWhendata fields
order.createdA new order was placed (POS, QR, website or API).orderId, orderNumber, type, channel, total, deliveryAddress, deliveryPhone, scheduledFor
order.paidA bill was paid.orderId, orderNumber, type, total, tip, discount, couponCode, payments[{method, amount}]
order.cancelledAn order was cancelled or an online order was rejected.orderId, orderNumber, type
reservation.createdA table was booked.reservationId, customerName, phone, guestCount, reservedAt, channel
delivery.updatedA driver was assigned or the delivery status changed.orderId, orderNumber, status, driver, address
campaign.messageA marketing message to deliver by SMS or WhatsApp (one event per recipient).campaignId, channel, to, name, message

Every data object also carries the branchId it happened at. A test event is sent by the Send test button.

Verify the signature

Anyone can POST to your URL, so always check the signature. v1 is the HMAC-SHA256, in hex, of the string {t}.{raw request body} using your signing secret. Use the raw body (before any JSON parsing), compare in constant time, and reject timestamps more than five minutes old.

Node.js
import crypto from "node:crypto";

export function verify(secret, rawBody, header) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
  const mac = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const a = Buffer.from(mac), b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Python
import hmac, hashlib, time

def verify(secret: str, raw_body: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    signed = parts["t"].encode() + b"." + raw_body
    mac = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, parts.get("v1", ""))

Retries & the delivery log

If your endpoint is down or answers anything other than 2xx, OrderRestro tries again after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours, then marks the delivery failed. Each attempt has a timeout of 8 seconds and redirects are not followed. Because of retries, an event can arrive more than once — use X-OrderRestro-Delivery to de-duplicate. Delivery history is kept for 30 days.

Network rules

For safety, webhook URLs must use https and may not point at a private, loopback or link-local address (so the server can’t be tricked into calling your internal network). The address is checked again on every attempt.

If OrderRestro runs on your restaurant’s own network and the receiver is a device on it, the operator can allow it by setting WEBHOOKS_ALLOW_PRIVATE=true (and WEBHOOKS_ALLOW_INSECURE=true for plain http) on the server.