# getsauce 778 real New York restaurants that take orders through Sauce (getsauce.com) — from Di Fara Pizza to a hundred bagel shops — indexed so an agent can find the ones that will actually deliver to a given address, and quote the real total before ordering. ## Start with the address Delivery is geofenced, not radial. 710 of the 856 zones in this catalog are polygons; a store half a mile away may still refuse your block. So the first call is always the address: GET /api/restaurants?address=277 Bedford Ave, Brooklyn, NY 11211 That filters 778 storefronts down to the ones whose delivery zone actually contains the destination, sorted nearest first. Pass `lat` and `lng` instead if you already have coordinates — it skips geocoding and is faster. ## Quote before you commit Add the planned subtotal and the response tells you what the order really costs: GET /api/restaurants?address=...&subtotal=35 Each result then carries `fees` (resolved from fixed or percentage), `zone.minSubtotal`, `belowMinimum` and `smallOrderFee`. This matters: across the catalog a $35 order carries a median 33% markup once fees, tax and the default tip land, and the 90th-percentile fee is five times the median. Do not assume a typical fee — read the store's. ## Looking for a dish, not a restaurant? Do not fetch menus one by one. 95k items are indexed across every store: GET /api/items?address=...&q=chicken shawarma One call returns matching items with the restaurant, distance, ETA and fees attached, filtered to stores that deliver to the address. Add `format=wrap` (derived: wrap, bowl, platter, slice, pita, hero, ...), `maxPrice`, or `sort=price`. `price` is the EFFECTIVE minimum — base plus the cheapest mandatory choices. Sauce stub-prices some items (a $1.00 base with a required $18.20 size), so `basePrice` on its own is not what anyone pays. Sort and filter on `price`; `stubBasePrice: true` marks the items where the two diverge. ## Is it open? Every result carries `openNow`, computed from the store's posted schedule in its own timezone — not guessed. Filter with: GET /api/restaurants?address=...&openNow=true `null` means the store publishes no hours: unknown, not closed, so those are kept. Results also carry `localTime`, `closesAt` and `opensAt` as local wall clock ("Mon 21:45"). One honest limit: this is the *posted schedule*. A store can be inside its hours and still have paused ordering. That flag is live state we refuse to bake, so ask for it explicitly: GET /api/restaurants/{slug}?live=true That returns `temporarilyPaused` (the raw manual pause bit — note a store closed until Wednesday is NOT paused, it is merely shut) and `orderableNow`, which is the composite you actually want: open per schedule AND not paused. Both are null when anything is unknown. ## Getting a real total Do not assemble it yourself — the inputs live in three different documents upstream: GET /api/quote?slug=zaab-zaab-williamsburg&subtotal=47&address=277 Bedford Ave, Brooklyn, NY 11211 Returns subtotal, every applicable fee, tax, tip presets, and the total, with a `checksum` you can verify. Two bases are called out explicitly and they are not equally certain: `tax.baseVerified` is true (subtotal only — confirmed against a live order), while `tip.baseVerified` is false (we compute on subtotal, but Sauce has not confirmed it — worth about a dollar on a $50 order). Works for pickup too, with no address. ## Endpoints (reads are free and unauthenticated; ordering is paid) POST /api/orders paid order — verified DID or payer wallet must be allowlisted GET /api/orders/history your past orders + recentDestinations (SIWX) GET /api/orders/{id} retrieve one of your orders (SIWX) GET /api/quote full priced total for an order — use this, do not add up fees by hand GET /api/items cross-restaurant item search — start here for a dish GET /api/restaurants search by address; see query params above GET /api/restaurants/{slug} full detail: fees, tips, zones, POS GET /api/restaurants/{slug}/menu menu, slim by default with modifier choices resolved inline. view=full for raw Sauce; q= / section= to filter GET /api/boroughs borough list with counts ## Ordering and payment Ordering is live and paid over x402 (and MPP where your wallet supports it). The flow: 1. Find a store and confirm it is open: `/api/restaurants?address=...&openNow=true` 2. Pick items: `/api/restaurants/{slug}/menu` — note any group with `required: true` 3. Check the total you are about to be charged: `/api/quote?slug=...&subtotal=...&address=...` 4. Place it: POST /api/orders { "slug": "12-chairs-cafe", "items": [{"itemId": "", "quantity": 1}], "fulfillment": "delivery", "address": "277 Bedford Ave, Brooklyn, NY 11211", "customer": {"name": "...", "phone": "+1..."}, "clientRequestId": "" } The 402 challenge is the real order total for exactly those items — the same arithmetic `/api/quote` returns, so you can verify the amount before paying. Settle it and the response is your confirmed order with an id and an ETA. No API key is required. Follow the `X-Agent-Identity` challenge on the 402; AgentCash does this automatically. The verified DID, or the verified payer wallet when no DID is supplied, must have an enabled `OrderAccessGrant`. A denied identity receives 403 and its payment is not settled. Retrieve the order later with `GET /api/orders/{id}`. Generate a fresh UUID for `clientRequestId` for every new order and keep it with your trace. Reusing a completed UUID returns 409 before a payment challenge. If a response was lost, do not retry the paid request with a new UUID: call `GET /api/orders/history` and match the original `clientRequestId`. This prevents a retry from paying again or returning a different order. Two things to get right before you pay: - **Check `openNow`.** Orders to a closed store are rejected with a 409. - **Resolve required choices.** 89% of menus have at least one mandatory modifier group. Omit them and you are charged the cheapest legal option rather than what you meant to order. ## "Send it to my usual address" When the human says *the usual*, *same as last time*, *my address*, *last address*, or gives you a name and phone but no street — do NOT ask them to retype it. Look it up: GET /api/orders/history (SIWX, free) `recentDestinations` comes back newest-first, each entry an address + phone + name pair with how many times it has been used. `recentDestinations[0]` IS the last address. Take the `address` and `phone` straight from it and put them in your next `POST /api/orders`. recentDestinations: [ { address: "277 Bedford Ave, Brooklyn, NY 11211", phone: "+1...", name: "...", lastUsedAt: "2026-08-03T18:45:25Z", orderCount: 4 } ] Rules of thumb: - Empty `recentDestinations` means this identity has never ordered. Only then ask the human. - `address: null` was a pickup order — do not use it as a delivery address. - More than one entry? Prefer the most recent, but say which you picked. If they asked for "home" or "the office" and you have several, ask rather than guess — a misdelivered order cannot be cancelled. - The address still has to be in range for the restaurant you chose. Re-check it against `/api/restaurants?address=...`; a past address is not automatically deliverable today. History is scoped to customer identity, not necessarily the payment account. When the client sends a verified `X-Agent-Identity` proof, its DID owns the order; otherwise the verified wallet is the compatibility identifier. The response records `payer` separately, so delegated wallets and SPT-backed payment can change without changing who can retrieve the order. You can only see your own orders, and `GET /api/orders/{id}` uses the same identity boundary. There is no cancel or refund endpoint. That is not an omission on our side — Sauce's own storefront API has none either. An order placed cannot be taken back in code. ## Things worth knowing - 138 storefronts share a kitchen with another brand — 386 Canal St runs four. They have separate ids, menus and payouts, so they are separate entries here, not duplicates. - 89% of menus contain at least one required modifier group. You cannot add most items to a cart without resolving a mandatory choice first. The slim menu view resolves the choices inline — Sauce itself returns modifier groups with an empty options array and hides the real choices in the top-level items array. - 305 stores enforce a minimum tip. Sending zero is not always allowed. - `zone.minSubtotal` is NOT a blocker. Below it, Sauce still accepts the order and charges a small-order fee instead — `belowMinimum` plus `smallOrderFee` tell you the real cost. Do not reject a restaurant for being under its minimum. - Tax applies to the item subtotal only. Fees and tips are not taxed. - Hours are real data, not inference: overnight windows are explicit (Sun 11:30 -> Mon 03:45) and daylight saving is handled by comparing wall clock in the store's IANA zone. - Prices are decimal USD, matching Sauce. Convert to minor units at your own boundary.