Refunds
We never held the money, so a refund is a transfer you make and we verify — with a deadline, and one transfer per refund.
This is the one place where not holding your money costs you work rather than saving it. We never received the payment, so we cannot send it back. You do, and we verify it.
A refund is a transfer you make.
There is no balance of yours here to debit. A refund is recorded as an obligation with a deadline; you pay it from your own wallet, and we confirm the transfer on chain before marking it complete. Nobody can mark a refund done without a transaction that actually reached the buyer.
The flow
Each step as an expandable operation is on the API reference.
// 1. Record what is owed. This does not move money.
const refund = await call('POST', '/v1/refunds', {
payment_id: 'pay_3nQ8xK2mLp4R',
amount: '20.00', // omit for the full amount
reason: 'Customer returned one ticket',
});
// 2. Build the transaction. We hand back exactly what to send.
const prepared = await call('POST', `/v1/refunds/${refund.id}/prepare`, {});
// 3. Send it from your own wallet. We are not in this step.
const sent = await wallet.sendTransaction(prepared.transaction);
// 4. Tell us, and we verify it on chain before marking it done.
await call('POST', `/v1/refunds/${refund.id}/submit`, { tx_hash: sent.hash });
# 1. Record what is owed. This does not move money.
refund = call("POST", "/v1/refunds", {
"payment_id": "pay_3nQ8xK2mLp4R",
"amount": "20.00", # omit for the full amount
"reason": "Customer returned one ticket",
})
# 2. Build the transaction. We hand back exactly what to send.
prepared = call("POST", f"/v1/refunds/{refund['id']}/prepare")
# 3. Send it from your own wallet. We are not in this step.
tx_hash = wallet.send(prepared["transaction"])
# 4. Tell us, and we verify it on chain before marking it done.
call("POST", f"/v1/refunds/{refund['id']}/submit", {"tx_hash": tx_hash})
Where it goes
The buyer's stated refund address wins over the address they paid from, because a paying address is frequently an exchange withdrawal that cannot receive an arbitrary token — sending there loses the money.
A buyer sets that address at checkout by signing for it with the paying wallet, and it freezes once their transaction is out. You can override the destination on the refund if the buyer asks you to in writing.
One transfer, one refund
A refund claims the exact transfer that paid it — chain, transaction and log. Two refunds of the same size to the same address cannot both point at one transfer, so a single payment cannot close two obligations.
When you can refund
Once the payment is completed. A payment that is only
settled can still be taken back by a chain reorganisation, and a
refund you had already sent would not come back with it. Asking earlier returns
payment_not_final.
The transfer you submit must be newer than the payment, and one transfer
completes one refund. The payment becomes refunded once refunds
add up to what you received — the net after the fee, which is also the most you
can refund.
Deadlines
Each refund carries an sla_due_at. Passing it emits
refund.sla_breached and emails every owner, then again once a day
until the refund is sent. Nothing is
automatically deducted — there is nothing to deduct from — but an account with
a pattern of breaches is one we will ask about.
Partial refunds and tax
A partial refund reverses tax pro rata at the rate that applied when the payment was taken, not today's. It also reverses any discount proportionally, so a buyer cannot refund their way into a better price on what they keep.
States
| State | Meaning |
|---|---|
requested | Recorded and owed. Nothing has been sent. |
fulfilment_pending | You submitted a transaction; we are verifying it. |
completed | Verified on chain. The buyer has their money. |
requested again, with failure_reason | The transaction you submitted reverted, did not reach the destination, predates the payment, or never mined within a day. Still owed; send a new one. refund.fulfilment_failed tells you. |
cancelled | Withdrawn before it was paid. |
Refunds you do not have to fund
Two cheaper paths exist, and both avoid a transfer entirely:
- Before settlement. Cancel the payment. Nothing has moved, so there is nothing to send back.
-
Overpayment from a wallet. Anything above the required
amount is returned to the buyer inside the original transaction,
automatically. You never see it and never owe it. A deposit address is
different: it forwards everything it received, and
payment.overpaidnames the excess for you to return.