Skip to content
CorekChat Help Customer guides ES

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 different id. To avoid processing the same event twice, go by the event's data: for example, the orderId together with the status.