---
title: "Build a cart"
method: POST
path: "/carts"
tags: ["Carts"]
---

# Build a cart

`POST /carts`

## Request

- Base URL: `https://api.layout.link/v1`
- URL: `https://api.layout.link/v1/carts`
- Auth: HTTP bearer

Starts a real build on the restaurant's own site and answers at once. Poll GET /carts/{cartId}. Nothing is charged: a cart is built and unpaid until it is confirmed.

## Headers

- `Layout-Version` '2026-10-01'
- `Idempotency-Key` string

## Request body

- CreateCartRequest
  - `request` string: What the person wants, in their words.
  - `items` string[]: One entry per item, when you have a list.
  - `placeId` string: The store, from a places search. Sent with restaurantName, the two must name the same store, or the build is refused with `store_mismatch`.
  - `orderUrl` string
  - `restaurantName` string
  - `near` string: Where the person is, as text. Used with restaurantName.
  - `geo` Geo
    - `lat` number, required
    - `lng` number, required
  - `timeZone` string
  - `fulfillment` union: `pickup`, `delivery`, or a delivery object. Leave it out for pickup. A bare `delivery` uses the person's saved default delivery address.
    - 'pickup' | 'delivery'
    - object
      - `type` 'pickup', required
    - DeliveryRequest
      - `type` 'delivery', required
      - `address` string: Where to deliver, as the person would write it. Leave it out to use the person's saved default delivery address. It must be the person's own address.
      - `addressLabel` string: One of the person's saved addresses: "work" or "personal" for the default of that kind, or the label of one from savedAddresses. Used when address is not sent.
      - `unit` string: Apartment, suite or floor, when address does not include it.
      - `instructions` string: Notes for the driver, for example a gate code.
      - `handoff` 'leave_at_door' | 'meet_at_door': What the driver does at the door. Defaults to `leave_at_door`, with a photo.
  - `tip` union: The driver's tip, all of it to the driver. A percent of the food, or an amount in minor units. Leave it out for the lowest preset; send `{ "percent": 0 }` for no tip.
    - object
      - `percent` integer, required
    - object
      - `minor` integer, required
  - `idempotencyKey` string: Unique per real intent. A retry with the same key and body returns the same answer. Optional when the request carries an Idempotency-Key header, whose value is then used; when both are sent, this one names the cart or the confirm.

## Response `202`

The build started, or this key already started it.

- CartAccepted
  - `cart` object, required
    - `id` string, required
    - `status` 'building' | 'ready' | 'needs_answer' | 'failed' | 'canceling' | 'canceled' | 'expired' | 'closed', required: `canceling`: a cancel arrived while the cart was building. Layout releases the cart the moment the build lands, never offers it to the person, and never texts it; read the cart until it is `canceled`.

## Other responses

- `400`: `invalid_request`: a field is missing or malformed. The message names it. `unsupported_version`: the Layout-Version header names no version this API answers.
- `401`: The token is missing, expired, revoked, issued for another audience, or the person disconnected. Every 401 reads the same.
- `403`: The credential is real and may not do this: an application or account that is suspended, a live person on an unapproved application, a scope the token was not granted, or a person whose Layout account cannot order yet.
- `409`: `idempotency_conflict`: the key was used for another cart. `in_progress`: the same request is still starting. `store_ambiguous`: pick one of `candidates` and send its placeId. `store_not_found`: no orderable store matched. `store_mismatch`: `placeId` and `restaurantName` name different stores; `error.store` is the store the placeId names, with `candidates` matching the name when Layout could find them. Nothing was built: ask the person which store they mean. `idempotency_conflict`: the Idempotency-Key was already used with a different request. `in_progress`: the first request with that key is still running; retry after `Retry-After`.
- `429`: `build_limit`: a daily build budget is spent; the message names which. `rate_limited`: a per-minute brake.
- `500`: Layout failed to answer. Retry with backoff.
- `503`: Credentials or a dependency could not be checked right now. Retry with backoff.

---

[API](https://skmtc.dev/layout/apis/layout-partner-api.md) · [All operations](https://skmtc.dev/layout/apis/layout-partner-api/llms.txt) · [OpenAPI document](https://skmtc.dev/layout/apis/layout-partner-api/revisions/7ff4ba01bf56?raw)
