---
title: "Create a build-to-order cart"
method: POST
path: "/carts/build-to-order"
tags: ["Checkout Carts"]
---

# Create a build-to-order cart

`POST /carts/build-to-order`

## Request

- Base URL: `https://api.ekho.com`
- URL: `https://api.ekho.com/carts/build-to-order`
- Auth: HTTP bearer

Creates a checkout cart for the account bound to the token and returns it with a `checkout_url` the buyer can be sent to. The account that owns the cart is resolved server-side from the token, so the request carries no owner identifiers. Buyer prefill is optional. Name the vehicle with `manufacturer_id`, `model_id`, `variant_id`, and `style_id`, the same four that address a vehicle on the catalog endpoint. A cart holds one vehicle. Pass `accessories` for anything sold with it, and `is_preorder` to create the cart as an ante-preorder. To sell a specific dealer unit that already exists, use `POST /carts/inventory`.

## Request body

- CreateBuildToOrderCartRequest
  - `manufacturer_id` string, required: The manufacturer of the vehicle.
  - `model_id` string, required: The model being built.
  - `variant_id` string, required: The trim of that model.
  - `style_id` string, required: The colour of that trim.
  - `accessories` string[]: Accessories sold with the vehicle. A `part` is fitted to the vehicle and financed with it; `gear` and `accessory` items are sold alongside it and priced as separate cart items. Which one an id is comes from the catalog, not from this request.
  - `is_preorder` boolean: When true, the cart is created as an ante-preorder.
  - `prefill_cart` CartPrefill
    - `buyer` object
      - `full_name` string
      - `email` string, email
      - `phone` string
      - `address` CartPrefillAddress: Buyer address. The state sets where the vehicle is registered.
        - `line1` string
        - `line2` string
        - `city` string
        - `state` string: Two-letter state code.
        - `zip` string
  - `cancel_redirect_url` string: URL the buyer is redirected to if they cancel checkout.
  - `metadata` object: Free-form metadata carried through to the cart, and on to the order and its vehicles. Stored as written, so a nested object or an array needs no JSON encoding and reads back in the shape it was sent. At most 100 object keys and 2 levels of object nesting, and no array may hold another array.
  - `promo_codes` string[]: Promotion codes, matched against the promotions published for your account. A code matching none is skipped rather than failing the request. At most 10. Matching runs after the cart is written, so a `validate_only` request reports nothing about them.
  - `validate_only` boolean: When true, the request is checked as a real one would be, then stops: no cart is created. A request that would have succeeded answers 200 with `{"validate_only": true}`; one that would have failed answers the same error. Reserves nothing, so a unit can still sell between a passing check and the create.

## Response `200`

A `validate_only` request passed. No cart was created.

- object
  - `data` CartValidation, required
    - `validate_only` true, required: Always true. Marks a response that validated a cart without creating one.
    - `dropped_accessories` DroppedAccessory[], required: Accessories named in the request that the cart was built without. Always present, empty when every one was added. A non-empty list is the only signal that the cart is priced for less than was asked for, so check it before treating the response as a complete order. Reported here for the cart that would be created.
      - `id` string, required: The accessory id exactly as the request wrote it.
      - `reason` 'not_found' | 'invalid_id' | 'inactive' | 'incompatible', required: Why it was left out. `not_found`: no accessory has that id. `invalid_id`: the id is malformed. `inactive`: it exists but is not sold. `incompatible`: it is not sold for this vehicle or manufacturer.

## Other responses

- `201`: The cart was created.
- `400`: The request could not create a cart.
- `401`: Authentication is missing or invalid.
- `403`: The token lacks `carts:write`.
- `429`: The rate limit has been exceeded.
- `500`: An unexpected error occurred.

## Changes

- **2026-10-20** `efe485119dd7`: 2 info
  - added the required property `data/dropped_accessories` to the response with the `200` status
  - added the required property `data/dropped_accessories` to the response with the `201` status
- **2026-10-19** `87f98453866f`: 1 info
  - added the new optional request property `promo_codes`
- **2026-10-15** `223234fa2ded`: 2 breaking
  - the `metadata/additionalProperties/` request property type changed from `string` to no type
  - the `data/metadata/additionalProperties/` response's property type changed from `string` to no type for status `201`
- **2026-09-26** `5fe44535e1ab`: 2 info
  - added the new optional request property `validate_only`
  - added the success response with the status `200`
- **2026-09-18** `78c545d96a34`: 4 breaking, 17 warning, 1 info
  - added the new required request property `manufacturer_id`
  - added the new required request property `model_id`
  - added the new required request property `style_id`
  - added the new required request property `variant_id`
  - …18 more

[Operation history](https://skmtc.dev/ekho/apis/ekho-api/changes/carts/build-to-order/post.md)

---

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