Skip to main content

Statuses

The API vocabulary is provider-agnostic: bookedconfirmedarrivedseatedfinished, with no_show and cancelled as exits at any point.
  • booked = not yet confirmed. Optional to push; never annotates the floor plan on the till.
  • Cancellation is always status: "cancelled" via PUT — there is no DELETE.
EatNow mapping (reference implementation): PARTIALLY_SEATED sends the actually-occupied tables in tableIds.

Seating is bidirectional

You seat — write status: "seated" with tableIds. The API checks occupancy synchronously (409 table_occupied if the table is taken, with the occupant in occupiedBy). On success the seat is accepted, not yet executed: the till opens the order within a couple of seconds and you receive table.seated carrying your reservationExternalId as confirmation. If a walk-in grabbed the table in that window, you receive reservation.seat_rejected instead — re-seat the party; the instruction will not retry until you change the reservation. A walk-in typed in your own system is just a reservation that arrives already seated. Staff seats — the till links the reservation and you learn about it through the same table.seated. Treat it as you would your own seat confirmation (e.g. EatNow flips to SEATED). After the link, the order belongs to the till. Your updates can still move the party (tableIds → the till executes the move) and adjust covers, but cancelling the reservation never closes the order — closing and paying are staff gestures. Staff-side moves come back to you as table.freed(moved) + table.seated. Your own webhook reactions echo back as PUTs with a newer updatedAt and identical content — that is expected and converges: identical content is a no-op on the till.

Prepayments and imprints

Money you collected upfront travels on the reservation:
  • prepaidMinor (deposit or full prepayment — same mechanics) is spendable on the bill: staff applies it in the payment sheet and it shows up in check.paid as the provider_prepaid tender — that is your consumption signal for reconciliation. The till applies at most the amount due.
  • imprint is display-only on the till (a guarantee badge). Releasing or charging it stays entirely on your side — key it on check.paid / table.freed (normal payment) and reservation.status_changed(no_show).

Guest profile and CRM

Each reservation carries a guest object and an internalNote. NowOS never writes the guest record back — you own it. Two notes, two audiences:
  • guest.allergies — the guest’s own note (allergies and preferences). Shown to staff on the reservation. Read-only on the till. Send the guest block whenever you have this note, even for a party you cannot nameguest.name is nullable precisely so an allergy is never lost for want of a customer record.
  • internalNote (on the reservation, not the guest) — a staff-only note (EatNow’s custom_message). Never shown to the guest. This one is two-way: it lands on the order the booking seats, where it prints on every kitchen ticket, and an edit made at the till comes back to you as reservation.note_changed. Last writer wins on updatedAt, and you are expected to echo it back like any other change.

The 360° profile (read-back)

NowOS is the transactional system of record: every paid check is pushed to you as check.paid, carrying lines[], tenders[], and the reservationExternalId that ties the spend to the party. You keep the CRM. Aggregate that history and return it on the guest, so the till can show a full profile when staff open the reservation. These guest fields are read-back only — you send them, NowOS displays them, NowOS never recomputes them. There is no two-way sync of a shared field: one writer per field. The loop is: check.paid → you update the guest’s history on your side → the next reservation read for that guest carries fresh aggregates → the till shows them. Leave any aggregate null if you don’t track it; the till just hides that line.