# Cancel while building

October 2, 2026 · API

Three fixes from a developer's report. A cancel your application sends while a cart is still building is kept and honoured. A request whose `placeId` and `restaurantName` disagree is refused instead of quietly ordering from the `placeId`'s store. And the store's address now travels with its name everywhere you and the person see it.

## What changed

- `POST /v1/carts/:id/cancel` on a cart that is still building answers `202 { "cart": { "id": "crt_…", "status": "canceling" } }` instead of `409 cart_not_ready`. Layout releases the cart the moment the build lands, never offers it to the person, and never sends Layout's cart-ready text for it. `GET /v1/carts/:id` reads `canceling`, then `canceled`. A confirm on a `canceling` or `canceled` cart is `409 cart_closed`.
- A cancel on a cart that was already confirmed is still `409 cart_closed`, now with `error.order`: the order and its state now (`placing`, `placed`, `unconfirmed` or `refunded`).
- The MCP `order` tool's `cancel: true` takes the build's `idempotencyKey` while the cart is still building, and answers `cancel.status: "canceling"`.
- `POST /v1/carts` and `/answer` with a `placeId` and a `restaurantName` that name different stores answer `409 store_mismatch`. Nothing is built. `error.store` is `{ placeId, name, address }` for the store the `placeId` names, and `candidates`, when Layout could find them, are stores matching the name. Over MCP, the build answers `storeMismatch`.
- The cart's `store` carries the address whenever Layout knows it, on ready, failed, answered and closed carts. `GET /v1/orders/:id` and every webhook payload add `store_address`, null when unknown. A sandbox cart's `confirmationCard` names the address.
- Layout's cart-ready text names the store and its address, and for a cart your application built it opens with your application's name from the console: `Acme Assistant built your order at Layout test kitchen, 1 Sandbox Way, San Francisco, CA 94107:`.

```
409 {
  "error": {
    "code": "store_mismatch",
    "message": "placeId names Layout Test Kitchen North, which does not match restaurantName. Nothing was built. Ask the person which store they mean and send only that store's placeId, or only restaurantName with near or geo.",
    "store": { "placeId": "test_place_branch_north", "name": "Layout Test Kitchen North", "address": "31 Sandbox Way, San Francisco, CA 94107" }
  }
}
```

## Why it matters

A cancel sent while a cart was building used to be refused, and the cart then landed and Layout texted the person to reply YES to an order your application had abandoned. A request carrying a `placeId` from one store and the name of another used to build at the `placeId`'s store without a word, and a person nearly ordered from the wrong place. Naming the address beside the store is how the person tells two branches apart before they agree to pay.

## What to do

Treat `canceling` as on its way to `canceled`, and never confirm it. On `store_mismatch`, ask the person which store they mean and build again with only that store's `placeId`. Show `store.address` beside the store's name. See [POST /v1/carts/:id/cancel](https://developer.layout.link/reference/carts#cancel) and [Errors](https://developer.layout.link/reference/errors).

## Breaking changes

None under the [versioning policy](https://developer.layout.link/reference/versioning): `canceling` is a new cart status, `store_mismatch` a new error code on a 409, and `store_address` a new field. One answer moves: a cancel on a building cart used to answer `409 cart_not_ready` and now answers `202 canceling`. A request that used to build at the `placeId`'s store despite naming another now answers `409 store_mismatch`.
