# What to send

The order payload, block by block — why one line per cart item rather than a total, the four amount identities Kustom validates but never computes, which options to pass and what belongs in a reference.

> Live page: the order payload as four annotated blocks, one open at a time, with the
> order lines shown as a worked example alongside the totals they sum to.

**What's being bought** — `order_lines`, `order_amount`, `order_tax_amount`. Lines do not stop at Kustom. They inform the payment method’s risk assessment, they are what the customer reviews in their payment app afterwards, they reach your TMS when the Kustom Shipping Assistant (KSA) is used, and both capture and refund carry them. One aggregated line costs you acceptance rate, a legible purchase history, carrier selection, mixed tax rates, and any partial refund. See /checkout/tax-and-order-lines/.
**Where the purchase happens** — `purchase_country`, `purchase_currency`, `locale`. These three fix the market: which payment methods appear, which language the checkout speaks, and which currency the amounts are in. purchase_country is fixed at creation. locale and purchase_currency can be updated afterwards, currency only to one your account is enabled for. See /checkout/use-cases/multi-market/.
**Where Kustom calls you back** — `merchant_urls`. Four required URLs, and a fifth that is optional but worth sending. Confirmation is where the customer lands, push is how you learn about the order when the browser never arrives, and validation is the last point at which you can still refuse a purchase you cannot fulfill. Confirmation and push cost money when wrong, and every URL here needs publicly reachable HTTPS with a real certificate — a self-signed one that worked in testing will not. Leaving validation out is a decision either way: without it, an order created an hour ago completes on whatever was true then. See /api/checkout/callbacks/.
**What the order can do** — `options`. Order-level capability: business buyers, age gating, a separate shipping address, VAT display, consent checkboxes. B2B and age verification need your account enabled first, and both are order-level — there is no per-line age flag. See /checkout/checkout-options/.

```json
{
  "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
    }
  ],
  "order_amount": 108000,
  "order_tax_amount": 21600,
  "purchase_country": "SE",
  "purchase_currency": "SEK",
  "locale": "en-SE",
  "merchant_urls": {
    "terms": "https://www.northwind-outfitters.com/terms",
    "checkout": "https://www.northwind-outfitters.com/checkout",
    "confirmation": "https://www.northwind-outfitters.com/checkout/confirmation?order_id={checkout.order.id}",
    "push": "https://www.northwind-outfitters.com/api/kustom/push?order_id={checkout.order.id}",
    "validation": "https://www.northwind-outfitters.com/api/kustom/validate"
  },
  "options": {
    "allow_separate_shipping_address": true
  }
}
```

Values for this example:

| Must hold | Value in this example |
| --- | --- |
| order_amount equals the sum of every line total | `108000` |
| order_tax_amount equals the sum of every line tax | `21600` |
| tshirt-001: quantity x unit_price - discount | `50000` |
| tshirt-001: tax at 25% | `10000` |
| chinos-001: quantity x unit_price - discount | `70000` |
| chinos-001: tax at 25% | `14000` |
| discount-10pct: quantity x unit_price - discount | `-12000` |
| discount-10pct: tax at 25% | `-2400` |

**The shape.** One order object, four blocks, each answering a different question. **What's
being bought** — `order_lines` plus the totals that have to reconcile with them. **Where** —
`purchase_country`, `purchase_currency` and `locale`. Only the country is fixed at creation;
the other two can be changed on a later update. **Where Kustom calls you back** — `merchant_urls`. **What the order can do** —
`options`, the capability switch covered further down this page. Everything else is optional.
Get these four right and the rest is detail.

## Why one line per item

Nothing in the API requires `order_lines` to be granular — Kustom accepts a single line
summing an entire cart. Nothing downstream forgives it.

Order lines do not stop at Kustom. Five systems consume the array, and each degrades
differently on a single aggregated line — two of them outside Kustom entirely: the
payment method’s risk assessment, and the TMS reached through the Shipping Service
Callback API.

| Consumer | What it does with them | Cost of one aggregated line |
| --- | --- | --- |
| The payment method's risk check | It reads the lines when deciding whether to accept | Thinner input, lower acceptance |
| The customer's payment app | Where they review the purchase afterwards | "1 item · 250.00" is all they see |
| Your TMS, via the Shipping Assistant | Carriers, pickup points, delivery windows | Cannot route a box it cannot describe |
| Kustom tax validation | Validates tax per line | One rate only — a mixed-rate cart is rejected |
| Capture and refund | Both calls carry order_lines | No vocabulary for a partial refund of one item |

Two of the five consumers above sit outside Kustom's own validation entirely, which is why
the argument starts with conversion rather than mechanics. The payment method — Klarna today,
and potentially others tomorrow — reads `order_lines` to assess risk and decide whether to
accept the purchase at all. There's no Kustom risk engine standing between a merchant and the
payment method, only the lines sent to it. The same lines are also what the customer reviews
afterwards, inside their payment app rather than your confirmation page.

| What breaks on one aggregated line | Why |
| --- | --- |
| Lower acceptance from the payment method | The payment method's risk assessment reads the array, and a flattened cart is thinner input |
| A receipt the customer doesn't recognize | Their payment app shows the line you sent, not your product catalog |
| A box the TMS can't route | The Shipping Service Callback API hands it `order_lines`, when the Kustom Shipping Assistant (KSA) is in use |
| A rejected order, not a warning | Kustom's tax validation checks one `tax_rate` per line, and a mixed-rate cart has nowhere to put two |
| No partial refund | Capture and refund both carry `order_lines`, and an aggregated line has no vocabulary for "one of three" |

The recommendation follows directly: one order line per cart item, always. Shipping and fees
aren't incidental to the cart — give each its own typed line rather than folding it into a
product line or into the order-level totals, for the same reason a product line stays
separate: something downstream needs to identify it as what it is. Line mechanics — required
fields, the `sales_tax` type, anonymizing a product name — are covered in
[Order lines and tax](/checkout/tax-and-order-lines/).

## Why amounts are strict

Kustom doesn't compute these numbers for you — it validates the following and rejects the
order if any one fails:

- Amounts are integers in minor units (250.00 SEK is `25000`).
- `order_amount` equals the sum of every `order_lines[].total_amount`.
- `order_tax_amount` equals the sum of every `order_lines[].total_tax_amount`.
- Per line, `total_amount` equals `quantity` × `unit_price` − `total_discount_amount`.
- Per line, `total_tax_amount` sits within ±1 of `total_amount − total_amount × 10000 / (10000 + tax_rate)`.
- `tax_rate` carries two implicit decimals, so 25% is `2500`.

The worked example at the top of this page is three lines that satisfy all six — including a
negative discount line, which is where the arithmetic is easiest to get wrong. A broken total
is the single largest source of a `400` on order creation, and the strictness follows directly
from the previous section: once a
line's numbers leave the building — into the payment method's risk assessment, a customer's
payment app, a TMS, a partial refund — Kustom has no way to quietly fix them the way it might
if they stayed internal.
See [What an order is](/basics/what-an-order-is/) for the object these amounts sit on, and
[Troubleshooting Checkout](/checkout/troubleshooting/) for the exact error a broken identity
returns.

## What belongs on the order, and what belongs on each line?

A field belongs to the order once, or to every line — never both, and the two are easy to
confuse on a first pass.

Per order:

- Who is buying
- Whether they are old enough
- Which addresses apply
- What they've consented to
- Where to call you back

Per line:

- What it is
- How many
- What it costs
- Its tax
- Its own reference

The mistake worth naming here: looking for a per-line age flag. There isn't one — age
verification, like selling to businesses, lives in `options`, at the order level. Both are
covered under [What options to pass](#what-options-to-pass) below.

## What to put in a reference

Three places on an order hold data of yours, and they answer different questions.

| Slot | Lives on | Use it for |
| --- | --- | --- |
| `order_lines[].reference` | each line | Your SKU or catalog id for that item |
| `merchant_reference1`, `merchant_reference2` | the order | Your own order number, so a Kustom order can be found from your system and back |
| `merchant_data` | the order, and each line | Anything of yours that only your own code reads |

The rule that matters more than the table: anything you'll join on later has to be stable
and minted by you before the order exists, and must never be regenerated per cart. A
`merchant_reference1` that's a fresh value on every retry looks fine until a customer
double-submits, and you can no longer tell which of the two orders is the one your own system
already knows about.

The escape hatch, for an order created before your own order number exists yet:
`merchant_reference1` can be set afterward, through Order Management, once you have something
to put there. See [Your own data on an order](/basics/conventions/merchant-data/) for the
third slot — `attachment` — and the full detail on all three.

## Where Kustom calls you back

`merchant_urls` carries four required URLs, and two of them cost money when they're wrong.
`confirmation` is where the browser redirects the customer after purchase. `push` is the
server-to-server call that tells you an order exists even when the browser never gets there —
a closed tab, a crashed app, a customer who never sees your confirmation page at all. Both need
to be publicly reachable HTTPS with a real certificate. A self-signed one that worked in a
local test fails silently in Kustom's hands, not yours.

The other two required URLs, `terms` and `checkout`, matter for a working checkout but not for
whether the purchase itself completes. Full detail, including the optional callback URLs, is
at [Merchant URLs explained](/checkout/integrate/merchant-urls/).

The example above sends a fifth. `validation` isn't required, and it's the only callback that
can still refuse a purchase: an order is created when the customer opens the checkout and bought
whenever they get around to pressing the button, and stock, a price or a fraud check can turn
between the two. Send it if anything about the order can stop being true in that window — and
note that skipping it isn't the same as opting out, because an unanswered validation request
completes the purchase after ten seconds either way. [Answer the
callbacks](/checkout/integrate/validation-callback/) is where that decision, and the flag that
reverses the default, are covered. [Server-side
callbacks](/checkout/callbacks/) has the payload and response shape for every URL in the list.

## What options to pass

| Pass it | When |
| --- | --- |
| `allowed_customer_types: ["person","organization"]` | You sell to businesses. Availability is narrower than it looks |
| `verify_age: { minimum_age: 18 }` | Age-restricted goods, NO/SE. Account must be enabled first. Prefer this over the two older identity-number options |
| `allow_separate_shipping_address` | You ship somewhere other than you bill |
| `vat_removed` | B2B summaries where VAT should not show |
| a custom checkbox | Legal requires explicit consent |
| a delivery note (≤255 chars) | You have something to say on the confirmation page |

Colors and corner radius are set in Kustom Portal. See the
[Checkout options](/checkout/checkout-options/) reference for the rest of the field table, and
[Sell to businesses (B2B)](/checkout/use-cases/b2b/) and
[Age verification](/checkout/use-cases/age-verification/) for the two rows above that need
your account enabled first.

## What not to send

- **Addresses from your own guest-checkout form.** Kustom drops them the moment the customer
  types their own into the checkout, so shipping to the form's address rather than the
  customer's is a bug waiting for the first mismatch.
- **A `reference` regenerated on every cart.** Mint it once and reuse it — see the mistake
  under references, above.
- **Personal data in `merchant_data`.** It's returned to you exactly as sent and stored like
  any other field of yours, not a place built for anything a customer would expect Kustom,
  rather than you, to be careful with.

## Prove it

> 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
  }
}
```

A `201` hands back an `order_id` and an `html_snippet` —
[Create the order](/checkout/integrate/create-order/) is where both get used.

## Related

- [Order lines and tax](/checkout/tax-and-order-lines/) — line validation, `sales_tax` lines, anonymizing product names
- [Checkout options](/checkout/checkout-options/) — the full `options` field table
- [Merchant URLs explained](/checkout/integrate/merchant-urls/) — every URL, who calls it and when
- [Your own data on an order](/basics/conventions/merchant-data/) — the three data slots in full
- [Create the order](/checkout/integrate/create-order/) — post this payload
