{"openapi":"3.1.0","info":{"title":"Chappa POS API (for React devs)","version":"2.0.0-but-with-pictures","summary":"The exact same API. Now with rounded corners, bigger fonts, and fewer scary words.","description":"## Hi 👋\n\nThis is the **whole** API. Two blocks. You send JSON, you get JSON back. Nothing re-renders. There is no `useEffect`. Nobody gets hydrated.\n\nIf you can write `fetch()`, you can use this. If you can't write `fetch()` yet — that's fine, a renderer is on the way that draws this whole thing as little colored blocks you drag around. Like Scratch. Like when you were eight and actually shipped something.\n\n## The two things you can do\n\n- 📖 **Look at the menu** → `POST /api/v2/client/getinfo`\n- 🍔 **Order food** → `POST /api/v2/client/order`\n\nThat's the entire framework. Sorry — *API*. Force of habit.\n\n## Logging in\n\nPut your token in a header called `X-API-Token`. A header. The part that isn't the body. You've met them — they live in the Network tab, between `npm install`s.\n\nLooking at the menu is **public**: no token needed. You only need the token to actually **order**, because ordering does something real in the real world.\n\n## What you get back\n\nEverything looks like this:\n\n```\n{ \"v\": 2, \"status\": 0, \"data\": { ...the good stuff... } }\n```\n\n`status` is `0` when it worked and `1` when it didn't. When it's `1` we put what went wrong in `error.msg`, in actual human sentences. No stack-trace archaeology. You're welcome.\n\n## Rate limiting\n\nThere is one. You won't reach it.","contact":{"name":"Chappa POS","url":"https://ceapa.cool"},"x-renderer":{"style":"scratch","audience":"react-devs","vibe":"kids-game","theme":{"background":"#fff7e6","blockFont":"rounded","palette":{"read":"#4c97ff","write":"#ff6680","auth":"#ffab19","ok":"#59c059","oops":"#ff8c1a"}},"instructions":"Draw each endpoint as a draggable Scratch block. The request body snaps in as little input bubbles; the response pops out the other side. That is the entire mental model. You do not need a state manager for a hamburger."}},"servers":[{"url":"https://locatia-ta.ceapa.cool","description":"Înlocuiește cu adresa localului tău (ex: https://constanta.ceapa.cool)"}],"security":[{"ChappaApiToken":[]}],"tags":[{"name":"Look","description":"Reading things. Safe. Nothing explodes.","x-color":"#4c97ff"},{"name":"Do","description":"Doing things. A little more responsibility.","x-color":"#ff6680"}],"paths":{"/api/v2/client/getinfo":{"post":{"tags":["Look"],"operationId":"lookAtTheMenu","summary":"Look at the menu 📖","description":"Ask for the parts of the venue you want — all at once, in **one** request. One. Not one request per component. Not one per `useQuery`. One.\n\nList what you want inside `query`. Want the menu **and** the categories? Send `{ \"query\": { \"items\": {}, \"categories\": {} } }`. Each thing you ask for comes back in `data` under the same name.\n\nThings you can ask for: `info` (the venue), `categories`, `items` (the menu), `tables`, `zones`, `events`. Use `{}` as the value — they're useful with no options at all.\n\nNo loading spinner required. We know you'll add three anyway.","x-block":{"color":"#4c97ff","shape":"hat","emoji":"📖","label":"when I want the menu","say":"give me the venue. all of it. one request.","difficulty":"you can do this"},"security":[{"ChappaApiToken":[]},[]],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MenuRequest"},"examples":{"theUsual":{"summary":"Give me the venue, its categories, and the menu","value":{"venue":3,"query":{"info":[],"categories":[],"items":[]}}},"justTheMenu":{"summary":"Honestly I just want the food list","value":{"venue":3,"query":{"items":[]}}}}}}},"responses":{"200":{"description":"Here's your stuff.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"},"examples":{"stuff":{"summary":"Each key you asked for, filled in","value":{"v":2,"status":0,"data":{"info":{"name":"Demo Bistro"},"categories":[{"id":1,"name":"Burgers"}],"items":[{"id":220,"name":"Classic Burger","price":32}]},"meta":[]}}}}}},"default":{"$ref":"#/components/responses/Oops"}}}},"/api/v2/client/order":{"post":{"tags":["Do"],"operationId":"orderFood","summary":"Order food 🍔","description":"Send a `table` and a list of `items`. Each item is `{ \"item\": <product id>, \"count\": <how many> }`. Add `\"notes\"` if the customer is particular.\n\nThe kitchen finds out **immediately**. You don't wire up a single websocket — we already did that part, in C++, while you were picking a state library.\n\n**Bonus, because we both know your users double-tap buttons:** add any number as `idempotency_key` and retry as much as you like. Same number = same order, no duplicates. It's that easy, and no, you don't need a reducer for it.","x-block":{"color":"#ff6680","shape":"stack","emoji":"🍔","label":"order food for table ( )","say":"table plus a list of things. food appears. magic.","difficulty":"still fine, breathe"},"security":[{"ChappaApiToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"},"examples":{"twoBurgers":{"summary":"Two burgers, one with no onions","value":{"table":11,"items":[{"item":220,"count":2},{"item":104,"count":1,"notes":"no onions"}]}},"retrySafe":{"summary":"Same thing, but safe to retry forever","value":{"table":11,"idempotency_key":7,"items":[{"item":220,"count":1}]}}}}}},"responses":{"200":{"description":"Order placed. The kitchen already knows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Ok"},"examples":{"placed":{"summary":"Done","value":{"v":2,"status":0,"data":{"placed":true,"id_sep":1},"meta":[]}}}}}},"default":{"$ref":"#/components/responses/Oops"}}}}},"components":{"securitySchemes":{"ChappaApiToken":{"type":"apiKey","in":"header","name":"X-API-Token","description":"Your venue token. Goes in a header. Looking at the menu doesn't need it; ordering does."}},"responses":{"Oops":{"description":"You did something wrong, and we'll tell you what — in words.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Oops"},"examples":{"oops":{"summary":"Something was off about an item","value":{"v":2,"status":1,"error":{"code":"INVALID_ITEM","msg":"Format invalid pentru produs sau cantitate."}}}}}}}},"schemas":{"MenuRequest":{"type":"object","required":["query"],"description":"Ask for the parts of the venue you want.","properties":{"venue":{"type":"integer","description":"Which venue (location id). Ask whoever gave you the token."},"query":{"type":"object","description":"The bits you want. Keys: info, categories, items, tables, zones, events. Each value is {}.","example":{"info":[],"categories":[],"items":[]}}}},"OrderRequest":{"type":"object","required":["table","items"],"description":"A table, and the things on it.","properties":{"table":{"type":"integer","description":"Table number."},"items":{"type":"array","minItems":1,"description":"What to order. At least one.","items":{"$ref":"#/components/schemas/OrderItem"}},"id_sep":{"type":"integer","description":"Optional. Which sub-bill. Leave it out and we'll figure it out."},"idempotency_key":{"type":"integer","description":"Optional. Any number. Reuse it to retry safely without ordering twice."}}},"OrderItem":{"type":"object","required":["item","count"],"properties":{"item":{"type":"integer","description":"Product id (you get these from the menu)."},"count":{"type":"integer","minimum":1,"description":"How many. A whole number you can type yourself — no `is-even`, no `is-odd`, no `is-number`, no eleven transitive dependencies. Just a number."},"notes":{"type":"string","description":"Optional. e.g. \"no onions\"."}}},"Ok":{"type":"object","required":["v","status","data"],"description":"It worked.","properties":{"v":{"type":"integer","const":2},"status":{"type":"integer","const":0,"description":"0 means it worked."},"data":{"type":"object","description":"The good stuff you asked for."},"meta":{"type":"object","description":"Boring extra info you can ignore."}}},"Oops":{"type":"object","required":["v","status","error"],"description":"It didn't work, and here's why.","properties":{"v":{"type":"integer","const":2},"status":{"type":"integer","const":1,"description":"1 means you messed up."},"error":{"type":"object","required":["code","msg"],"properties":{"code":{"type":"string","description":"A short machine-readable label."},"msg":{"type":"string","description":"What went wrong, in a real sentence."}}}}}}}}