Build on GrillTab.
Read sales, checks, menus, labor and inventory, send orders in, keep the menu in sync, and get signed webhooks the moment something happens. The API is part of every plan.
Getting started
The restaurant owner makes a key in the owner dashboard (API keys), picks the locations and what it can read or change, and gives it to you. The key is shown once. Send it on every call:
curl https://hq.grilltab.com/api/v1/locations \
-H "Authorization: Bearer gt_live_…"Every call is relayed live to the location's own POS computer, so data is always current. If that computer is offline the call answers 502; try again later. Limit: 120 calls a minute per key (429 past that). Errors are JSON, in plain English: {"error": "…"}.
Try it now: the sandbox
This public test key works on a made-up bar, Demo Taproom (location 1), with realistic sales, checks, menu, labor and inventory. Every call answers exactly like the real API; writes are checked and echoed back, never kept. Up to 60 calls a minute.
curl "https://hq.grilltab.com/api/v1/locations/1/sales?from=2026-10-24&to=2026-10-30" \
-H "Authorization: Bearer gt_test_demo_taproom_public_sandbox"Read
| Call | Scope | Returns |
|---|---|---|
GET /locations | any | The key's locations: id, name, address, online |
GET /locations/{id}/sales?from&to | sales | Totals, by category and by business day (up to 92 days) |
GET /locations/{id}/checks?from&to&after&limit | checks | Paid checks with items and payments; page with after = next_after (up to 31 days, 200 a page) |
GET /locations/{id}/menu | menu | Items with ids, groups, prices, sizes, 86 status and counts; choices |
GET /locations/{id}/labor?from&to | labor | Clock-ins with job, hours and rate |
GET /locations/{id}/inventory | inventory | On hand, par, unit cost, vendor |
GET /locations/{id}/employees | employees | Names, roles, active (never PINs or pay) |
Dates are business days (YYYY-MM-DD); a business day ends at the location's day-end time, not midnight. Times are the store's local time. Money is in dollars.
Write
Only keys the owner gave a write scope can change anything, and every change shows in the location's activity log.
| Call | Scope | Does |
|---|---|---|
POST /locations/{id}/menu/items/{item} | menu_write | {"stock": "out" | "today" | "in" | "count", "count": 6} for 86s and counts; {"price": 12.5, "size_id": 3} for a price (size optional) |
POST /locations/{id}/orders | orders_write | Sends an order to the POS Orders hub, like an online order. Priced from the POS menu; the guest pays at the POS. |
GET /locations/{id}/orders/{external_id} | orders_write | Its status: pending, accepted (with check_number) or failed (with error) |
Send an order
POST /api/v1/locations/12/orders
{
"external_id": "A-1001", // your order number: a retry never makes a second order
"type": "pickup", // pickup | delivery | dine_in (with "table")
"pickup_at": "2026-11-01 18:30", // optional, store time: the kitchen fires it on time
"customer": { "name": "Pat", "phone": "5551234567" },
"items": [ { "menu_item_id": 534, "qty": 2, "notes": "extra crispy",
"modifiers": [ { "name": "Ranch" } ] } ],
"notes": "Leave at the bar"
}The location picks orders up within a minute while it's online, prints the ticket and sends it to the kitchen screens.
Webhooks
A manager adds your https address in the POS back office (Integrations → Webhooks) and gives you its secret. Each event is a POST of { id, event, created_at, location, data } with the headers X-GrillTab-Event, X-GrillTab-Delivery and X-GrillTab-Signature: t=<unix seconds>,v1=<hex>. Answer with a 2xx. Failed deliveries are retried after 1 min, 5 min, 30 min, 2 h, 6 h and 24 h, and every delivery can be sent again from the POS.
| Event | When |
|---|---|
order.paid | A check is paid in full |
payment.created | A payment is taken (cash, card, gift card…) |
payment.refunded | A payment is refunded |
item.86 | An item or choice is 86'd or set to a count |
item.back | An item or choice is back in stock |
menu.changed | The menu was changed |
punch.in | Someone clocks in |
punch.out | Someone clocks out |
drawer.closed | A cash drawer is closed |
keg.changed | A keg is tapped, kicked or changed |
activity.* | Every line of the activity log: voids, comps, discounts, no-sales, refunds, settings changes… |
Check the signature
const crypto = require('crypto');
function valid(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('=')));
const want = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return Math.abs(Date.now() / 1000 - Number(t)) < 300 && v1.length === want.length && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(want));
}Nightly export
Each morning a location can send the business day as CSV files in one .zip to a folder, an https address or an SFTP server (Settings → Providers → Nightly export). Each .zip has a FIELDS.txt with the list below. The owner can also download every table at any time (Settings → Providers → Your data).
OrderDetails.csv
| Column | What it is |
|---|---|
id | Check number (the same number on the receipt and in every other file) |
tab_name | Tab or guest name |
table_no | Table |
guests | Number of guests |
dining_option | Dine in, to go, delivery… |
source | pos, online, kiosk, qr, phone, app… |
status | open, closed or void |
opened_at | Opened (store time) |
closed_at | Paid and closed |
server | Employee who owns the check |
ItemSelectionDetails.csv
| Column | What it is |
|---|---|
order_id | Check number |
item_id | This line's own number |
name | Item as rung in |
qty | Quantity |
unit_price | Price of one with its choices, before discounts |
revenue_class | Food, Beer, Liquor, Wine… |
modifier_text | Choices and notes |
status | ordered, sent, made, void |
comp | 1 if comped |
sent_at | Sent to the kitchen or bar |
PaymentDetails.csv
| Column | What it is |
|---|---|
order_id | Check number |
payment_id | This payment's own number |
method | cash, card, gift, house account… |
amount | Paid toward the check, without tip |
tip | Tip |
card_fee | Surcharge or convenience fee charged to the guest |
created_at | When it was taken |
employee | Who took it |
TimeEntries.csv
| Column | What it is |
|---|---|
id | Shift number |
employee | Employee |
job | Job worked |
clock_in | Clock in (store time) |
clock_out | Clock out (blank if still on) |
rate | Hourly rate for that job |
Changelog
- 2026-10-31: Write API (86s, counts and prices; orders in, with status). A field guide in every nightly export.
- 2026-10-31: Read API v1 (sales, checks, menu, labor, inventory, employees) and signed webhooks.
Questions or a partnership? Get in touch.
Ready to run a tighter bar?
Sign up in a few minutes. Your equipment ships configured for your bar, ready to plug in and take orders.