Send orders to a kitchen screen
The key needs See orders and sales and Update orders (orders:read and orders:write). Give each branch's screen a key limited to that branch, so it only sees that branch's orders.
1. Poll for open orders
curl "https://serveu-psi.vercel.app/api/v1/orders?status=new,preparing,ready&limit=100" -H "Authorization: Bearer serveu_live_XXXX…"- Poll every 5–10 seconds. That is 6–12 of the key's 60 requests per minute, leaving room for the buttons.
- A page holds up to 100 orders; if
next_cursorisn't null, fetch the next page too. - Prefer webhooks, which tell you about a new order the moment it arrives: see the section below.
2. Show each order
number(e.g.A-042),typeandstatus.items:quantity,name,size,optionsand the line'snote; plus the order's ownnote.- Where it goes:
table.labelfor dine-in, orcar.plateandcar.descriptionfor orders from the car.
3. Buttons
Each button sends one action. Use <order id>-<action>-<order.updated_at> as the Idempotency-Key, with the updated_at of the order the screen is showing:
curl -X POST https://serveu-psi.vercel.app/api/v1/orders/<order id>/actions -H "Authorization: Bearer serveu_live_XXXX…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: <order id>-start-2026-11-08T09:41:12.482Z" \
-d '{"action":"start"}'- A retry of the same tap (or a second screen tapping the same button) sends the same key and gets the same answer.
- An error answer is stored with its key for 24 hours. Because
updated_atmoves whenever the order changes, a tap after the order changed (say, it was marked paid afterserveansweredpayment_required) uses a new key and is tried again, instead of replaying the old error.
start: new → preparing.ready: → ready.serve: → served, and the customer gets their stamps or points.- Two screens tapping the same button is fine: repeating an action that already happened answers
replayed: true. rejectneeds areason, one ofitem_unavailable,closing_soon,suspicious,cancelled_by_customer,other; without one the answer is400 invalid_request. Rejecting an order paid online refunds it in full.- If the business wants counter orders paid first,
serveanswers409 payment_requireduntil the order is paid — and when it wants payment before preparing, so doesstart. Mark a counter payment with{"action":"mark_paid","method":"cash"}(cash,card,apple_payorother), then send the action again. - An action the order's status doesn't allow answers
409 order_not_actionable: refresh the screen.
The answer to every action is the updated order, so the screen can redraw without waiting for the next poll. See Errors for every code.
4. Get orders pushed instead of polling
Add a webhook endpoint for the screen's URL with the events order.created and order.status_changed: the owner does it in Settings → Developers → Webhooks, or your system does it with a key that has webhooks:manage (see Webhooks). Verify every request's signature first.
- On
order.created, show the ticket fromdata.object: itsitems, table or car and note, as in step 2. It arrives when the order reaches the counter (a pay-now order, once it's paid). - On
order.status_changed, move or remove the ticket (data.object.statusis the new status,data.previous_statusthe one before). Ignore an event whosedata.object.updated_atisn't newer than the one the screen shows: events can arrive late, twice or out of order, and the screen's own taps come back this way too. - The buttons don't change:
POST /api/v1/orders/{id}/actionswithstart,readyorserveand theIdempotency-Keyfrom step 3. - Keep a slow poll — say once a minute, with
updated_since(see Pagination, filtering & sync) — as a safety net for a missed delivery. - A screen for one branch can use an endpoint limited to that branch: the owner picks the branch in the dashboard, or a key for all branches sends
branch_id(a key limited to one branch can't manage webhooks).