MCP builds match REST
Store names
- A build session’s
buildwith aplaceIdand arestaurantorrestaurantNamenow compares every word of the name against the store’s name and address, asPOST /v1/cartsdoes. A name the store does not carry answersstoreMismatchand builds nothing. - A
buildorpreviewfor a name Layout finds no restaurant by answersstore_not_found, on a preview aspreview.outcome: "store_not_found"withpreview.storeNotFound.candidates. Its candidates are restaurants with other names within about 30 miles. Offer them by their own names, never as branches, and build at one only if the person picks it, with itsplaceIdand its own name asrestaurant. - In sandbox, a name or
test_place_id that no test restaurant carries answersstore_not_found, with the test restaurants as candidates.
Cancel while building
A build grant can now cancel a cart that is still building: order with action: "status", cancel: true and that build's idempotencyKey answers cancel.status: "canceling", and Layout releases the cart the moment the build lands and never texts the person about it. It works only for a build your application started; any other key answers no_active_order. In mcp-tools.json it is the session action cancel_build. A cancel naming neither an orderId nor an idempotencyKey is still refused as forbidden. See Confirm with the person's code.
ord_ ids on every action
orderId takes the ord_ publicOrderId on order_status, confirm and a cancel, and every answer is the same as for the id the build returned. A build grant reads only its own application’s orders. See The tools.
Carted, expired and canceled carts
- A ready cart nobody has confirmed, read by
orderId, answersplacement.status: "carted"withtotalMinor,priceandexpiresAt, where it answeredbuilding. It waits on the person, so stop polling it. - A cart past its confirm window reads
cart_expiredby key, andfailedwithexpired: truebyorderId. Its answer carriesfailureCode: "CART_EXPIRED", where it wasOUR_TIMEOUT, test restaurants included. - Reading an expired cart with
order_statuscloses it, as a REST read does, soorder.failedis sent at once. - A cart the person canceled reads
canceledby key, and byorderIdits message says it was canceled.
Confirm totals
A build session’s confirm carries totalMinor, the total you confirmed with, fee included; merchantTotalMinor, the restaurant’s own total; and subtotalMinor, the food before tax, absent when Layout cannot read it. They are on every answer about an order that went ahead: placing, placed, confirmed, already_placing and an alreadyConfirmed answer. See Confirm in sandbox.
Waits you can act on
A confirm or resend answered rate_limited with no reason now carries retryAfterSec. too_many_attempts and limiter_unavailable carry retryAfterSec too, and too_many_attempts carries lock: person, requester or spacing.
grant_expired
An expired build grant sent to mcp.layout.link now answers 401 with error.code: "grant_expired" and WWW-Authenticate: Bearer error="invalid_token", error_description="grant_expired", as on api.layout.link. Mint a new grant. Any other refused grant still answers unauthorized. A secret key sent to mcp.layout.link is 401 unauthorized with WWW-Authenticate: Bearer error="invalid_token" and a message naming the build grant. See MCP refusals.
Smaller changes
- mcp-tools.json adds
buildSessionTools, the tools as a build grant’stools/listreturns them, rendered on MCP tools. - The
orderandorder_statusoutput schemas in a build grant’stools/listno longer listbuild.confirmationCard, which a build session never gets. - A simulated challenge carries
smsSent: false.
Breaking changes
- A build session’s
buildwhose name has a word the store does not carry now answersstoreMismatch, where it could build. - A build session’s
buildfor a name Layout cannot find now answersstore_not_found, where it could ask which of several other restaurants. - A build session’s confirm
subtotalMinoris now the food before tax, not the restaurant’s total. ReadmerchantTotalMinorfor that, andtotalMinorfor what was confirmed. - Code that polls a carted cart by
orderIduntil it leavesbuildingnow getscartedat once, and a poll of an expired cart answerscart_expiredorfailed. - Code that matches
OUR_TIMEOUTfor an expired cart over MCP now getsCART_EXPIRED. - Code that matches
unauthorizedfor an expired build grant now getsgrant_expired. - A build session’s
buildthat asks for delivery builds nothing and answersdeliveryUnavailable: { reason: "not_enabled" }, where it used to build a pickup.
Everything else here is a new field, value or action, or a call that used to be refused, and code that ignores what it does not know keeps working.