On-chain payments don't reverse themselves
A crypto payment is final the moment it confirms. That finality is the reason merchants like settling on-chain — no fraud reserves, no months-long dispute windows, no acquirer clawing funds back weeks after the goods shipped. But finality cuts both ways. When a customer needs their money back, there is no protocol-level undo. You have to send a new transaction to a destination you trust, for an amount you can justify, and you have to be able to explain later why it left your balance.
Teams that handle this by hand learn the failure modes quickly. Someone copies a wallet address from a support email and fat-fingers a digit. Two operators both process the same refund request and the customer is paid twice. A refund goes out with no link back to the order it relates to, and at month-end finance is staring at an outbound transfer nobody can map to a sale. None of these are exotic — they are the ordinary cost of moving money manually under time pressure.
halfin treats a refund as a first-class operation rather than an ad-hoc payout. A refund is created against the invoice the customer already paid, so the system already knows who paid, how much, in which asset, and on which network. That context is what turns a risky free-form transfer into a controlled, traceable action.
A refund is bound to the invoice it came from
The defining property of a halfin refund is its anchor: it references a completed invoice. That single link is what separates a refund from a generic outbound payout. Because the original invoice carries the paid amount, the settlement asset, the payer context, and the network it arrived on, a refund inherits that context instead of asking an operator to re-enter it from memory.
Practically, this means the refund amount is reasoned about relative to what was actually paid on that invoice — you are returning value against a known, confirmed payment, not inventing a transfer out of thin air. A refund can return the whole paid amount or part of it, which covers the everyday cases: a full cancellation, a partial credit for a missing item, or returning the surplus when a customer overpaid. Network fees and on-chain rates still apply to the outbound transaction, so the value the payer receives reflects the cost of actually sending it back across the chain.
Binding the refund to the invoice also fixes the reconciliation problem before it starts. Finance does not see a mysterious outbound transfer; they see a refund that points at invoice X, which they can open, read, and tie to the original sale. The audit story is complete from the order to the return.
| Scenario | What the customer experienced | Refund against the invoice |
|---|---|---|
| Order cancelled | Paid in full, then cancelled before fulfilment | Return the full paid amount to the payer |
| Overpaid invoice | Sent more than the invoice asked for | Return the surplus over the invoiced amount |
| Partial credit | Paid in full, one line item unavailable | Return part of the paid amount |
| Wrong product | Paid for the wrong item, re-ordered the right one | Return against the original invoice, bill the new one separately |
Operator approval is the control, not an afterthought
Sending money out of a balance is the most sensitive action a payment system performs, and refunds are no exception. halfin routes refunds through operator control: a refund is something a person with the right permission initiates and approves, not something that fires automatically off a customer request. That deliberate human gate is the point — it is where someone confirms the request is legitimate, the amount is right, and the destination is the payer you expect.
This pairs with halfin's scoped API keys and permissions model. The ability to move funds — payouts and refunds alike — sits behind scoped permissions, so a read-only reporting key cannot trigger a refund, and the key your support tooling uses can be limited to exactly what it needs. Who can approve a refund is a policy you set, backed by the same permission system that governs the rest of your account.
Every refund lands in the operation log with its actor, its amount, its target, and its link to the source invoice. When a refund is questioned weeks later — by your own finance team, by an auditor, by a partner — the record answers the obvious questions without anyone reconstructing the event from chat history.
- A refund is initiated and approved by an operator, never auto-fired from a raw customer request.
- Permission to move funds is scoped — reporting keys and integration keys carry only the access you grant.
- Each refund is logged with actor, amount, destination, and the invoice it refunds.
- The destination is reasoned about against the paid invoice, not pasted blind from an email thread.
Where refunds sit next to payouts and conversion
A refund is a specific kind of outbound money movement, and it shares plumbing with the rest of halfin's pay side. If you already run single payouts to partners or batch payouts to a list of recipients, a refund is the same class of action with a tighter contract: it must point at a paid invoice and return value against it. Understanding that relationship helps you decide which primitive fits a given task.
Use a refund when you are returning money to someone who paid you on a specific invoice. Use a single payout when you are paying a destination that has no originating invoice — a supplier settlement, an affiliate commission, a withdrawal. Use a mass payout when you are fanning many such payments out at once. Keeping these distinct keeps your books honest: refunds reduce the revenue tied to a sale, while payouts are operating outflows that were never customer revenue to begin with.
Settlement asset matters too. If your customer paid in one asset but your balance has been moved into another through balance conversion or treasury rebalancing, the refund still reasons from the original invoice — the asset and network the payer used to pay are part of that invoice's record, which is exactly the context a refund needs to send value back the way it came.
| Primitive | Anchored to an invoice? | Typical use |
|---|---|---|
| Refund | Yes — a paid invoice | Return money to a customer who paid you |
| Single payout | No | Pay one supplier, affiliate, or withdrawal |
| Mass payout | No | Fan out many payouts in one idempotent batch |
Read refund state through the API and webhooks
Refunds are part of the same spec-first REST surface as the rest of halfin. You create and read them through the API, and you find out when a refund's state changes through the same HMAC-signed webhook envelope that carries your invoice and payout events. That means your back office does not poll and guess — it reacts to a signed event and updates the order record.
As with every halfin webhook, verify the signature before you act on the payload. A refund event is an instruction to update your own ledger or notify a customer, so treat it the way you treat a payment confirmation: confirm it is genuinely from halfin, then take the business action. The code below sketches reading a refund's current state for a known refund identifier; consult the docs for the exact request and response shapes for your account.
# Read the current state of a refund by id.
# Use a key scoped to read refunds; replace the id with your own.
curl https://api.thehalfin.com/api/v1/refunds/00000000-0000-0000-0000-000000000000 \
-H "X-API-Key: $HALFIN_API_KEY"
# A refund references the invoice it returns value against, so the
# response ties back to the original payment your customer made.
# On any state change, halfin also delivers a signed webhook event —
# verify the HMAC signature before you update your order or ledger.Refunds finance can actually close the month on
The real test of a refund feature is not whether money can leave the balance — it is whether your finance team can close the books without a forensic exercise. Because each halfin refund references the invoice it came from, the trail runs cleanly from the sale, to the payment that settled it, to the refund that returned part or all of it. There is no orphaned outbound transfer to explain.
That continuity is what makes refunds safe to delegate. A support lead can be granted the permission to process refunds within policy, knowing every action is approved, logged, and attributable. A reconciliation analyst can match returns to sales without chasing screenshots. And when an auditor or partner asks why funds moved, the answer is one record away, not a reconstruction.
Refunds round out halfin's money-movement story: you accept payment through invoicing and checkout, you settle and hold across supported chains and assets, you pay out to partners and contractors, and when value needs to go back to a customer, you return it against the invoice that started it — under the same approval and audit discipline as everything else.