Help · Integrations and API
How to set up webhooks to receive notifications in your system
A webhook is a notification CorekChat sends to your system the moment something happens: an order comes in, its status changes, a reservation is created or cancelled, a table is opened or closed. That way your invoicing, your accounting or your dashboard find out on their own, without having to ask every so often. The first part is for whoever manages the account; the second, for whoever programs the receiver.
#Step 1: open the Webhooks tab
Open «Configuraciones» (Settings) and choose «Webhooks», in the «Desarrolladores» (Developers) group. There you find the addresses that receive notifications (they're called endpoints) and, below, the history of the latest deliveries.
System screenshot, customers onlySettings → Webhooks: endpoints at the top and the latest deliveries belowSee it in your account
#Step 2: create the endpoint
Press «Nuevo endpoint» (New endpoint) and fill in:
- Address (HTTPS): the one given to you by whoever programs the receiver. It
has to start with
https://and be published on the internet; addresses on an internal network aren't accepted. - Description: to recognize it later ("Invoicing system").
- Events: check only the ones that system uses.
Press «Guardar» (Save). You can have up to ten endpoints.
System screenshot, customers onlyNew endpoint: the HTTPS address, a description and the events that system needsSee it in your account
#Step 3: save the secret
When you save, the endpoint's secret appears, only once. Copy it and hand it to whoever programs the receiver: with it they check that each notification came from CorekChat and not from a third party. If it's lost, use «Rotar secreto» (Rotate secret): a new one is generated and the previous one stops working as soon as the rotation finishes, so the new one has to be loaded into the receiver right away.
System screenshot, customers onlyThe secret is shown only once: when the endpoint is created or rotatedSee it in your account
#Reviewing deliveries
Under «Últimas entregas» (Latest deliveries) you see each notification, which endpoint it went to, whether it arrived and what the receiver replied. A «Entregada» (Delivered) delivery is one the receiver accepted; a «Falló» (Failed) one ran out of attempts and can be sent again with «Reintentar» (Retry).
If an endpoint fails five deliveries in a row, it deactivates itself and you get an email: that way a receiver that's down doesn't keep piling up notifications. Once it's fixed, press «Reactivar» (Reactivate). «Desactivar» (Deactivate) does the same by hand, for example during a provider change.
#For whoever programs it: what arrives
Each notification is a POST to the endpoint's address, with a JSON body like
this:
{
"id": "5d0e3c1a-…",
"event": "order.created",
"createdAt": "2026-09-27T13:05:12.000Z",
"data": {
"orderId": "8f1c…",
"branchId": "3a2e…",
"channel": "whatsapp",
"status": "pending",
"total": 42000,
"itemCount": 2,
"createdAt": "2026-09-27T13:05:12.000Z"
}
}
And with these headers:
| Header | What it carries |
|---|---|
X-Corek-Event |
The event name, same as event |
X-Corek-Delivery |
The delivery id, same as id |
X-Corek-Signature |
The signature: t=<seconds>,v1=<signature> |
#The events
| Event | When | What it carries in data |
|---|---|---|
order.created |
An order comes in | orderId, branchId, channel, status, total, itemCount, createdAt |
order.status_changed |
An order changes status | orderId, branchId, previousStatus, status, total, changedAt |
reservation.created |
A reservation is created | reservationId, branchId, status, partySize, scheduledAt, createdAt |
reservation.cancelled |
A reservation is cancelled | reservationId, branchId, partySize, scheduledAt, cancelledBy (customer or staff), cancelledAt |
conversation.escalated |
The assistant hands a conversation to a person | conversationId, channel, reason, escalatedAt |
table.session_opened |
A table is opened in the dining room | sessionId, tableId, tableLabel, branchId, partySize, openedAt |
table.session_closed |
A table is closed | sessionId, tableId, tableLabel, branchId, openedAt, closedAt, total, orderCount |
order.updated |
Your team edits an order from the dashboard (products, quantities, address, payment, note or customer details) | orderId, branchId, status, total, itemCount, changes, updatedAt |
Notifications carry the minimum on purpose. For an order's or reservation's details, query the API with the id you received. The two table events are only sent with the POS module.
The changes field of order.updated is the list of what changed, with these
names: items, total, deliveryAddress, paymentMethod, notes,
customerName, customerPhone and deliveryFee (the delivery fee, when the
customer changes the address of a pending order through the chat). If an order
goes back to an earlier status (for example, from "Sent" to "Preparing"), what
arrives is order.status_changed, with the previous status in
previousStatus.
#Verifying the signature
The signature is an HMAC-SHA256, using the endpoint's secret, over the text
<t>.<body>: the seconds in t, a dot and the body exactly as it arrived,
before parsing it as JSON. Reject the notification if the signature doesn't
match or if t is more than five minutes old: that way nobody can resend an old
notification they captured.
// Node.js — example with Express. Careful: you have to read the RAW body.
const crypto = require('crypto');
const express = require('express');
const app = express();
// The whsec_… from step 3.
const SECRET = process.env.COREK_WEBHOOK_SECRET;
const raw = express.raw({ type: 'application/json' });
app.post('/webhooks/corekchat', raw, (req, res) => {
const body = req.body.toString('utf8');
const header = String(req.get('X-Corek-Signature') || '');
const parts = Object.fromEntries(
header.split(',').map((p) => p.split('=')),
);
const t = Number(parts.t);
const expected = crypto
.createHmac('sha256', SECRET)
.update(`${t}.${body}`)
.digest('hex');
const signatureOk =
parts.v1 &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(parts.v1),
Buffer.from(expected),
);
const recent = Math.abs(Date.now() / 1000 - t) <= 300;
if (!signatureOk || !recent) return res.status(400).end();
const notification = JSON.parse(body);
// …process notification.event and notification.data…
res.status(200).end();
});
# Python — the same calculation.
import hashlib, hmac, time
def valid_signature(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t = int(parts.get("t", "0"))
signed = f"{t}.".encode() + body
expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
recent = abs(time.time() - t) <= 300
return hmac.compare_digest(parts.get("v1", ""), expected) and recent
#How to respond and what happens if it fails
- Respond with a 2xx code in under 10 seconds. If the work takes long, store the notification, respond and process it afterwards.
- If the receiver doesn't answer or responds with a server error (5xx), the notification is retried automatically: up to five attempts, after 30 seconds, 2 minutes, 8 minutes and 32 minutes.
- A client error (4xx) is not retried, because repeating the same thing would give the same error; except 408 and 429, which are.
- The same notification can arrive more than once. Automatic retries keep the
same
id; a manual «Reintentar» (Retry) creates a new delivery with a differentid. To avoid processing the same event twice, go by the event's data: for example, theorderIdtogether with thestatus.