Refund an order
Credit money back on a captured order with POST /refunds, in full or in part, with return fees where the market supports them and allocated to the right shipment.
Refunding returns money you have already captured. It’s a separate call from cancelling, which releases money you never took — if any part of the order is captured, refund is the only way back. This page covers refunding in full and refunding in part. You need a captured order and a credential for its environment.
Before you start
Section titled “Before you start”Paths are relative to your base URL:
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 has the rules and the retry schedule.
Read the order first. refunded_amount may not exceed captured_amount, and that’s the
constraint most refused refunds hit.
Refund
Section titled “Refund”Refund everything you captured when the whole order comes back.
POST /ordermanagement/v1/orders/{order_id}/refundsKustom-Idempotency-Key: 4c2e8a51-7d3f-4b6e-9a10-5f8d2c7e1b94Content-Type: application/json{ "refunded_amount": 20000, "reference": "4c2e8a51-7d3f-4b6e-9a10-5f8d2c7e1b94", "description": "Returned: the whole order"}refunded_amount is the only required field. description is shown to the customer, so
write it for them rather than for your logs.
A successful refund returns 201 Created with an empty body and the new refund’s id in two
headers, Refund-Id and Location. Use that Location as returned rather than assembling
one. Then re-read the order: refunded_amount should have risen by what you refunded.
Partial refund
Section titled “Partial refund”Refund part of what you captured — a returned item, say — with a smaller refunded_amount,
and send the order lines being returned. The lines are optional in general but are what
drives allocation when the order shipped in parts.
POST /ordermanagement/v1/orders/{order_id}/refundsKustom-Idempotency-Key: 9dfcd4b6-9428-4835-ad93-3720ea808608Content-Type: application/json{ "refunded_amount": 10000, "reference": "9dfcd4b6-9428-4835-ad93-3720ea808608", "description": "Returned: one pair of sneakers", "order_lines": [ { "type": "physical", "reference": "B24680", "name": "Sneakers", "quantity": 1, "quantity_unit": "pcs", "unit_price": 10000, "total_amount": 10000, "tax_rate": 2500, "total_tax_amount": 2000 } ]}Deducting a return fee
Section titled “Deducting a return fee”Supported in Austria, Germany, Denmark, Finland, France, the Netherlands, Norway and Sweden,
and only on an order that was fully captured in a single capture. Model the fee as an order
line of type return_fee with a negative amount, and make the lines sum to
refunded_amount:
{ "refunded_amount": 9500, "reference": "1786c73b-a2be-4d42-a9f7-ff1c4bca5ccc", "description": "Returned: one T-shirt, less return postage", "order_lines": [ { "type": "physical", "name": "T-Shirt", "quantity": 1, "unit_price": 10000, "total_amount": 10000 }, { "type": "return_fee", "name": "Return fee", "quantity": 1, "unit_price": -500, "total_amount": -500 } ]}When the order shipped in parts
Section titled “When the order shipped in parts”Send order lines whose reference values are the same ones you sent on the capture that
contained the returned goods. Kustom uses those references to allocate the credit to the
right invoice.
Say an order shipped as two captures:
{ "captured_amount": 10000, "description": "Capture A — T-shirt and jeans", "order_lines": [ { "type": "physical", "reference": "A12345", "name": "T-shirt", "quantity": 1, "unit_price": 3000, "total_amount": 3000 }, { "type": "physical", "reference": "A98765", "name": "Jeans", "quantity": 1, "unit_price": 7000, "total_amount": 7000 } ]}{ "captured_amount": 10000, "description": "Capture B — sneakers", "order_lines": [ { "type": "physical", "reference": "B24680", "name": "Sneakers", "quantity": 1, "unit_price": 10000, "total_amount": 10000 } ]}The customer returns the sneakers, so the refund must carry B24680 — the reference from
Capture B, the capture the sneakers were in. That’s the refund body above. Sending
A12345 instead, or sending no lines at all, credits the wrong invoice.
The lines must sum exactly to refunded_amount, and refunded_amount must be no greater
than the captured_amount of the capture being refunded. Check in the Kustom Portal that the
credit landed on the invoice you expected: the API call succeeds either way.
Release what you never captured
Section titled “Release what you never captured”If you’re not going to capture the rest of an order, release the remaining authorization rather than leaving it to expire:
POST /ordermanagement/v1/orders/{order_id}/release-remaining-authorizationKustom-Idempotency-Key: 2471e418-d208-49b4-bce4-d2cd45dbc1d4No body. Returns 204 No Content. This isn’t a refund — nothing captured moves — it just
frees the reservation on the customer’s side.
Troubleshooting
Section titled “Troubleshooting”Errors carry error_code, error_messages and a correlation_id. Branch on the code,
and quote the correlation id to support.
The one to recognize is
REFUND_NOT_ALLOWED: the order
doesn’t have that much captured amount left to return — nothing has been captured yet, the
captured amount is already fully refunded, or refunded_amount is larger than what remains. It describes the order’s state, not your request,
so retrying the same call can’t succeed — capture first, or check captured_amount against
what you’ve already returned.
Related
Section titled “Related”- Settlements — how refunds show up in what you’re paid
- Capture an order — the references allocation depends on
- Update or cancel an order — cancelling instead, when nothing is captured
- Order Management — where refunds sit in the lifecycle
This page is also available as Markdown — append .md to its URL — and is indexed in llms.txt.