# Answer the callbacks

Decide whether you need address_update, shipping_option_update or validation at all, and why an unanswered validation request completes the purchase in ten seconds by default.

> Live page: opens the session console, which lists every call in this session — both the calls the docs made and the ones Kustom made back (`GET /demo-api/session/calls`).

Kustom can call your server three times while the customer is still looking at the
checkout: once when the destination changes what something costs, once when the customer picks
shipping inside the iframe and once for a last look before the purchase completes. Most
integrations need none of them. This page tells you which apply to yours, then covers the one
call that can't be skipped once you register it — validation — and the ten-second window that
decides whether an unanswered request passes the purchase or blocks it.

## Do you need any callbacks?

You need none of the three if your totals don't change with the shipping address, you offer one
shipping option or ship free and nothing about the order can become untrue between creating it
and the customer pressing **Pay order**. A catalog with flat shipping and stock that never runs
out has no reason to register any of them.

| Callback | You need it only if |
| --- | --- |
| `address_update` | Totals depend on the destination — shipping cost or tax that varies by address |
| `shipping_option_update` | The customer picks a shipping option inside the checkout iframe, rather than on a page of yours |
| `validation` | Stock, an expiring price or a fraud check can invalidate the order between creation and purchase |

[Server-side callbacks](/checkout/callbacks/) owns how `address_update` and
`shipping_option_update` work — what each one sends and what a valid response looks like — so
register them there once you've decided you need them. The rest of this page is about the third
one, because deciding to skip it is not the same as opting out of its effect.

## Fail open, or fail closed?

Validation must answer within ten seconds. Past that, Kustom treats the order as valid and
redirects the customer to confirmation anyway — so you're fail open by default, whether or not
you chose it. For most catalogs that default is the right one: a customer who already paid
shouldn't be blocked because your server had a slow ten seconds.

Set `options.require_validate_callback_success: true` on the order when that trade is reversed —
an item that can oversell, a price that expires, a check required by law — where letting the
wrong purchase through costs more than blocking a good one. The flag turns a timeout or any
response other than a `2xx` or a redirect into a block instead of a pass. Choose it deliberately, once your endpoint is
reliable, rather than as the default you get by not deciding.

## Answer the validation callback

Register `merchant_urls.validation` and Kustom `POST`s the full order — line items, addresses,
amounts — to it while the order's status is still `checkout_incomplete`. This is your last look
before the purchase completes.

```json
{
  "merchant_urls": {
    "validation": "https://shop.example/api/kustom/validate"
  }
}
```

Reply within the ten-second window:

| Your response | What happens |
| --- | --- |
| `200` | The purchase proceeds |
| `303` with a `Location` header | Kustom sends the customer to that URL instead of confirmation — this is the rejection message the customer actually sees, so make the page explain why: out of stock, price changed, no longer available |
| Anything else, or no reply in time | Fail open (the default): treated the same as `200`. Fail closed (`require_validate_callback_success: true`): the customer gets a generic error and the purchase stops |

## When it breaks

| Failure | What happens |
| --- | --- |
| The handler takes longer than ten seconds | Fail open lets the purchase through. Fail closed shows the customer a generic error |
| The handler reads or updates the order through the API instead of reading the `POST` body | Your own call can race the response you're about to send — treat the callback body as the order, don't refetch it |
| A response that is neither a `2xx` nor a `3xx` with `Location` | Under fail open — the default — it is treated as an approval and the purchase completes, whatever the status was. It stops the purchase only with `require_validate_callback_success: true`. The exception either way is a `400` carrying `error_type: address_error` or `approval_failed`, which denies and shows the customer your `error_text` |
| The customer authenticates with 3D Secure before your answer lands | Your `200` doesn't by itself mean the purchase completed. Deny after that authentication and Kustom cancels the payment — refunding it if it had already been captured — so the reservation is released and you see no order |

None of these fail loudly on your side — the customer sees a generic error page or an order that
went through, and the reason lives in whichever response you actually sent.

## Prove it

These docs answer `200` to every validation call they receive, which is why a purchase made in
this Playground session always completes. To see the other branch on your own integration, point
`merchant_urls.validation` at an endpoint that returns `500`, or one that simply never answers,
then complete a purchase against it. The request Kustom made and the status code you returned
appear in this session's console exactly as they would in your own Developer Logs — the fastest
way to confirm which side of fail open your endpoint actually lands on before a real customer
finds out for you.

What you're left with, once you've answered — or knowingly skipped — validation, is a purchase
Kustom is allowed to complete. [Confirmation & push](/checkout/integrate/confirmation-and-push/)
is where that completion actually reaches you: the redirect, the push and reading the order back
to find out what happened.

## Related

- [Server-side callbacks](/checkout/callbacks/) — every callback's payload, response shape and
  the push retry schedule.
- [Merchant URLs explained](/checkout/integrate/merchant-urls/) — where `validation` and the
  other callback URLs are registered.
- [Confirmation & push](/checkout/integrate/confirmation-and-push/) — what happens once the
  purchase Kustom allowed actually completes.
