{"openapi":"3.1.0","info":{"title":"ManuBBQ Ordering for AI Agents","version":"1.0.0","description":"Order BBQ from ManuBBQ (Ploiești, Romania) on behalf of a human. Read-only menu, placing an order that the human pays on a Stripe-hosted page, and cancelling it while still unpaid.\n\nCancelling: cancel_order works only while the order is unpaid and not yet sent to the kitchen. The server re-checks Stripe and expires the payment link first; paid or in-kitchen orders return 409 order_not_cancellable with the restaurant's phone and email. Repeating it on a cancelled order returns that order.\nOrder rules: Minimum order: 50 RON, counted on the items subtotal (items + options, before delivery fee and discounts). quote_order reports minimumOrder {minOrderRon, met, shortfallRon} and orderable=false when it is not met; place_order then returns 422 below_minimum_order (details.shortfallRon), so ask the human to add items first. Last orders: 22:00 Europe/Bucharest time. From 22:00 until midnight get_availability shows isOpen=false, reason after_last_order, and quote_order/place_order return 409 ordering_closed (not retryable tonight).\nSafety: Unpaid orders expire: the payment link is valid for AGENT_CHECKOUT_EXPIRES_MIN minutes (default 60) and the order is then automatically cancelled (status expired); paid orders are never touched. Phone numbers must be E.164 (+40712345678) or Romanian national format (0712 345 678) and are stored as E.164. The restaurant can pause agent ordering (place_order returns 503 ordering_paused) while menu, quotes, status and cancel keep working. All endpoints are public: an API key is optional and only raises rate limits.\n\nAgent instructions: You order food from ManuBBQ (BBQ restaurant, Ploiești, Romania) for a human. Flow: get_menu -> get_availability -> (check_delivery for delivery) -> quote_order -> show the human the exact items and totalRon and get an explicit yes -> place_order (fresh idempotency key) -> give the human paymentUrl -> poll get_order_status at most every 30 s. If the human wants to cancel before paying: cancel_order. Never place an order the human has not confirmed. Use the human's real name, email and phone. The human pays on the Stripe page themselves; you never handle card data. The kitchen only receives the order after payment. Prices are in RON and computed by the server. Orders cannot be changed via the API. Only call cancel_order when the human asks; it works only while the order is unpaid, otherwise give the human the restaurant's phone from error.hint. On an error, read error.hint. If error.retryable is true, wait (Retry-After) and retry; otherwise fix the request or ask the human.\n\nLimits: max 20 distinct items, max 20 per item, max 1500 RON per order; rate-limited per IP. Card payment only; no promo codes or points.\nDocs: https://manubbq.ro/llms.txt. MCP server: https://manubbq.ro/mcp. info.version 1.0.0 is the contract version of the /api/v1 paths.","x-ai-instructions":"You order food from ManuBBQ (BBQ restaurant, Ploiești, Romania) for a human. Flow: get_menu -> get_availability -> (check_delivery for delivery) -> quote_order -> show the human the exact items and totalRon and get an explicit yes -> place_order (fresh idempotency key) -> give the human paymentUrl -> poll get_order_status at most every 30 s. If the human wants to cancel before paying: cancel_order. Never place an order the human has not confirmed. Use the human's real name, email and phone. The human pays on the Stripe page themselves; you never handle card data. The kitchen only receives the order after payment. Prices are in RON and computed by the server. Orders cannot be changed via the API. Only call cancel_order when the human asks; it works only while the order is unpaid, otherwise give the human the restaurant's phone from error.hint. On an error, read error.hint. If error.retryable is true, wait (Retry-After) and retry; otherwise fix the request or ask the human.","contact":{"name":"ManuBBQ","url":"https://manubbq.ro/developers"}},"servers":[{"url":"https://manubbq.ro"}],"tags":[{"name":"ordering","description":"Menu, quotes and orders"}],"paths":{"/api/v1/agent/menu":{"get":{"operationId":"get_menu","tags":["ordering"],"summary":"Get the current menu (RON prices, options, allergens)","description":"Step 1. Returns orderable items with integer ids, RON prices, Romanian names (English when available), allergens if published, and option groups with min/max selections. Use these ids in quote_order.","responses":{"200":{"description":"Menu","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Menu"},"example":{"currency":"RON","language":"ro","notes":["Prices are in RON (Romanian lei) and include VAT."],"categories":[{"id":7,"name":"Burgeri","nameEn":"Burgers","sortOrder":1}],"items":[{"id":101,"name":"Burger Manu","nameRo":"Burger Manu","nameEn":"Manu Burger","description":"Vită, cheddar, sos BBQ","descriptionRo":"Vită, cheddar, sos BBQ","descriptionEn":"Beef, cheddar, BBQ sauce","allergens":["gluten","lapte"],"priceRon":45,"categoryId":7,"optionGroups":[{"id":51,"name":"Sos","required":true,"minSelections":1,"maxSelections":1,"options":[{"id":511,"name":"BBQ","nameEn":"BBQ","priceRon":0},{"id":512,"name":"Usturoi","nameEn":"Garlic","priceRon":0}]}]}],"generatedAt":"2026-10-13T12:00:00.000Z"}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"menu_unavailable, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"menu_unavailable":{"summary":"Menu backend unreachable","value":{"error":{"code":"menu_unavailable","message":"Menu backend unreachable","hint":"Retry in a minute.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/availability":{"get":{"operationId":"get_availability","tags":["ordering"],"summary":"Is the restaurant open? Hours, delivery rules, limits","description":"Step 2. Check isOpen before quoting. Also returns weekly hours, delivery radius and fee, and order limits. If closed, tell the human when it reopens.","responses":{"200":{"description":"Availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Availability"},"example":{"isOpen":true,"reason":"open","timezone":"Europe/Bucharest","alwaysOpen":false,"weeklyHours":{"mon":[["11:00","22:00"]]},"closedDates":[],"fulfillment":["pickup","delivery"],"paymentMethods":["card"],"currency":"RON","agentOrderingPaused":false,"delivery":{"maxDistanceKm":10,"feeRon":10,"freeOverRon":150,"minOrderRon":0},"limits":{"maxDistinctItems":20,"maxQuantityPerItem":20,"maxOrderRon":1500,"minOrderRon":0},"serverTime":"2026-10-13T12:00:00.000Z"}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/delivery/check":{"post":{"operationId":"check_delivery","tags":["ordering"],"summary":"Check if an address in Ploiești is inside the delivery area","description":"Optional, for delivery orders. Send the street and number. If deliverable is false, offer the human pickup instead.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Address"},"example":{"street":"Strada Gheorghe Doja 12","city":"Ploiești"}}}},"responses":{"200":{"description":"Delivery check","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryCheck"},"example":{"deliverable":true,"distanceKm":2.4,"maxDistanceKm":10,"formattedAddress":"Strada Gheorghe Doja 12, Ploiești, Romania","deliveryFeeRon":10,"freeOverRon":150,"minOrderRon":0,"currency":"RON"}}}},"400":{"description":"validation_error, invalid_json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"validation_error":{"summary":"Body or arguments fail validation (details lists each field)","value":{"error":{"code":"validation_error","message":"Request validation failed","hint":"Fix the fields listed in details and resend. Unknown fields are rejected.","retryable":false}}},"invalid_json":{"summary":"Malformed JSON body","value":{"error":{"code":"invalid_json","message":"Malformed JSON body","hint":"Send a valid JSON object body with Content-Type: application/json.","retryable":false}}}}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"413":{"description":"payload_too_large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"payload_too_large":{"summary":"Body over 16 KB","value":{"error":{"code":"payload_too_large","message":"Body over 16 KB","hint":"Request bodies are limited to 16 KB; send fewer items or shorter notes.","retryable":false}}}}}}},"415":{"description":"unsupported_media_type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_media_type":{"summary":"Body sent without Content-Type: application/json","value":{"error":{"code":"unsupported_media_type","message":"Body sent without Content-Type: application/json","hint":"Use Content-Type: application/json.","retryable":false}}}}}}},"422":{"description":"address_not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"address_not_found":{"summary":"Address cannot be geocoded","value":{"error":{"code":"address_not_found","message":"Address cannot be geocoded","hint":"Ask the human for a fuller street address (street name and number) in Ploiești.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"geocoding_unavailable, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"geocoding_unavailable":{"summary":"Address lookup unavailable","value":{"error":{"code":"geocoding_unavailable","message":"Address lookup unavailable","hint":"Retry in a minute, or offer pickup.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/quote":{"post":{"operationId":"quote_order","tags":["ordering"],"summary":"Price a cart (no order is created)","description":"Step 3. Server-computed prices, delivery fee and total in RON. Nothing is ordered. Show the lines and totalRon to the human and get explicit confirmation before place_order.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Cart"},"examples":{"pickup":{"summary":"Pickup","value":{"fulfillment":"pickup","items":[{"itemId":101,"quantity":2,"optionIds":[511]}]}},"delivery":{"summary":"Delivery","value":{"fulfillment":"delivery","items":[{"itemId":101,"quantity":1,"optionIds":[512]}],"deliveryAddress":{"street":"Strada Gheorghe Doja 12","city":"Ploiești","details":"Ap. 4, interfon 4"}}}}}}},"responses":{"200":{"description":"Quote","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"},"example":{"currency":"RON","fulfillment":"pickup","lines":[{"itemId":101,"name":"Burger Manu","quantity":2,"unitPriceRon":45,"options":[{"id":511,"name":"BBQ","priceRon":0}],"lineTotalRon":90}],"subtotalRon":90,"deliveryFeeRon":0,"discountRon":0,"totalRon":90,"paymentMethods":["card"],"validForSeconds":600}}}},"400":{"description":"validation_error, invalid_json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"validation_error":{"summary":"Body or arguments fail validation (details lists each field)","value":{"error":{"code":"validation_error","message":"Request validation failed","hint":"Fix the fields listed in details and resend. Unknown fields are rejected.","retryable":false}}},"invalid_json":{"summary":"Malformed JSON body","value":{"error":{"code":"invalid_json","message":"Malformed JSON body","hint":"Send a valid JSON object body with Content-Type: application/json.","retryable":false}}}}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"409":{"description":"restaurant_closed, ordering_closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"restaurant_closed":{"summary":"place_order while the restaurant is closed or paused","value":{"error":{"code":"restaurant_closed","message":"Restaurant is currently closed for online orders","hint":"Check get_availability for opening hours and tell the human when ordering reopens.","retryable":false}}},"ordering_closed":{"summary":"quote_order/place_order after the last-order cutoff (LAST_ORDER_TIME, default 22:00 Europe/Bucharest)","value":{"error":{"code":"ordering_closed","message":"Online ordering closes at 22:00 Europe/Bucharest time, try again tomorrow","hint":"Online ordering closes at the last-order time (get_availability.lastOrderTime, Europe/Bucharest). Tell the human ordering reopens tomorrow; do not retry tonight.","retryable":false,"details":{"lastOrderTime":"22:00","timezone":"Europe/Bucharest"}}}}}}}},"413":{"description":"payload_too_large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"payload_too_large":{"summary":"Body over 16 KB","value":{"error":{"code":"payload_too_large","message":"Body over 16 KB","hint":"Request bodies are limited to 16 KB; send fewer items or shorter notes.","retryable":false}}}}}}},"415":{"description":"unsupported_media_type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_media_type":{"summary":"Body sent without Content-Type: application/json","value":{"error":{"code":"unsupported_media_type","message":"Body sent without Content-Type: application/json","hint":"Use Content-Type: application/json.","retryable":false}}}}}}},"422":{"description":"item_unavailable, invalid_options, too_many_items, order_too_large, outside_delivery_zone, address_not_found, invalid_request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"item_unavailable":{"summary":"Unknown or unavailable itemId","value":{"error":{"code":"item_unavailable","message":"Item 999 is not available","hint":"Call get_menu (GET /menu) again and use a current item id.","retryable":false}}},"invalid_options":{"summary":"optionIds break the item's option group rules","value":{"error":{"code":"invalid_options","message":"optionIds break the item's option group rules","hint":"Respect each option group's minSelections/maxSelections and use option ids from that item.","retryable":false}}},"too_many_items":{"summary":"Total quantity over the limit","value":{"error":{"code":"too_many_items","message":"Total quantity over the limit","hint":"Reduce quantities or split into separate orders.","retryable":false}}},"order_too_large":{"summary":"Total over the RON limit","value":{"error":{"code":"order_too_large","message":"Total over the RON limit","hint":"Reduce the order; larger orders must be placed by phone with the restaurant.","retryable":false}}},"outside_delivery_zone":{"summary":"Delivery address outside the radius","value":{"error":{"code":"outside_delivery_zone","message":"Delivery address outside the radius","hint":"Offer the human pickup instead (fulfillment: pickup).","retryable":false}}},"address_not_found":{"summary":"Address cannot be geocoded","value":{"error":{"code":"address_not_found","message":"Address cannot be geocoded","hint":"Ask the human for a fuller street address (street name and number) in Ploiești.","retryable":false}}},"invalid_request":{"summary":"Order rejected by the shared order rules (e.g. no priceable items)","value":{"error":{"code":"invalid_request","message":"Order rejected by the shared order rules (e.g. no priceable items)","hint":"Read the message, fix the order (items, address, customer) and resend.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"geocoding_unavailable, menu_unavailable, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"geocoding_unavailable":{"summary":"Address lookup unavailable","value":{"error":{"code":"geocoding_unavailable","message":"Address lookup unavailable","hint":"Retry in a minute, or offer pickup.","retryable":true}}},"menu_unavailable":{"summary":"Menu backend unreachable","value":{"error":{"code":"menu_unavailable","message":"Menu backend unreachable","hint":"Retry in a minute.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/orders":{"post":{"operationId":"place_order","tags":["ordering"],"summary":"Place a confirmed order and get the payment link","description":"Step 4, ONLY after the human confirmed the quote. Needs a fresh idempotency key per order (reuse it only to retry the same order). Returns paymentUrl for the human and a secret statusToken.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"description":"Fresh random key per order (16-128 chars, A-Z a-z 0-9 _ -). Alternatively send idempotencyKey in the body.","schema":{"type":"string","minLength":16,"maxLength":128,"pattern":"^[A-Za-z0-9_-]{16,128}$"},"example":"3f2b8c1e-7a4d-4e8b-9c1a-2d5e6f7a8b9c"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"},"example":{"fulfillment":"pickup","items":[{"itemId":101,"quantity":2,"optionIds":[511]}],"customer":{"name":"Ion Popescu","email":"ion@example.com","phone":"+40712345678"},"notes":"Fără ceapă","expectedTotalRon":90,"idempotencyKey":"3f2b8c1e-7a4d-4e8b-9c1a-2d5e6f7a8b9c"}}}},"responses":{"201":{"description":"Order created; awaiting payment by the human","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCreated"},"example":{"orderNumber":"MANUBBQ-LX9K2P7QAB12","statusToken":"q2V9k3J0c1x8m4N7b6Z5a1S2d3F4g5H6","status":"awaiting_payment","paymentUrl":"https://checkout.stripe.com/c/pay/cs_live_a1B2c3","paymentExpiresAt":"2026-10-13T13:00:00.000Z","nextStep":"Give paymentUrl to the human now. The order goes to the kitchen only after they pay.","currency":"RON","fulfillment":"pickup","lines":[{"itemId":101,"name":"Burger Manu","quantity":2,"unitPriceRon":45,"options":[{"id":511,"name":"BBQ","priceRon":0}],"lineTotalRon":90}],"subtotalRon":90,"deliveryFeeRon":0,"discountRon":0,"totalRon":90,"paymentMethods":["card"]}}}},"400":{"description":"validation_error, invalid_json, idempotency_key_required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"validation_error":{"summary":"Body or arguments fail validation (details lists each field)","value":{"error":{"code":"validation_error","message":"Request validation failed","hint":"Fix the fields listed in details and resend. Unknown fields are rejected.","retryable":false}}},"invalid_json":{"summary":"Malformed JSON body","value":{"error":{"code":"invalid_json","message":"Malformed JSON body","hint":"Send a valid JSON object body with Content-Type: application/json.","retryable":false}}},"idempotency_key_required":{"summary":"place_order without a valid idempotency key","value":{"error":{"code":"idempotency_key_required","message":"place_order without a valid idempotency key","hint":"Generate a fresh random key (e.g. a UUID) per order and reuse it only when retrying that same order.","retryable":false}}}}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"409":{"description":"restaurant_closed, ordering_closed, price_changed, request_in_progress","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"restaurant_closed":{"summary":"place_order while the restaurant is closed or paused","value":{"error":{"code":"restaurant_closed","message":"Restaurant is currently closed for online orders","hint":"Check get_availability for opening hours and tell the human when ordering reopens.","retryable":false}}},"ordering_closed":{"summary":"quote_order/place_order after the last-order cutoff (LAST_ORDER_TIME, default 22:00 Europe/Bucharest)","value":{"error":{"code":"ordering_closed","message":"Online ordering closes at 22:00 Europe/Bucharest time, try again tomorrow","hint":"Online ordering closes at the last-order time (get_availability.lastOrderTime, Europe/Bucharest). Tell the human ordering reopens tomorrow; do not retry tonight.","retryable":false,"details":{"lastOrderTime":"22:00","timezone":"Europe/Bucharest"}}}},"price_changed":{"summary":"expectedTotalRon differs from the current total","value":{"error":{"code":"price_changed","message":"Total is now 95 RON","hint":"Prices changed since the quote. Show the new total to the human, get confirmation, then resend with the new expectedTotalRon.","retryable":false,"details":{"totalRon":95}}}},"request_in_progress":{"summary":"Same idempotency key while the first request is still running","value":{"error":{"code":"request_in_progress","message":"Same idempotency key while the first request is still running","hint":"The same order is still being created; wait a few seconds and retry with the same key.","retryable":true}}}}}}},"413":{"description":"payload_too_large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"payload_too_large":{"summary":"Body over 16 KB","value":{"error":{"code":"payload_too_large","message":"Body over 16 KB","hint":"Request bodies are limited to 16 KB; send fewer items or shorter notes.","retryable":false}}}}}}},"415":{"description":"unsupported_media_type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_media_type":{"summary":"Body sent without Content-Type: application/json","value":{"error":{"code":"unsupported_media_type","message":"Body sent without Content-Type: application/json","hint":"Use Content-Type: application/json.","retryable":false}}}}}}},"422":{"description":"item_unavailable, invalid_options, too_many_items, order_too_large, outside_delivery_zone, address_not_found, invalid_request, below_minimum_order, idempotency_key_reused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"item_unavailable":{"summary":"Unknown or unavailable itemId","value":{"error":{"code":"item_unavailable","message":"Item 999 is not available","hint":"Call get_menu (GET /menu) again and use a current item id.","retryable":false}}},"invalid_options":{"summary":"optionIds break the item's option group rules","value":{"error":{"code":"invalid_options","message":"optionIds break the item's option group rules","hint":"Respect each option group's minSelections/maxSelections and use option ids from that item.","retryable":false}}},"too_many_items":{"summary":"Total quantity over the limit","value":{"error":{"code":"too_many_items","message":"Total quantity over the limit","hint":"Reduce quantities or split into separate orders.","retryable":false}}},"order_too_large":{"summary":"Total over the RON limit","value":{"error":{"code":"order_too_large","message":"Total over the RON limit","hint":"Reduce the order; larger orders must be placed by phone with the restaurant.","retryable":false}}},"outside_delivery_zone":{"summary":"Delivery address outside the radius","value":{"error":{"code":"outside_delivery_zone","message":"Delivery address outside the radius","hint":"Offer the human pickup instead (fulfillment: pickup).","retryable":false}}},"address_not_found":{"summary":"Address cannot be geocoded","value":{"error":{"code":"address_not_found","message":"Address cannot be geocoded","hint":"Ask the human for a fuller street address (street name and number) in Ploiești.","retryable":false}}},"invalid_request":{"summary":"Order rejected by the shared order rules (e.g. no priceable items)","value":{"error":{"code":"invalid_request","message":"Order rejected by the shared order rules (e.g. no priceable items)","hint":"Read the message, fix the order (items, address, customer) and resend.","retryable":false}}},"below_minimum_order":{"summary":"place_order with an items subtotal below the minimum order (MIN_ORDER_LEI, default 50 RON)","value":{"error":{"code":"below_minimum_order","message":"Minimum order is 50 RON of items, before delivery fee, add 12.5 RON more","hint":"The items subtotal (before delivery fee) is below the minimum order (details.minOrderRon, default 50 RON). Ask the human to add items worth at least details.shortfallRon RON, re-quote, then place the order.","retryable":false,"details":{"minOrderRon":50,"subtotalRon":37.5,"shortfallRon":12.5,"basis":"items_subtotal"}}}},"idempotency_key_reused":{"summary":"Same idempotency key with a different body","value":{"error":{"code":"idempotency_key_reused","message":"Same idempotency key with a different body","hint":"This key was used for a different order. Use a new key for a new order.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"502":{"description":"payment_unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"payment_unavailable":{"summary":"Stripe payment link could not be created","value":{"error":{"code":"payment_unavailable","message":"Stripe payment link could not be created","hint":"Retry place_order with the SAME idempotency key in a minute.","retryable":true}}}}}}},"503":{"description":"geocoding_unavailable, menu_unavailable, ordering_unavailable, ordering_paused, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"geocoding_unavailable":{"summary":"Address lookup unavailable","value":{"error":{"code":"geocoding_unavailable","message":"Address lookup unavailable","hint":"Retry in a minute, or offer pickup.","retryable":true}}},"menu_unavailable":{"summary":"Menu backend unreachable","value":{"error":{"code":"menu_unavailable","message":"Menu backend unreachable","hint":"Retry in a minute.","retryable":true}}},"ordering_unavailable":{"summary":"Ordering not configured or temporarily off","value":{"error":{"code":"ordering_unavailable","message":"Ordering not configured or temporarily off","hint":"Online ordering is temporarily unavailable; ask the human to order on https://manubbq.ro or by phone.","retryable":true}}},"ordering_paused":{"summary":"place_order while AGENT_ORDERING_PAUSED=true (kill switch)","value":{"error":{"code":"ordering_paused","message":"place_order while AGENT_ORDERING_PAUSED=true (kill switch)","hint":"Agent ordering is paused by the restaurant right now. Menu, quotes and order status still work; ask the human to order on https://manubbq.ro or by phone, or try again later.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/orders/{orderNumber}":{"get":{"operationId":"get_order_status","tags":["ordering"],"summary":"Get the status of an order you placed","description":"Step 5. Needs the statusToken from place_order in the X-Order-Token header. Returns status, payment status, totals and items only (no personal data). Poll at most every 30 seconds.","parameters":[{"name":"orderNumber","in":"path","required":true,"description":"orderNumber returned by place_order","schema":{"type":"string","pattern":"^(MANUBBQ|FISH)-[A-Z0-9]{8,48}$"},"example":"MANUBBQ-LX9K2P7QAB12"},{"name":"X-Order-Token","in":"header","required":true,"description":"statusToken returned by place_order","schema":{"type":"string","minLength":16,"maxLength":128},"example":"q2V9k3J0c1x8m4N7b6Z5a1S2d3F4g5H6"}],"responses":{"200":{"description":"Order status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderStatus"},"example":{"orderNumber":"MANUBBQ-LX9K2P7QAB12","status":"preparing","paymentStatus":"paid","fulfillment":"pickup","currency":"RON","totalRon":90,"lines":[{"name":"Burger Manu","quantity":2,"lineTotalRon":90}],"paymentUrl":null,"createdAt":"2026-10-13T12:00:00.000Z"}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"summary":"Unknown order, wrong or missing token, unknown path or tool","value":{"error":{"code":"not_found","message":"Order not found","hint":"Check the order number and use the statusToken returned by place_order.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"unavailable, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unavailable":{"summary":"Order status temporarily unavailable","value":{"error":{"code":"unavailable","message":"Order status temporarily unavailable","hint":"Retry later.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}},"/api/v1/agent/orders/{orderNumber}/cancel":{"post":{"operationId":"cancel_order","tags":["ordering"],"summary":"Cancel an order you placed while it is still unpaid","description":"Only when the human asks. Needs the statusToken in X-Order-Token. Works only while unpaid and not sent to the kitchen; else 409 order_not_cancellable with the restaurant phone in the hint. Safe to repeat.","x-openai-isConsequential":true,"parameters":[{"name":"orderNumber","in":"path","required":true,"description":"orderNumber returned by place_order","schema":{"type":"string","pattern":"^(MANUBBQ|FISH)-[A-Z0-9]{8,48}$"},"example":"MANUBBQ-LX9K2P7QAB12"},{"name":"X-Order-Token","in":"header","required":true,"description":"statusToken returned by place_order","schema":{"type":"string","minLength":16,"maxLength":128},"example":"q2V9k3J0c1x8m4N7b6Z5a1S2d3F4g5H6"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"},"example":{"reason":"Changed my mind"}}}},"responses":{"200":{"description":"Order cancelled (or already cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderCancelled"},"example":{"orderNumber":"MANUBBQ-LX9K2P7QAB12","status":"cancelled","paymentStatus":"pending","fulfillment":"pickup","currency":"RON","totalRon":90,"lines":[{"name":"Burger Manu","quantity":2,"lineTotalRon":90}],"paymentUrl":null,"createdAt":"2026-10-13T12:00:00.000Z","cancelled":true,"alreadyCancelled":false}}}},"400":{"description":"validation_error, invalid_json","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"validation_error":{"summary":"Body or arguments fail validation (details lists each field)","value":{"error":{"code":"validation_error","message":"Request validation failed","hint":"Fix the fields listed in details and resend. Unknown fields are rejected.","retryable":false}}},"invalid_json":{"summary":"Malformed JSON body","value":{"error":{"code":"invalid_json","message":"Malformed JSON body","hint":"Send a valid JSON object body with Content-Type: application/json.","retryable":false}}}}}}},"401":{"description":"invalid_api_key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"summary":"Authorization header with an unknown key","value":{"error":{"code":"invalid_api_key","message":"Authorization header with an unknown key","hint":"Remove the Authorization header (keys are optional) or use a valid key.","retryable":false}}}}}}},"403":{"description":"cors_forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cors_forbidden":{"summary":"Browser write from a non-allowed Origin","value":{"error":{"code":"cors_forbidden","message":"Browser write from a non-allowed Origin","hint":"Call the API server-side; browser writes are limited to allowed origins.","retryable":false}}}}}}},"404":{"description":"not_found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"summary":"Unknown order, wrong or missing token, unknown path or tool","value":{"error":{"code":"not_found","message":"Order not found","hint":"Check the order number and use the statusToken returned by place_order.","retryable":false}}}}}}},"405":{"description":"method_not_allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"method_not_allowed":{"summary":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","value":{"error":{"code":"method_not_allowed","message":"Wrong HTTP method on a known path (GET/DELETE on /mcp)","hint":"See the OpenAPI spec at /api/v1/openapi.json for allowed methods.","retryable":false}}}}}}},"409":{"description":"order_not_cancellable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"order_not_cancellable":{"summary":"cancel_order on a paid, processing or in-kitchen order","value":{"error":{"code":"order_not_cancellable","message":"Order cannot be cancelled: it is already paid","hint":"This order can no longer be cancelled online. Ask the human to contact ManuBBQ: phone +40373816805, email info@manubbq.ro.","retryable":false}}}}}}},"413":{"description":"payload_too_large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"payload_too_large":{"summary":"Body over 16 KB","value":{"error":{"code":"payload_too_large","message":"Body over 16 KB","hint":"Request bodies are limited to 16 KB; send fewer items or shorter notes.","retryable":false}}}}}}},"415":{"description":"unsupported_media_type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unsupported_media_type":{"summary":"Body sent without Content-Type: application/json","value":{"error":{"code":"unsupported_media_type","message":"Body sent without Content-Type: application/json","hint":"Use Content-Type: application/json.","retryable":false}}}}}}},"429":{"description":"rate_limited","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Too many requests (Retry-After header)","value":{"error":{"code":"rate_limited","message":"Too many requests (Retry-After header)","hint":"Wait for Retry-After seconds before trying again. Do not retry in a tight loop.","retryable":true,"retryAfterSeconds":30}}}}}}},"500":{"description":"internal_error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error","value":{"error":{"code":"internal_error","message":"Unexpected server error","hint":"Retry once later; if it persists ask the human to order on https://manubbq.ro.","retryable":true}}}}}}},"503":{"description":"cancel_unavailable, unavailable, disabled","headers":{"Retry-After":{"description":"Seconds to wait before retrying (when known)","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"cancel_unavailable":{"summary":"Payment state could not be verified, so nothing was cancelled","value":{"error":{"code":"cancel_unavailable","message":"Payment state could not be verified, so nothing was cancelled","hint":"Could not confirm the payment state; retry cancel_order in a minute (safe to repeat).","retryable":true}}},"unavailable":{"summary":"Order status temporarily unavailable","value":{"error":{"code":"unavailable","message":"Order status temporarily unavailable","hint":"Retry later.","retryable":true}}},"disabled":{"summary":"Agent API switched off (AGENT_API_ENABLED=false)","value":{"error":{"code":"disabled","message":"Agent API switched off (AGENT_API_ENABLED=false)","hint":"The agent API is switched off; ask the human to order on https://manubbq.ro.","retryable":false}}}}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","hint","retryable"],"properties":{"code":{"type":"string","enum":["validation_error","invalid_json","idempotency_key_required","invalid_api_key","cors_forbidden","forbidden_origin","not_found","method_not_allowed","restaurant_closed","ordering_closed","price_changed","request_in_progress","order_not_cancellable","payload_too_large","unsupported_media_type","item_unavailable","invalid_options","too_many_items","order_too_large","below_minimum_order","outside_delivery_zone","address_not_found","idempotency_key_reused","invalid_request","rate_limited","internal_error","payment_unavailable","menu_unavailable","geocoding_unavailable","ordering_unavailable","ordering_paused","cancel_unavailable","unavailable","disabled"],"description":"Stable machine-readable code; see /llms-full.txt for each"},"message":{"type":"string"},"hint":{"type":"string","description":"What the agent should do next"},"retryable":{"type":"boolean","description":"true if the same request may succeed later (respect Retry-After)"},"retryAfterSeconds":{"type":"integer"},"details":{"description":"Optional extra data, e.g. field errors [{path, message}] or {totalRon}"}}}}},"Option":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"nameEn":{"type":"string","description":"English name or null"},"priceRon":{"type":"number"}}},"OptionGroup":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"required":{"type":"boolean"},"minSelections":{"type":"integer"},"maxSelections":{"type":"integer"},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"}}}},"MenuItem":{"type":"object","properties":{"id":{"type":"integer","description":"Use as itemId"},"name":{"type":"string","description":"Romanian name"},"nameRo":{"type":"string"},"nameEn":{"type":"string","description":"English name, or null"},"description":{"type":"string","description":"Romanian description, or null"},"descriptionRo":{"type":"string"},"descriptionEn":{"type":"string","description":"English description, or null"},"allergens":{"type":"array","items":{"type":"string"},"description":"null when not published; ask the restaurant"},"priceRon":{"type":"number"},"categoryId":{"type":"integer"},"optionGroups":{"type":"array","items":{"$ref":"#/components/schemas/OptionGroup"}}}},"Menu":{"type":"object","properties":{"currency":{"type":"string","enum":["RON"]},"language":{"type":"string","enum":["ro"]},"notes":{"type":"array","items":{"type":"string"}},"categories":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"nameEn":{"type":"string"},"sortOrder":{"type":"integer"}}}},"items":{"type":"array","items":{"$ref":"#/components/schemas/MenuItem"}},"generatedAt":{"type":"string","format":"date-time"}}},"Availability":{"type":"object","properties":{"isOpen":{"type":"boolean"},"reason":{"type":"string","enum":["open","closed_hours","closed_date","paused","after_last_order"],"description":"after_last_order = past the last-order cutoff (lastOrderTime)"},"lastOrderTime":{"type":"string","nullable":true,"description":"Last-order cutoff HH:MM in `timezone` (default 22:00 Europe/Bucharest). From then until midnight quote_order/place_order return 409 ordering_closed. null = no cutoff."},"minOrderRon":{"type":"number","description":"Minimum order value in RON (default 50), applied to the items subtotal before delivery fee and discounts"},"minimumOrderBasis":{"type":"string","enum":["items_subtotal"]},"timezone":{"type":"string"},"alwaysOpen":{"type":"boolean"},"agentOrderingPaused":{"type":"boolean","description":"true while place_order returns 503 ordering_paused"},"weeklyHours":{"type":"object","description":"Opening hours per weekday, or null"},"closedDates":{"type":"array","items":{"type":"string","format":"date"}},"fulfillment":{"type":"array","items":{"type":"string","enum":["pickup","delivery"]}},"paymentMethods":{"type":"array","items":{"type":"string","enum":["card"]}},"currency":{"type":"string","enum":["RON"]},"delivery":{"type":"object","properties":{"maxDistanceKm":{"type":"number"},"feeRon":{"type":"number"},"freeOverRon":{"type":"number"},"minOrderRon":{"type":"number"}}},"limits":{"type":"object","properties":{"maxDistinctItems":{"type":"integer"},"maxQuantityPerItem":{"type":"integer"},"maxOrderRon":{"type":"number"},"minOrderRon":{"type":"number"}}},"serverTime":{"type":"string","format":"date-time"}}},"Address":{"type":"object","additionalProperties":false,"required":["street"],"properties":{"street":{"type":"string","maxLength":200,"description":"Street and number, e.g. 'Strada Gheorghe Doja 12'"},"city":{"type":"string","maxLength":80,"description":"Defaults to Ploiești"},"details":{"type":"string","maxLength":200,"description":"Apartment, floor, intercom"}}},"DeliveryCheck":{"type":"object","properties":{"deliverable":{"type":"boolean"},"distanceKm":{"type":"number"},"maxDistanceKm":{"type":"number"},"formattedAddress":{"type":"string"},"deliveryFeeRon":{"type":"number"},"freeOverRon":{"type":"number"},"minOrderRon":{"type":"number"},"currency":{"type":"string","enum":["RON"]}}},"CartItem":{"type":"object","additionalProperties":false,"required":["itemId","quantity"],"properties":{"itemId":{"type":"integer","description":"Menu item id from get_menu"},"quantity":{"type":"integer","minimum":1,"maximum":20},"optionIds":{"type":"array","items":{"type":"integer"},"maxItems":20,"description":"Option ids from that item's optionGroups"},"notes":{"type":"string","maxLength":200}}},"Cart":{"type":"object","additionalProperties":false,"required":["fulfillment","items"],"properties":{"fulfillment":{"type":"string","enum":["pickup","delivery"]},"items":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/CartItem"}},"deliveryAddress":{"$ref":"#/components/schemas/Address"}}},"Customer":{"type":"object","additionalProperties":false,"required":["name","email","phone"],"properties":{"name":{"type":"string","maxLength":100},"email":{"type":"string","format":"email","maxLength":254},"phone":{"type":"string","pattern":"^\\+?[0-9 ()./-]{6,25}$","description":"E.164 (+40712345678) or Romanian national format (0712 345 678); stored as E.164"}}},"OrderRequest":{"type":"object","additionalProperties":false,"required":["fulfillment","items","customer"],"properties":{"fulfillment":{"type":"string","enum":["pickup","delivery"]},"items":{"type":"array","minItems":1,"maxItems":20,"items":{"$ref":"#/components/schemas/CartItem"}},"deliveryAddress":{"$ref":"#/components/schemas/Address"},"customer":{"$ref":"#/components/schemas/Customer"},"notes":{"type":"string","maxLength":300},"expectedTotalRon":{"type":"number","description":"totalRon from the confirmed quote; if the price changed you get 409 price_changed"},"idempotencyKey":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$","description":"Use if you cannot send the Idempotency-Key header"}}},"QuoteLine":{"type":"object","properties":{"itemId":{"type":"integer"},"name":{"type":"string"},"quantity":{"type":"integer"},"unitPriceRon":{"type":"number"},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"}},"lineTotalRon":{"type":"number"}}},"Quote":{"type":"object","properties":{"currency":{"type":"string","enum":["RON"]},"fulfillment":{"type":"string","enum":["pickup","delivery"]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/QuoteLine"}},"subtotalRon":{"type":"number"},"deliveryFeeRon":{"type":"number"},"discountRon":{"type":"number"},"totalRon":{"type":"number"},"paymentMethods":{"type":"array","items":{"type":"string","enum":["card"]}},"validForSeconds":{"type":"integer"},"orderable":{"type":"boolean","description":"false when the minimum order is not met; place_order would return 422 below_minimum_order"},"minimumOrder":{"type":"object","description":"Minimum order check on the items subtotal (before delivery fee and discounts)","properties":{"minOrderRon":{"type":"number"},"basis":{"type":"string","enum":["items_subtotal"]},"met":{"type":"boolean"},"shortfallRon":{"type":"number","description":"RON of items still needed (0 when met)"}}}}},"OrderCreated":{"type":"object","properties":{"orderNumber":{"type":"string"},"statusToken":{"type":"string","description":"Secret. Send as X-Order-Token to get_order_status. Do not post publicly."},"status":{"type":"string","enum":["awaiting_payment"]},"paymentUrl":{"type":"string","format":"uri","description":"Stripe-hosted checkout. Give it to the human to pay."},"paymentExpiresAt":{"type":"string","format":"date-time"},"nextStep":{"type":"string"},"currency":{"type":"string","enum":["RON"]},"fulfillment":{"type":"string","enum":["pickup","delivery"]},"lines":{"type":"array","items":{"$ref":"#/components/schemas/QuoteLine"}},"subtotalRon":{"type":"number"},"deliveryFeeRon":{"type":"number"},"discountRon":{"type":"number"},"totalRon":{"type":"number"},"paymentMethods":{"type":"array","items":{"type":"string","enum":["card"]}}}},"CancelRequest":{"type":"object","additionalProperties":false,"properties":{"reason":{"type":"string","maxLength":200,"description":"Optional short reason from the human"}}},"OrderCancelled":{"type":"object","properties":{"orderNumber":{"type":"string"},"status":{"type":"string","enum":["cancelled"]},"paymentStatus":{"type":"string","enum":["pending","failed"]},"cancelled":{"type":"boolean"},"alreadyCancelled":{"type":"boolean","description":"true if it was already cancelled before this call"},"fulfillment":{"type":"string","enum":["pickup","delivery"]},"currency":{"type":"string","enum":["RON"]},"totalRon":{"type":"number"},"lines":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer"},"lineTotalRon":{"type":"number"}}}},"paymentUrl":{"type":"string","description":"Always null once cancelled"},"createdAt":{"type":"string","format":"date-time"}}},"OrderStatus":{"type":"object","properties":{"orderNumber":{"type":"string"},"status":{"type":"string","enum":["awaiting_payment","payment_failed","refunded","received","preparing","ready","out_for_delivery","delivered","cancelled","expired","processing"]},"paymentStatus":{"type":"string","enum":["pending","paid","failed","refunded"]},"fulfillment":{"type":"string","enum":["pickup","delivery"]},"currency":{"type":"string","enum":["RON"]},"totalRon":{"type":"number"},"lines":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"integer"},"lineTotalRon":{"type":"number"}}}},"paymentUrl":{"type":"string","description":"Still-valid payment link while awaiting payment, else null"},"createdAt":{"type":"string","format":"date-time"}}}}}}