{"openapi":"3.1.0","info":{"title":"GustoQR API","version":"1.0","description":"Menus, orders and tables of a GustoQR account. Docs: https://www.gustoqr.com/docs/api"},"servers":[{"url":"https://api.gustoqr.com/v1"}],"security":[{"apiKey":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"An API key from gustoqr.com/docs/api (sk-…), or its read-only form (gq_ro_…)"}}},"paths":{"/menus":{"get":{"summary":"List menus","description":"Every menu in the account, without its content.","tags":["Menus"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"menus":[{"id":355,"name":"Sunset Cafe","restaurant_name":"Sunset Cafe","status":"published","currency":"EUR","url":"https://sunset-cafe.gustoqr.com","visits":1840,"qr_scans":1215,"published_at":"2026-10-02T09:12:44Z","updated_at":"2026-11-12T08:30:02Z"}]}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}},"post":{"summary":"Create a menu","description":"A new draft menu. Guests see nothing until it is published.","tags":["Menus"],"parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","currency"],"properties":{"name":{"type":"string","description":"The menu’s name in your panel."},"currency":{"type":"string","description":"ISO 4217 code, such as USD, EUR or TRY."},"restaurant_name":{"type":"string","description":"The venue’s name as guests read it. Defaults to name."},"tagline":{"type":"string","description":"One line under the venue’s name."},"menu_data":{"type":"object","description":"The content: languages, categories and their items."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"menu":{"id":355,"name":"Sunset Cafe","restaurant_name":"Sunset Cafe","status":"draft","currency":"EUR","url":"https://sunset-cafe.gustoqr.com","menu_data":{"languages":["en","de"],"categories":[{"id":"cocktails","name":"Cocktails","items":[{"id":"negroni","name":"Negroni","description":"Gin, Campari, sweet vermouth.","price":12,"dietary":["vegan"],"allergens":[]}]}]},"menu_style":{"menuPrimaryLanguage":"en"}}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/menus/{id}":{"get":{"summary":"Get a menu","description":"One menu with its content (menu_data) and its look (menu_style).","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"menu":{"id":355,"name":"Sunset Cafe","restaurant_name":"Sunset Cafe","status":"published","currency":"EUR","url":"https://sunset-cafe.gustoqr.com","menu_data":{"languages":["en","de"],"categories":[{"id":"cocktails","name":"Cocktails","items":[{"id":"negroni","name":"Negroni","description":"Gin, Campari, sweet vermouth.","price":12,"dietary":["vegan"],"allergens":[]}]}]},"menu_style":{"menuPrimaryLanguage":"en"}}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}},"patch":{"summary":"Update a menu","description":"Send only what changes. menu_data and menu_style are replaced whole, so read the menu, change it and send it back. On a published menu guests see the change at once; translations into the menu’s other languages follow by themselves. The menu as it was is kept as a version you can restore.","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"name":{"type":"string","description":"The menu’s name in your panel."},"restaurant_name":{"type":"string","description":"The venue’s name as guests read it."},"currency":{"type":"string","description":"ISO 4217 code. Prices are relabelled, not converted."},"menu_data":{"type":"object","description":"The whole content."},"menu_style":{"type":"object","description":"The whole look."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"menu":{"id":355,"name":"Sunset Cafe","restaurant_name":"Sunset Cafe","status":"published","currency":"EUR","url":"https://sunset-cafe.gustoqr.com","menu_data":{"languages":["en","de"],"categories":[{"id":"cocktails","name":"Cocktails","items":[{"id":"negroni","name":"Negroni","description":"Gin, Campari, sweet vermouth.","price":12,"dietary":["vegan"],"allergens":[]}]}]},"menu_style":{"menuPrimaryLanguage":"en"}}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/menus/{id}/versions":{"get":{"summary":"List a menu’s versions","description":"The menu as it was before each of its last 50 changes, newest first, whoever made them: source is panel (the owner or the panel’s assistant), api, mcp (a connected assistant) or restore. Edits in the panel save by themselves, so they are kept at most once every two minutes.","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"versions":[{"id":"4812","saved_at":"2026-11-12T18:04:11.000Z","source":"mcp","before":"Negroni: €12 → €13"}]}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/menus/{id}/restore":{"post":{"summary":"Restore a version","description":"Undo: puts the menu’s content and look back as a version has them, taking back the change it was kept before and every change after it. The menu as it was a moment ago is kept too, so a restore can be undone. Whether the menu is published does not change.","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"version_id":{"type":"string","description":"The version, from the list. Left out: the newest, which undoes the last change."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"restored":{"id":"4812","saved_at":"2026-11-12T18:04:11.000Z","source":"mcp","before":"Negroni: €12 → €13"},"undo_version_id":"4815","guests_see_it":"now"}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/menus/{id}/publish":{"post":{"summary":"Publish a menu","description":"Makes a draft live at its own address.","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"custom_url":{"type":"string","description":"The first part of the address: 3 to 30 letters, digits or hyphens."}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"url":"https://sunset-cafe.gustoqr.com"}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/menus/{id}/analytics":{"get":{"summary":"Menu analytics","description":"Visits, QR scans, orders and revenue by day. Best-selling and most-viewed items come with Pro.","tags":["Menus"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"days","in":"query","required":false,"description":"How far back, 1 to 365. Default 30.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"analytics":{"summary":{"total_visits":1840,"total_qr_scans":1215,"total_orders":96,"total_revenue":2890.5},"daily_stats":[{"date":"2026-11-11","visits":71,"qr_scans":48,"orders":5,"revenue":142}],"popular_items":[],"hourly_distribution":{}}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/orders":{"get":{"summary":"List orders","description":"Orders from the POS and from guests ordering on the menu. Poll with date_from to pick up new ones. Ordering comes with Pro.","tags":["Orders"],"parameters":[{"name":"menu_id","in":"query","required":false,"description":"Only this menu’s orders.","schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"description":"One status, several with commas, or all.","schema":{"type":"string","enum":["pending","preparing","ready","served","paid","cancelled","all"]}},{"name":"date_from","in":"query","required":false,"description":"A day (2026-11-01) or a moment (2026-11-01T18:00:00Z).","schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"description":"The same, for the end.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"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}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/orders/{id}":{"patch":{"summary":"Update an order’s status","description":"Moves an order along: preparing, ready, served, paid or cancelled.","tags":["Orders"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","description":"The new status.","enum":["pending","preparing","ready","served","paid","cancelled"]}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"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"}}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/tables":{"get":{"summary":"List tables","description":"The venue’s tables and whether each is free or occupied.","tags":["Tables"],"parameters":[{"name":"menu_id","in":"query","required":false,"description":"Only this menu’s tables.","schema":{"type":"integer"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"tables":[{"id":12,"table_number":"7","capacity":4,"location":"Terrace","current_status":"occupied","current_order_id":9120}]}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/logs":{"get":{"summary":"Activity log","description":"What happened at the till: orders taken and paid, tables opened, moved and closed, staff changes. The newest 500 in the period.","tags":["Activity"],"parameters":[{"name":"menu_id","in":"query","required":true,"description":"The menu whose venue you ask about.","schema":{"type":"integer"}},{"name":"date_from","in":"query","required":false,"description":"A day (2026-11-01) or a moment (2026-11-01T18:00:00Z). Default: 30 days ago.","schema":{"type":"string"}},{"name":"date_to","in":"query","required":false,"description":"The same, for the end. Default: now.","schema":{"type":"string"}},{"name":"type","in":"query","required":false,"description":"Only one kind of entry; waiter is staff changes.","schema":{"type":"string","enum":["order","payment","table","waiter"]}},{"name":"waiter_name","in":"query","required":false,"description":"Only this member of staff.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"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"}]}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}},"/keys/read-only":{"post":{"summary":"Make a read-only key","description":"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.","tags":["Keys"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"key":"gq_ro_Zk3…","read_only":true}}}},"401":{"description":"The API key is missing, wrong or deleted."},"403":{"description":"A read-only key was used to change something, or the plan does not allow it."},"404":{"description":"Not found, or not yours."},"422":{"description":"The request was refused; error.message says why."},"429":{"description":"Over the limit for this minute; Retry-After says how many seconds to wait."}}}}}}