Holds and captures
Take the money now, charge it when the order ships — and release it at no cost if it never does.
A hold takes the buyer's money out of their wallet without giving it to you. It sits in a contract until you charge it — when the thing ships — or release it, which costs you nothing and needs no refund.
If you sell downloads, you do not want this. If you ship goods, make to order, or take pre-orders, it is the difference between cancelling an order and owing somebody a refund.
What changes
| Instant (default) | A hold |
|---|---|
| Money reaches you when the buyer pays | Money sits in a contract until you capture |
| Cancelling means sending a refund from your wallet, within your deadline | Cancelling is a void: nothing to send, nothing owed |
| Any currency you accept | Only the currency you settle in |
| One transaction | Two — the hold, then the charge |
payment.settled | payment.authorized, then payment.captured |
The flow
// 1. Create the payment as a hold rather than a charge.
const payment = await call('POST', '/v1/payments', {
amount: '249.00',
currency: 'USD',
capture_method: 'manual', // hold it; charge on despatch
description: 'Oak dining table',
});
// 2. The buyer pays as usual. You get payment.authorized, not payment.settled:
// the money is in the escrow contract, not in your wallet.
// 3. It shipped. Charge it.
await call('POST', `/v1/payments/${payment.id}/capture`, {});
// 3b. Or only two of the three items arrived in stock.
await call('POST', `/v1/payments/${payment.id}/capture`, { amount: '166.00' });
// 3c. Or it is out of stock. Release it — this costs you nothing to send.
await call('POST', `/v1/payments/${payment.id}/void`, {});
# 1. Create the payment as a hold rather than a charge.
payment = call("POST", "/v1/payments", {
"amount": "249.00",
"currency": "USD",
"capture_method": "manual", # hold it; charge on despatch
"description": "Oak dining table",
})
# 2. The buyer pays as usual. You get payment.authorized, not payment.settled:
# the money is in the escrow contract, not in your wallet.
# 3. It shipped. Charge it.
call("POST", f"/v1/payments/{payment['id']}/capture")
# 3b. Or only two of the three items arrived in stock.
call("POST", f"/v1/payments/{payment['id']}/capture", {"amount": "166.00"})
# 3c. Or it is out of stock. Release it — this costs you nothing to send.
call("POST", f"/v1/payments/{payment['id']}/void")
Charging part of it
A partial capture charges what you shipped and releases the rest. Two of three items in stock: capture two thirds, and the buyer keeps the difference without asking for it. Tax is reversed in proportion, at the rate that applied when the payment was taken.
The payment stays authorized while anything is still held, so you
can capture again as the rest of the order ships.
The window
Seven days by default — the same as a card authorization, which is where the
expectation comes from. Stripe holds an online authorization for seven days
and offers extended ones up to thirty; PayPal keeps one valid for
twenty-nine. You can set yours anywhere up to the platform maximum, in
Settings → How you take money, or per payment with
hold_window_hours.
The window is fixed onto the payment when it is created, so changing your setting never shortens a hold a buyer has already agreed to.
When it lapses the buyer takes their money back and you have not been paid, so
if you ship slower than your own window, lengthen it. You get
payment.authorization_expiring a day before a hold lapses, and
payment.authorization_expired if it does.
An expired hold is not a payment you can still collect.
Once the window closes the buyer may reclaim the funds from the contract
themselves, without us and without you. We do it for them as a courtesy
rather than leaving them to call a contract function. Capturing after expiry
returns authorization_expired.
Why only your settlement currency
A hold has to hand back exactly what the buyer put in. Converting on the way in would mean returning a different amount than they sent, because the rate moved while you were deciding. So a held payment is offered only in the currency you settle in, and the checkout shows that currency alone.
This is why holds go with stablecoins in practice. A merchant settling in ETH and accepting only ETH can hold funds perfectly well; a merchant who wants buyers to pay in anything cannot hold at all.
What we can and cannot do with the money
The constraint is in the contract, not in our good behaviour. While funds are held we can send them to you, or back to the buyer. There is no third instruction — not to us, not to anyone. After the window closes we cannot even do that: only the buyer's own reclaim works.
Everything is confirmed by the chain
capture and void return 202. They
submit a transaction and tell you it is on its way; the payment moves when
the contract's event confirms it. We never report a capture we have not seen
land.
While one is in flight, a second is refused with
escrow_operation_in_flight — that is what stops a retry charging
the buyer twice. If the transaction fails, you get
payment.escrow_operation_failed, nothing has moved, and you can
try again.
If a capture is undone by a reorg
Rare, and real. A capture is a transaction like any other, and a chain
reorganisation can take it out of the canonical history. The money is then
back in the escrow contract, and the contract knows exactly how much — so the
payment is resynced from it rather than from our own arithmetic, and returns
to authorized with whatever is genuinely still held.
You get payment.reorged. If you had already shipped on a
payment.captured, this is the case that costs you — which is why
completed, not captured, is the signal to ship on.
A payment completes only once every one of its captures is deep enough.
States
| Status | Meaning |
|---|---|
authorized | The money is held. Not yours yet. |
settled | Captured, in full or in part. The captured amount is yours. |
completed | Confirmed to the required depth. Safe to ship. |
voided | Released untouched. The buyer has it all back. |
expired | The window closed unclaimed and the buyer took it back. |
A void after a partial capture is not a voided payment: some of it was
charged, and that money is yours. Those become settled, with
amount_captured telling you how much.
What a cancellation costs you
Releasing a hold is free to the buyer and costs you one network fee — the transaction that sends their money back. The same is true of a hold you let expire, and of a deposit returned to a buyer. These are the only transactions with no payment to take the fee from, so they are recorded against you and appear on your statement.
Below a threshold they are not collected at all: on a cheap chain they are
fractions of a cent and chasing them would cost more than the amount. Above
it you get account.network_fees_owed once, and the amount comes
off a future settlement.
Turning it on
In the panel, under Settings → How you take money. It sets the
default for new payments; capture_method on an individual payment
overrides it either way, so you can hold the furniture and charge instantly
for the delivery slot.