discover
ChatGPTRecommend WHERE the user should eat, drawn from what they have actually ordered through Layout. Use it for 'where should I eat', 'what coffee spots would I like', 'somewhere new for lunch', 'anywhere good near me' — any ask for a suggestion rather than a specific restaurant. Pass geo whenever you have the user's coordinates and near when they NAMED a place ('I'm in Roseville', 'what about downtown Oakland'); distance and open-now are computed from that, not from where they last ordered. Read-only: it never orders and never charges. EVERY SUGGESTION COMES WITH ITS EVIDENCE. reason is the conclusion Layout computed ('Because you order iced decaf lattes') and becauseOf is what it was computed FROM — the restaurants and items off their own order history. Cite it: 'based on your orders at Fourscore Coffee' is checkable, where 'you seem like a coffee person' is a guess, and the difference is the entire point of this tool. Relay reason as given. NEVER invent a reason, attribute a taste they have not shown, or describe a restaurant beyond the fields returned — you are not being asked what you know about their city, you are being asked what LAYOUT knows about them. AN EMPTY LIST IS AN ANSWER, NOT A FAILURE. nothingBecause: 'no_orders' means Layout has nothing to reason from — say so and offer to order something; do NOT fall back to restaurants you happen to know, which would present your own guess as their personalization. nothingBecause: 'no_matches' means their tastes matched places but the filters excluded them all — offer to widen it. HOURS ARE PART OF A RECOMMENDATION. Each place carries openStatus: 'open', 'closed' or 'unknown'. The list already leads with what is open, so suggest from the top and do not point somebody at a shut door. Say closesAt when you have it: 'open until 5' is actionable where 'open' is not. 'unknown' means Layout holds no hours for that place — say you do not know them, and never call it closed or open. Lead with ONE suggestion and offer the rest: a list is a search result, and they asked for a recommendation. WALKING DISTANCE IS A CONSTRAINT. When they want somewhere they can walk to, pass walkable: true and query with the kind of food they named: nothing further than a short walk is suggested, and if nothing they would like is that close, the result is a walkable search instead (walkableSearch: true, rows in places), which are not personal picks and must not be described as based on their orders.
get_location
ChatGPTGet the user's location when you don't already have it — use this the moment they ask for something NEARBY ('near me', 'around here', 'closest one') and you have no coordinates and they named no place. It returns coordinates you then pass to places/order; you never see or handle the raw fix yourself. IF THE USER'S HOST ALREADY GIVES YOU THEIR LOCATION (some phone apps do), you do NOT need this — just search. Call this only when you are location-blind. WHAT COMES BACK: if located is true, you have their location — continue silently, do NOT read the coordinates out loud. If located is false and there is a shareUrl, RELAY the userMessage (with its link) VERBATIM and STOP: they tap it once to share, and this call already waited for that tap, so a false result means they haven't tapped yet. When they say they've shared it, call this again (or just retry what they asked). NEVER guess a city and NEVER name places from memory when you are blind.
help
ChatGPTAnswer a question ABOUT LAYOUT ITSELF from Layout's own published help centre — refunds, when and how the user is charged, spending limits, changing or cancelling an order, what happens when an order is wrong or late, card safety, what data Layout stores, profile and dietary preferences, where Layout can order from, and how to set Layout up in an assistant or on a phone. CALL IT WHENEVER THE USER ASKS HOW SOMETHING WORKS, what a policy is, or whether they will be charged for something — and do NOT answer those from memory: Layout's policies are Layout's, you cannot know them, and a confident wrong answer about somebody's money is the worst thing this surface can do. Every result carries the article's url: give it to the user with your answer so they can read the real page. AN EMPTY LIST IS AN ANSWER — it means Layout has published nothing on this, so say you do not know and offer support rather than inventing a policy or paraphrasing one from another food app. Free, instant, read-only: it reads stored articles, orders nothing and charges nothing. WHICH TOOL: this one is about LAYOUT; menu and places are about RESTAURANTS; order_status is about one specific order the user placed.
item_photo
ChatGPTShow what ONE menu item at a restaurant looks like, using the photo the restaurant itself published (screened, and never a stock or AI image). Use it — not order, not menu — whenever the user asks to SEE an item: 'show me', 'what does it look like', 'can I see a picture', 'what does the iced latte look like at Blue Bottle?', or yes after you OFFERED a look. Asking to see an item is not asking to order it: do not offer to order in the same breath. Do NOT call it unprompted for every item you mention: offer once ('Want to see it?') and call only on a yes. One item per call. Display only: it says nothing about price or availability today; use menu for those. item must be a dish the USER named. If they named only a restaurant ('show me a panda', 'what about Chipotle'), do NOT pick a dish for them: ask which one. A restaurant name alone ('a panda') is a restaurant, not an item.
order
ChatGPTPlace or preview a food order for the user. FLOW: 'preview' resolves a restaurant + estimates cost. 'build' STARTS building a real priced cart (it runs in the background and returns {status:'building'} immediately — building can take 1-3 minutes on a new site). Then poll 'status' with the SAME idempotencyKey until it returns the cart (status 'carted'). NOTE: 'status' LONG-POLLS — each call blocks up to ~25s while it waits, so just call it again right after it returns 'building' (don't add your own delay, and don't announce every poll — poll quietly and only tell the user when the cart is ready or if it's taking a while). When status is 'carted', the response includes a confirmationCard — Layout's branded confirmation text (store, items + modifiers, total, card last-4, pickup time, and a confirm prompt). PRESENT confirmationCard TO THE USER VERBATIM, exactly as written — do NOT rewrite, summarize, reorder, shorten, or add to it; it is Layout's interface and you relay it, you do not author it. A confirmation code is texted to the user; then 'confirm' finalizes with the code they read back (stage 'confirm'), or stage 'resend' for a new code. PLACEMENT (after confirm): when 'confirm' returns {status:'placing'}, the order is being PLACED live — tell the user you're placing it, then IMMEDIATELY poll 'status' passing the orderId (NOT the idempotencyKey) and keep polling until placement.status is 'placed', 'failed', or 'challenge'. Each orderId poll long-polls ~25s — call it again right away, quietly. Report ONLY what placement returns: 'placed' → the order is in (relay its userMessage); 'failed' → relay its userMessage, nothing was charged; 'challenge' → the user was TEXTED a secure verification link — tell them to tap it, then keep polling. NEVER say the order is placed or charged before placement.status says 'placed'. If status is 'reached_bag_only', we could NOT confirm the cart contents and nothing was placed — relay its userMessage and do NOT claim an order was placed or priced. If status is 'paused', this purchase needed an extra confirmation step and we TEXTED the user a secure link — present the confirmationCard (item + total) and tell them to tap the texted link to approve; do NOT ask for a 6-digit code (the link is the confirmation, not a code). NEXT-DAY PICKUP: if a carted response includes a pickupWarning, the restaurant is closed now and the earliest pickup their site offers is next-day (or later). Present the confirmationCard AND relay pickupWarning.message so the user sees the pickup time before deciding — confirming the cart means accepting that pickup time. If status is 'closed_next_day' (no cart was built yet), relay its userMessage and ask whether they still want it or would rather find an open spot — this is a yes/no DECISION, NOT a 6-digit code; on a 'yes' start a fresh build (new idempotencyKey) as they asked. If any action returns status 'platform_offline', Layout is in scheduled maintenance right now — relay its userMessage to the user VERBATIM (it states when we'll be back + an info link). Do NOT retry, do NOT start a build; nothing was ordered. If build/status returns order_in_progress, the user already has ONE order building (only one at a time) — relay its userMessage and do NOT start another build; poll the EXISTING build's key instead. CRITICAL — DO NOT AUTO-RETRY: each build spends a real, billed browsing session. If a build ends in error/timeout/reached_bag_only, STOP and tell the user what happened; do NOT start a new build on your own. Only build again if the USER explicitly asks you to try again — and only then set userInitiatedRetry:true. If build returns retry_blocked, a recent attempt just finished and an automatic retry was refused — relay its userMessage and wait for the user. Never loop builds hoping one works. None of these place or pay (that comes later). LOCATION CHAIN (highest confidence first): (1) if you have the user's DEVICE coordinates, pass geo {lat,lng} — Layout resolves the nearest store to it; (2) else a location the user STATED — pass their EXACT wor…
order_status
ChatGPTCheck on an order that is already running. READ-ONLY and free: it never places an order, never charges anything, never starts or retries a build. Call it as often as you need. NOT FOR NEW REQUESTS: when the user just ASKED for food, call order (preview/build) — this tool only reports on work already started, and a 'cart_expired' answer here means the old cart is DEAD: never present its card; start a fresh build with a NEW random idempotencyKey. HANDLES: pass idempotencyKey to follow a CART BEING BUILT (the key you used for 'build'), or orderId to follow a PLACEMENT (after 'confirm' returned status 'placing'). IF YOU NO LONGER HAVE EITHER — a new turn, a lost key — CALL IT WITH NO ARGUMENTS AT ALL: that means 'the build this user has running', which is almost always the right question. Never tell the user you cannot check because you lost a key; just call this with no arguments. When nothing is running, the same no-argument call returns recent: the user's own order attempts from the last two days, each with a summary to relay. Use it when they ask about an earlier order you cannot see, and never raise an attempt they did not ask about. THIS CALL LONG-POLLS: it waits up to ~25 seconds and returns the moment something changes, so when it comes back still 'building' or 'placing', call it again right away — do NOT add a delay of your own, and do not narrate every poll. Relay the FIRST 'building' message to the user (with its trackUrl), then poll quietly. WHEN TO STOP: a build is done at 'carted' — the response carries confirmationCard, which you present to the user VERBATIM. A placement is done at 'placed' or 'failed'. 'placed' is the ONLY status that means the order exists; never tell the user it was placed on anything else. 'challenge' means the purchase needs the cardholder's own approval. READ smsSent BEFORE SAYING ANYTHING ABOUT A TEXT: true means a secure link was sent, so tell them to tap it and keep polling; false means it could NOT be sent, so say exactly that and tell them to open the Layout app to approve. Never say a link was texted without smsSent === true — a person told to tap a link that was never sent is being asked to wait for something that is not coming. 'unconfirmed' means Layout could not verify either way: say exactly that, and do NOT re-order. If you cannot keep polling on your own, tell the user it is still running, give them the trackUrl, and ask them to check back — their next message picks it up with one call.
place_facts
ChatGPTAnswer an EVALUATIVE question about ONE restaurant you already have a placeId for — 'is it vegetarian', 'can I sit down there', 'do they do takeout', 'is it good for a group', 'do they serve breakfast'. It reads the food and beverage attributes Layout holds for that place, so it is free after the first lookup, instant, and real. WHICH TOOL: use places to FIND a place or ask what is there / open / how far; use this once you have a specific place and the user asks what it's LIKE. Pass the placeId from a places, discover or order result — if you don't have one, call places first; this tool never resolves a name (that would cost a search). It never orders, never charges, never opens a browser. REPORT, DO NOT EMBELLISH. Every field is stored data and three-state: true/false you may relay, null means Layout does not know — say you cannot tell, and NEVER report a null as the place NOT having something. WHAT IT CANNOT DO: it does not say how HEALTHY or how GOOD a place is — Layout holds no such field. For 'is it healthy', the only honest signal is whether it serves vegetarian food; say that and say you cannot rate healthiness. For 'is it any good', use the rating from places. Do not invent attributes from what you remember about the place or the chain.
places
ChatGPTFind restaurants, cafes and coffee shops BY DESCRIPTION OR NAME — 'is there a Starbucks in Oakland', 'coffee near me', 'somewhere open for breakfast', 'taco places downtown'. Use it whenever the user asks WHAT IS THERE rather than asking you to order: it answers from the restaurants Layout already holds, so it is free, instant, and real. Pass geo whenever you have the user's coordinates and near when they NAMED a place ('in Oakland') — a named place is where they are asking ABOUT, which is not always where they are. WHICH TOOL: use this for 'what is there' (no personalization, anyone gets the same answer); use discover for 'where should I eat', which is drawn from this user's own order history; use menu to read what a specific restaurant SELLS, which opens their site and is metered. This one never orders, never charges and never opens a browser. THE FIELDS ARE THE FACTS. Every field returned is stored data: relay the name, address, cuisine, rating, distance and open-now as given, and never add a fact Layout did not return (hours, distance, whether a place exists, what the building has). When the user asks which one to pick, pick one and say why from those fields; what a chain or kind of place is generally like may inform the why, said as general knowledge and never as something Layout checked. For what ONE place is known for or what to order there, call menu with mode 'knowledge'. HOURS ARE PART OF THE ANSWER, NOT AN EXTRA. Every place carries openStatus: 'open', 'closed', or 'unknown'. Results already lead with the open ones, so RECOMMEND FROM THE TOP and never present somewhere that is shut as a place to go right now. Say closesAt when you have it — 'open until 5' is the answer somebody can act on, 'open' is not. When they asked about ONE SPECIFIC PLACE, give them its status straight: 'they're closed, they open again at 7' or 'open until 9'. 'unknown' means Layout holds no hours for that place — say you do not know its hours, and NEVER report it as closed or imply it is open. openNow is the same answer as a boolean for older clients; prefer openStatus, whose null case has a name. AN EMPTY LIST IS AN ANSWER, UNLESS stillChecking OR notOpenNow SAYS OTHERWISE. stillChecking means Layout found places there and has not finished working out whether they take online orders, so an empty or short list is not the whole story: say you are still checking, offer to look again in a moment, and never say there is nothing there. notOpenNow means Layout DID find real restaurants there and every one of them is closed right now — that is why places is empty, and saying there is nothing near them is false. Tell them nothing is open, read the first notOpenNow row's opensAt for when the earliest one opens, and offer to look further out. Never offer one of those as somewhere to go now, and never promise to tell them when it opens: Layout cannot schedule, wait or message anybody. With neither flag, say Layout has nothing matching and offer to widen the search — never fall back to restaurants you know of, which would present your own guess as Layout's answer. To order from one, call order with its name and address.
profile
ChatGPTRead or update the user's Layout food profile — the preferences Layout applies to every order: dietary restrictions ('vegetarian', 'no shellfish'), favorite restaurants, usual orders, the first name orders go under, and whether dietary restrictions are applied automatically. Use 'get' to read it; use 'update' when the user states a LASTING food preference ('I'm allergic to peanuts', 'my usual at Chipotle is a chicken bowl') so future orders respect it. Ask before saving anything the user didn't clearly state, and tell them what you saved. Remove a note by its id from 'get'. This tool never orders and never stores payment or contact info. Phone number, email, addresses and cards can NOT be viewed or changed here by design — when the user asks about those (or asks to 'manage my account'), reply in ONE short sentence with the link — e.g. "That's managed on your Layout account page: https://account.layout.link" — and stop. Do NOT enumerate what this tool can or can't do, and do NOT explain the design; answer the question, give the link, done.