Webhooks
Subscribe to reservation and voucher events with HMAC-signed deliveries and exponential retries.
Webhooks let you react to events in DineOS without polling. Subscribe to one or more event types, and we will POST a signed JSON payload to your endpoint every time the event occurs.
Events
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| reservation.created | event | — | — | Fired when a new reservation is recorded, regardless of channel. |
| reservation.updated | event | — | — | Fired on any change to an existing reservation (time, party size, table, status). |
| reservation.cancelled | event | — | — | Fired when status transitions to cancelled. |
| voucher.redeemed | event | — | — | Fired on every full or partial voucher redemption. |
Payload shape
{
"id": "evt_01HQXZ5J4M3K7P9Q1B",
"type": "reservation.created",
"created_at": "2026-04-16T08:42:11Z",
"data": {
"id": "rsv_01HQXZ5J4M3K7P9Q1C",
"starts_at": "2026-04-20T19:30:00Z",
"party_size": 2,
"status": "confirmed"
}
}Signature verification
Every delivery includes an X-DineOS-Signature header with an HMAC-SHA256 of the raw body using your endpoint's signing secret. Verify it in constant time before processing the payload:
import crypto from 'node:crypto';
export function verifyDineosWebhook(rawBody, signatureHeader, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const provided = Buffer.from(signatureHeader, 'hex');
const expectedBuf = Buffer.from(expected, 'hex');
if (provided.length !== expectedBuf.length) return false;
return crypto.timingSafeEqual(provided, expectedBuf);
}Retries
Non-2xx responses are retried with exponential backoff for up to 24 hours: 30 seconds, then 1 minute, 5 minutes, 30 minutes, 2 hours, and 6 hours. After 24 hours the delivery is marked as failed and surfaces in the dashboard. Use the replay button to retry manually after fixing the issue.