# Capture an order

Capture a full or partial payment against an authorized order with POST /captures, and attach the order lines and shipping details each capture covers.

Capture is the call that moves money from the customer to your bank account, and you make
it when you fulfill. This page covers capturing in full and capturing in parts as you ship.
It assumes you have an authorized order and credentials that can act on it.

## What capture means

Checkout authorizes: it reserves the money without taking it. Capture takes it. They are
separate because you should only charge for what you actually ship, and the moment you
know that's fulfillment, not purchase. It's also why partial capture exists rather than
being an edge case — an order shipped in two parcels is two captures against one
authorization, and the customer is charged as each one leaves.

## Before you start

You need the `order_id` from a completed checkout, and an API credential for the same
environment the order was created in. Paths are relative to your
[base URL](/api/authentication/#base-urls): `https://api.playground.kustom.co/` while
testing, `https://api.kustom.co/` in production.

**Send a `Kustom-Idempotency-Key` on every write.** Every `POST` and `PATCH` to Order Management moves money or changes the order. Send a `Kustom-Idempotency-Key` header with one UUID per operation, and reuse it when you retry, so a call repeated after a timeout runs once instead of twice. [Idempotency and retries](/order-management/idempotency-and-retries/) has the rules and the retry schedule.

## Capture

Capture the whole order once it ships.

```http
POST /ordermanagement/v1/orders/{order_id}/captures
Kustom-Idempotency-Key: 6a1f7c3e-9b2d-4a11-8f0e-2c5d7b9a4e13
Content-Type: application/json
```

```json
{
  "captured_amount": 6000,
  "reference": "c9a94cf1-7126-4f55-92fa-4d34f416ed7f",
  "description": "Shipped the full order",
  "order_lines": [
    {
      "type": "physical",
      "reference": "123050",
      "name": "Tomatoes",
      "quantity": 10,
      "quantity_unit": "kg",
      "unit_price": 600,
      "tax_rate": 2500,
      "total_amount": 6000,
      "total_tax_amount": 1200
    }
  ],
  "shipping_info": [
    { "shipping_company": "Postnord", "tracking_number": "1234567890" }
  ]
}
```

Only `captured_amount` is required, and for a full capture it must match the order's
`order_amount`. The rest is optional but worth sending: `order_lines` is what the customer
and the Portal display as the contents of this capture, and `shipping_info` is required in
practice for physical goods.

A successful capture returns `201 Created` with a `capture_id` and a `Location` header.
Use that `Location` as given rather than assembling a URL from the id:

```http
GET /ordermanagement/v1/orders/{order_id}/captures/{capture_id}
```

Then read the order itself and check the arithmetic — `captured_amount` should have gone
up by what you captured, and `remaining_authorized_amount` down by the same. That's the
check that catches a double capture, which is the failure an idempotency key exists to
prevent.

## Partial capture

Partial capture is the same endpoint with a smaller `captured_amount` — send the lines
you're actually shipping, and their `shipping_info`. Do this once per shipment. The
remaining authorization stays available for the rest, and the order's status becomes
`PART_CAPTURED`.

Keep each capture's order-line `reference` values: a later refund of something from that
shipment sends the same references, so the credit lands on the right invoice — see
[Partial refund](/order-management/refunds/#partial-refund).

## Troubleshooting

A capture larger than `remaining_authorized_amount` is rejected rather than clamped. So is
a capture against an order that has expired — authorizations don't last indefinitely, and
once one expires the money is gone from your side of the transaction. If fulfillment is
going to run long, extend before it expires rather than after.

Both of those come back as [`CAPTURE_NOT_ALLOWED`](/api/errors/#error-order-management-capture-not-allowed),
which is a statement about where the order is rather than about your request — so retrying it
unchanged will fail identically. Read the order first. A capture id you no longer recognize
gives [`NO_SUCH_CAPTURE`](/api/errors/#error-order-management-no-such-capture). List the order's captures
rather than reconstructing the id.

An order with a very large number of lines can exceed the accepted body size and return
[`PAYLOAD_TOO_LARGE`](/api/errors/#error-order-management-payload-too-large). Sustained traffic above
your account's limit returns [`TOO_MANY_REQUESTS`](/api/errors/#error-order-management-too-many-requests),
and a Kustom-side failure returns
[`TEMPORARILY_UNAVAILABLE`](/api/errors/#error-order-management-temporarily-unavailable) — the last two
are worth retrying with backoff, the rest never are.

## Related

- [Refund an order](/order-management/refunds/) — giving it back once captured
- [Extend order expiry](/order-management/extend-authorization/) — before it expires, not after
- [Order Management](/order-management/) — where capture sits in the lifecycle
- [Update or cancel an order](/order-management/update-and-cancel/) — reading back what you just did
