Move to V1
- Update the receiver to verify
EatNow-Signature(see Verify the signature) and to read the V1 body:typeinstead ofevent,data.object(the partner API reservation:party_size,start_at…) instead ofdata.reservation,data.previous_attributes(changed fields only) instead ofprevious_data. The secret does not change. - In Settings → Integrations → Webhooks, click Upgrade to V1 on the webhook. This cannot be undone.
- Deliveries already in the journal keep their legacy body and signature, resends included; everything published afterwards is V1.
Headers
Custom headers from the configuration are applied first; the headers above are
applied after them and always win (names compared case-insensitively), so a
custom header can never replace them. Any custom header whose name starts with
X-EatNow- is dropped. The signature covers the raw body
only, not a concatenation of the timestamp header and body.
Payload
Illustrative creation event; IDs and timestamps are examples:
The reservation always includes
id, restaurantId, status, group_size,
reservation_date, reservation_time, created_at, updated_at, source,
tags and metadata (which can be null). This is not the Partner API
reservation format: it uses group_size and separate date/time fields, not
party_size and start_at.
reservation_date carries the restaurant’s service-day label, normally
serialized as an ISO midnight string. Preserve its YYYY-MM-DD part; do not
convert that UTC midnight to another timezone to determine the day.
reservation_time is a local HH:mm value. Do not treat the pair as a UTC
instant. created_at, updated_at and the event timestamp are UTC instants.
Optional content depends on the reservation:
customer: ID, nullableexternal_id, name and available email, phone and language.tables: objects withidandname; no nested room.room:idand multilingualnameobject, not a string.waiter:idandname.payments:id,amount,currency,status,provider, optionalprovider_idandcreated_at.custom_message,allergiesandtotal_amount_paid: omitted when empty or zero by the current transformer. Omission does not mean “unchanged”.
total_amount_paid and payments[].amount
are integers in cents (5000 = 50.00 in the payment’s currency). Divide by
100 to display them.
previous_data accompanies most updates. When the change comes from a staff
action, a partner call or an automation, it is the full reservation before the
change. When it comes from a register (NowOS), it carries the reservation’s own
fields before the change (status, date, time, covers…) with the current
relations (customer, tables, room).
shift: may appear in the test sample; ordinary reservation events currently omit it. Do not require it.
Verify the signature
Compute HMAC-SHA256 over the exact bytes received, using the webhook secret. Reject missing or malformed signatures, then compare digests in constant time. Only parse the JSON after verification. Parsing and reserializing JSON changes whitespace or field order and can invalidate a legitimate signature.- Node.js
- Python
- PHP
Express receiver example
Save the Node.js verifier asverify-signature.js. Register the raw-body
webhook route before any express.json() middleware. This example only
validates and logs event identifiers; replace that demonstration handling with
your processing before acknowledging a production event. Load the real secret
from secure server-side storage rather than committing it.
