# Create the order

POST your order to /checkout/v3/orders, store the order_id the 201 hands back, read the order back and reuse it when the customer returns to the checkout.

> Live page: a request runner, which sends this call and shows the response inline. Programmatic equivalent: `POST https://api.playground.kustom.co/checkout/v3/orders`, authenticated with HTTP Basic using your API username and API password.

```json
{
  "purchase_country": "SE",
  "purchase_currency": "SEK",
  "locale": "en-SE",
  "order_amount": 108000,
  "order_tax_amount": 21600,
  "order_lines": [
    {
      "type": "physical",
      "reference": "tshirt-001",
      "name": "Cotton T-Shirt",
      "quantity": 2,
      "quantity_unit": "pcs",
      "unit_price": 25000,
      "tax_rate": 2500,
      "total_amount": 50000,
      "total_discount_amount": 0,
      "total_tax_amount": 10000
    },
    {
      "type": "physical",
      "reference": "chinos-001",
      "name": "Slim Fit Chinos",
      "quantity": 1,
      "quantity_unit": "pcs",
      "unit_price": 70000,
      "tax_rate": 2500,
      "total_amount": 70000,
      "total_discount_amount": 0,
      "total_tax_amount": 14000
    },
    {
      "type": "discount",
      "reference": "discount-10pct",
      "name": "10% off",
      "quantity": 1,
      "quantity_unit": "pcs",
      "unit_price": -12000,
      "tax_rate": 2500,
      "total_amount": -12000,
      "total_discount_amount": 0,
      "total_tax_amount": -2400
    }
  ],
  "options": {
    "allow_separate_shipping_address": true
  }
}
```

You have an order payload — see [What to send](/checkout/integrate/the-order/) if you don't
yet. `POST` it to `/checkout/v3/orders` and a `201` hands back two things you'll keep: an
`order_id` and an `html_snippet`. This page is that call, what each of the two is for
and reading the order back — changing one that already exists belongs to the next page, once
there's a rendered checkout to keep in sync.

## Authenticate

Every call to the Checkout API uses HTTP Basic auth with your API username and API password,
both created in the Kustom Portal. [Authentication and environments](/api/authentication/)
covers getting them.

```http
Authorization: Basic base64(<API username>:<API password>)
```

| Environment | Base URL |
| --- | --- |
| Playground | `https://api.playground.kustom.co` |
| Production | `https://api.kustom.co` |

Credentials are environment-specific — a Playground merchant ID doesn't authenticate against
production, or the reverse. [Authentication and environments](/api/authentication/) covers
getting a key and rotating one.

## Send the request

`POST` the payload to `/checkout/v3/orders`. A `201` comes back with two things worth naming,
because the rest of this guide is built on them:

| Field | Where it lives | What it's for |
| --- | --- | --- |
| `order_id` | Response body | Identifies the order — in every later read and update, and in Order Management later |
| `html_snippet` | Response body | The markup [Render Checkout snippet](/checkout/integrate/render-checkout-snippet/) puts on your page |

Anything that fails here is a payload problem, not a request one — see
[What to send](/checkout/integrate/the-order/) for the amount identities Kustom checks before
it accepts anything.

## Store the order_id

Store `order_id` on the cart. Every later read and update of this order is keyed on it, at
`{base URL}/checkout/v3/orders/{order_id}` — the one URL you'll build for the rest of the
integration.

The `201` also carries a `Location` header, and it holds no more than the `order_id` already
does: it's that same URL, `{base URL}/checkout/v3/orders/{order_id}`, already absolute. Build
the URL from `order_id` and you never need to read it.

## Read it back

`GET` the order at `{base URL}/checkout/v3/orders/{order_id}` and you get the order exactly as
Kustom holds it — the fields you sent, plus whatever the customer has added since.

> Live page: a request runner, which sends this call and shows the response inline. Programmatic equivalent: `GET https://api.playground.kustom.co/checkout/v3/orders/{order_id}`, authenticated with HTTP Basic using your API username and API password.

This is a read. Changing an order you've already created, while a customer is looking at it, is
[Render Checkout snippet](/checkout/integrate/render-checkout-snippet/)'s job, not this page's.

## When the customer comes back

Customers close tabs and walk back to product pages. Create a fresh order every time they
return and you throw away everything they had already typed into the checkout.

Keep the stored `order_id` with the session, and on re-entry:

1. Look for a stored `order_id` before creating anything.
2. If there is one, `GET` the order. You get it back with its `html_snippet`, and can update
   it instead of creating a new one.
3. If that `GET` returns a 4xx, a 5xx or anything else unexpected, create a new order.

The fallback in step 3 isn't optional: an order expires after 48 hours without activity, and
can expire sooner. A cart change while the customer is on the page is an update to the same
order, never a second one — [Render Checkout
snippet](/checkout/integrate/render-checkout-snippet/) has the sequence.

## When it breaks

An order is valid for 48 hours after the last merchant update or customer interaction, then
expires, and a `GET` against its URL answers `404` with an empty body — create a new
order rather than trying to resurrect the old one.

A failed create isn't safe to retry blindly either: `POST /checkout/v3/orders` carries no
idempotency key, so retrying an ambiguous `5xx` can leave you with two orders instead of one.
See [Troubleshooting Checkout](/checkout/troubleshooting/) for the full error catalogue,
[Idempotency and retries](/order-management/idempotency-and-retries/) for the calls that do carry a key and
[Limits](/api/limits/) for the rate this call counts against.

## Prove it

The editor at the top of this page is wired to the Playground, not a mock — edit a field, hit
Run, and you get back a real `order_id` and a real `html_snippet`, the same two things a
production call returns.

That `html_snippet` isn't on a page yet, and updating this order only means something once it
is — both are [Render Checkout snippet](/checkout/integrate/render-checkout-snippet/)'s job.

## Related

- [What to send](/checkout/integrate/the-order/) — the payload this page posts
- [Render Checkout snippet](/checkout/integrate/render-checkout-snippet/) — put the `html_snippet` on your page, and change the order once it's there
- [Authentication and environments](/api/authentication/) — get a key, rotate one
- [Troubleshooting Checkout](/checkout/troubleshooting/) — the full error catalogue
