Short-lived capacity coordination between POST /orders/preview and POST /orders. Use holds to reduce preview→payment races without enabling hot-drop mode.
Checkout hold states#
Holds do not pull keys. Authority remains the order commit path.
When to use#
- POST /api/seller/v1/orders/preview — gate on
canFulfill(do not charge if false). - Optional:
POST /api/seller/v1/orders/hold— reserve a slot for the checkout window (default 30 minutes, matching a hosted PSP page). SendcountryCodeandintegrationOrderContext(including buyer IP) so territory and consumer-geo gates fail before payment — sameCONSUMER_*/TERRITORY_*codes as preview. - Charge the buyer at your PSP.
- POST /api/seller/v1/orders with the same SKU, quantity,
orderReference, and optionalcheckoutHoldId.
Capability discovery#
curl -s -X POST "https://api.avrix.io/api/seller/v1/orders/hold" \
-H "Authorization: Bearer $AVRIX_API_KEY" \
-H "Idempotency-Key: hold-store-10432" \
-H "Content-Type: application/json" \
-d '{
"skuCode": "SANDBOX-ALWAYS-001",
"quantity": 1,
"countryCode": "NL",
"orderReference": "store-order-10432",
"integrationOrderContext": {
"schemaVersion": 1,
"salesCountryCode": "NL",
"currencyCode": "EUR",
"salesPriceGrossMinor": 7999,
"priceIncludesTax": true,
"salesTaxRatePercent": 21,
"salesChannel": "web",
"salesTaxAmountMinor": 1388
}
}'curl -s -X POST "https://api.avrix.io/api/seller/v1/orders/hold"
-H "Authorization: Bearer avrix_sk_sbx_your_key_here"
-H "Content-Type: application/json"
-H "Idempotency-Key: hold-store-order-10432"
-d '{
"skuCode": "SANDBOX-ALWAYS-001",
"quantity": 1,
"orderReference": "store-order-10432"
}'
Successful response (`data.type`: `checkout_hold`):
| Field | Meaning |
| --- | --- |
| `holdId` | Pass as `checkoutHoldId` on `POST /orders` |
| `expiresAt` / `expiresInSeconds` | TTL remaining |
| `allocationId` | Bound allocation |
| `maxFulfillableQuantity` | Ceiling observed at hold time |
Common errors: **409** sold out, **404** SKU/allocation, **412** hold disabled.
### `DELETE /api/seller/v1/orders/hold`
**Requires** `Idempotency-Key`.
Release on cart abandon:
```bash
curl -s -X DELETE "https://api.avrix.io/api/seller/v1/orders/hold" \
-H "Authorization: Bearer avrix_sk_sbx_your_key_here" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: release-hold-store-order-10432" \
-d '{ "holdId": "<uuid>" }'
Bind on create order#
Optional checkoutHoldId on POST /orders must match seller, SKU, quantity, and orderReference. The hold is consumed on successful pull.
TTL#
Default hold lifetime is 30 minutes (platform-clamped window, one minute to one hour), sized to a hosted PSP checkout. Expired holds release automatically; prefer explicit DELETE on abandon so other shoppers see capacity sooner.
A lapsed hold never blocks a paid order. If POST /orders references a checkoutHoldId that has expired, been released, or been consumed, the commit proceeds as a plain order (inventory from the pool, price from priceCommitmentToken / expectedUnitPriceCents) and the response carries checkoutHold: { holdId, status: "expired", committedWithoutHold: true }. Only a hold that exists but does not match the body is rejected (422 HOLD_MISMATCH, recommended action fallback_to_post_orders_without_hold).
Revocation#
If the publisher revokes or pauses the allocation grant while holds are open, the platform
cancels those holds immediately (allocation.updated arrives with reason: "revoked" or
"paused"). A POST /orders carrying a cancelled checkoutHoldId fails hold validation — treat
it like an expired hold: re-preview, and if the SKU is no longer sellable, refund the buyer at
your PSP.
vs hot-drop#
| Checkout hold | Hot-drop reserve | |
|---|---|---|
| Requires hot-drop mode | No | Yes |
| Commit path | POST /orders | POST /orders/commit |
| Typical TTL | Minutes | About 60 seconds |
| When to use | Ordinary checkout races | Declared launches above ~500 RPS |