Webhooks

Have events sent to your URL as they happen, such as a new order, a closed bill, a Wi-Fi code used or a Wi-Fi box going offline

  1. On the Integrations page, under Webhooks, click Add webhook (the Project needs a website shop with Business Apps, the Business Apps API or the Wi-Fi Hotspot API, and you must be the Owner or an Admin)
  2. Enter an https:// URL reachable from the internet, and pick the events you want (none means every event)
  3. Copy the Signing secret: it is shown only once
  4. Click Test to send a ping event to your URL
EventWhen
order.created, order.updated, bill.closed, booking.created, shift.closed and moreSomething changed in a shop, a website's or the API's, whether from the Console, the staff app or the API (carries shopId)
order.payment_claimed, booking.payment_claimed, booking.cancelledA customer on the website says they paid, or cancels their booking
voucher.usedA Wi-Fi code is used for the first time (the code itself is never sent; the note you gave it, such as a room number, is)
guest.signed_inA guest left an email or phone on the sign-in page (returning says whether they came before)
box.offline, box.onlineA Wi-Fi box stopped calling for over 2 minutes, and when it is back

Each event is a JSON POST. Your system must answer 2xx within 10 seconds. If it does not, we retry automatically, waiting longer each time, for about a day. Deliveries shows what was sent.

JSON
{"id":"evt_…","type":"order.created","createdAt":"2026-10-13T09:30:00Z","shopId":"…","data":{…}}

Check that a webhook really comes from us

Each one carries X-TH-Signature: t=<time>,v1=<signature>. The signature is the HMAC-SHA256 of <time>.<body> with your signing secret. Compute it over the raw body you received, before parsing the JSON.

Node.js
const crypto = require("crypto");
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map(p => p.split("=")));
  const mac = crypto.createHmac("sha256", secret).update(parts.t + "." + rawBody).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(parts.v1));
}
Python
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    mac = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mac, parts["v1"])

Events may arrive out of order: sort them by createdAt, and use id to skip duplicates, since an event can be delivered again.

If something goes wrong

Adding a webhook says the URL must be on the internet

Use an https:// URL that can be opened from outside, not an internal IP or localhost.

Deliveries shows “Failed”

Check the HTTP status your URL answered. We keep retrying until it succeeds or the retries run out. Fix your receiver, then click Test to check.

Still stuck? Let our team help

Open a ticket from the Console. Tell us the site or service, what you did and the message on screen. Our reply comes back in the same ticket, so everything stays in one place.