Want an assistant to do it instead of code? The same account works from ChatGPT, Claude and Cursor through the MCP server.
Start in a minute
Make a key, then ask for your menus.
export GUSTOQR_API_KEY="sk-…"
curl "https://api.gustoqr.com/v1/menus" \
-H "Authorization: Bearer $GUSTOQR_API_KEY"Basics
| Base address | https://api.gustoqr.com/v1 |
| Sign each request | Authorization: Bearer sk-… |
| Read-only keys | Start with gq_ro_. They answer every GET and refuse the rest with 403. Make one in the box above. |
| Limits | 1200 reads and 300 changes a minute per key. More |
| Undo | The menu as it was before each of its last 50 changes is kept, whether it came from the API, an assistant or the panel. Restore a version |
| Format | JSON, UTF-8. Send Content-Type: application/json with a body. |
| OpenAPI file | https://api.gustoqr.com/v1/openapi.json, no key needed |
| Price | Free on every plan. Orders and tables need ordering turned on, which comes with Pro. |
| Browsers | Any origin may call it (CORS is open), but a key in a web page is a key anyone can read. Call from a server. |
Errors
A failed request answers with its HTTP status and one shape:
{
"error": {
"code": "unauthorized",
"message": "The API key is missing, wrong or deleted.",
"docs": "https://www.gustoqr.com/docs/api"
}
}| Status | Code | What happened |
|---|---|---|
| 400 | invalid_json | The body is not a JSON object. |
| 401 | unauthorized | The key is missing, wrong or deleted. |
| 403 | forbidden | Your plan or your role does not allow it. |
| 403 | read_only_key | A read-only key tried to change something. |
| 404 | not_found, no_version | No such thing, or it belongs to another account. |
| 422 | invalid_request | A field is missing or wrong. The message says which. |
| 429 | rate_limited | Over the limit for this minute. Retry-After says how long to wait. |
| 5xx | internal_error, bad_gateway | Our side. Safe to retry a GET. |
Limits
| What | A minute | Counted |
|---|---|---|
| Reads (every GET) | 1200 | per key |
| Changes (POST and PATCH) | 300 | per key |
| Without a key (the OpenAPI file, sign-in) | 300 | per address |
Every answer says where you stand: RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset, the seconds until the count starts again. Over the limit you get a 429 with Retry-After.
How a menu is built
A menu has two parts. menu_data is the content: its languages, then categories, each with its items. menu_style is the look. To change the content, read the menu, change menu_data and send it back whole.
| Item field | Type | What it is |
|---|---|---|
id | string | Yours to choose, unique in the menu. Keep it stable: orders and translations follow it. |
name, description | string | In the menu’s main language. The other languages are translated for you. |
price | number | In the menu’s currency, as guests read it: 12 or 12.5. |
image | string | The address of a photo. |
dietary, allergens | string[] | Diet tags and allergens shown on the item. |
Fields you do not know are safe to leave as they are. Do not drop them when you send the menu back.
Menus
Orders
List orders
GET /orders
Orders from the POS and from guests ordering on the menu. Poll with date_from to pick up new ones. Ordering comes with Pro.
| Query | Type | What it is |
|---|---|---|
menu_id | integer | Only this menu’s orders. |
status | string | One status, several with commas, or all. One of: pending, preparing, ready, served, paid, cancelled, all. |
date_from | string | A day (2026-11-01) or a moment (2026-11-01T18:00:00Z). |
date_to | string | The same, for the end. |
curl "https://api.gustoqr.com/v1/orders?status=pending,preparing&date_from=2026-11-12" \
-H "Authorization: Bearer $GUSTOQR_API_KEY"{
"orders": [
{
"id": 9120,
"order_number": "ORD-0042",
"status": "preparing",
"table_number": "7",
"total_amount": 31.5,
"items": [
{
"id": "negroni",
"name": "Negroni",
"quantity": 2,
"price": 12
}
],
"created_at": "2026-11-12T18:04:11Z"
}
],
"summary": {
"total_orders": 1,
"total_amount": 31.5,
"pending_count": 0,
"preparing_count": 1,
"ready_count": 0
}
}Update an order’s status
PATCH /orders/:id
Moves an order along: preparing, ready, served, paid or cancelled.
| Body | Type | What it is |
|---|---|---|
statusrequired | string | The new status. One of: pending, preparing, ready, served, paid, cancelled. |
curl -X PATCH "https://api.gustoqr.com/v1/orders/9120" \
-H "Authorization: Bearer $GUSTOQR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"ready"}'{
"order": {
"id": 9120,
"order_number": "ORD-0042",
"status": "ready",
"table_number": "7",
"total_amount": 31.5,
"items": [
{
"id": "negroni",
"name": "Negroni",
"quantity": 2,
"price": 12
}
],
"created_at": "2026-11-12T18:04:11Z"
}
}Tables
List tables
GET /tables
The venue’s tables and whether each is free or occupied.
| Query | Type | What it is |
|---|---|---|
menu_id | integer | Only this menu’s tables. |
curl "https://api.gustoqr.com/v1/tables?menu_id=355" \
-H "Authorization: Bearer $GUSTOQR_API_KEY"{
"tables": [
{
"id": 12,
"table_number": "7",
"capacity": 4,
"location": "Terrace",
"current_status": "occupied",
"current_order_id": 9120
}
]
}Activity
Activity log
GET /logs
What happened at the till: orders taken and paid, tables opened, moved and closed, staff changes. The newest 500 in the period.
| Query | Type | What it is |
|---|---|---|
menu_idrequired | integer | The menu whose venue you ask about. |
date_from | string | A day (2026-11-01) or a moment (2026-11-01T18:00:00Z). Default: 30 days ago. |
date_to | string | The same, for the end. Default: now. |
type | string | Only one kind of entry; waiter is staff changes. One of: order, payment, table, waiter. |
waiter_name | string | Only this member of staff. |
curl "https://api.gustoqr.com/v1/logs?menu_id=355&date_from=2026-11-12" \
-H "Authorization: Bearer $GUSTOQR_API_KEY"{
"logs": [
{
"id": "order_9120",
"type": "order",
"action": "Order created",
"details": "2 × Negroni",
"amount": 24,
"table_number": "7",
"order_id": 9120,
"waiter_name": "Deniz",
"timestamp": "2026-11-12T18:04:11Z"
}
]
}Keys
Make a read-only key
POST /keys/read-only
The read-only form of the key you call with: it reads everything that key reads and changes nothing. Give it to dashboards, reports and assistants that only need to look. The same key always gives the same read-only key, and deleting the key cuts its read-only form off too. Limits are counted per key: 1200 reads and 300 changes a minute.
curl -X POST "https://api.gustoqr.com/v1/keys/read-only" \
-H "Authorization: Bearer $GUSTOQR_API_KEY"{
"key": "gq_ro_Zk3…",
"read_only": true
}Help
Something missing or not working? Write to support@luvi.io and say what you are building.