If you know the dollar amount but not the coin, stop guessing the coin. Bill the dollars and let the payer answer the rest.
Most teams write their first invoice the hard way. They have a $240 order, they pick USDT on Tron because that's what the docs example used, and they hand the customer an address for an amount of USDT they computed against last week's rate. Then the customer pays in USDC on Base instead, or pays the right asset but the rate has drifted, and now someone is reconciling a few cents by hand. Multiply that by a few thousand orders and you have a job nobody wanted.
The fix is a fiat-anchored invoice in deferred mode. You state the price in fiat. The payer chooses the asset. The rate locks the moment they choose. This post is about when that's the right call and when it isn't.
Two ways to write the same invoice
halfin's invoicing API accepts an invoice in one of two shapes, and the shape you pick decides who makes the asset decision.
Fixed-asset. You send currency (a crypto asset, e.g. USDT_TRC20) and amount. You are saying: I want exactly this much of exactly this coin. The invoice is denominated in the asset from the first byte. There is no rate to lock because there is no conversion — the payer owes the asset you named.
Fiat-anchored, deferred. You send amount_fiat, fiat_currency, and deferred: true. You are saying: the price is $240; I don't care which supported coin pays it. No crypto currency and no crypto amount at creation. The invoice exists as a fiat figure with no asset attached yet.
{
"amount_fiat": "240.00",
"fiat_currency": "USD",
"deferred": true,
"description": "Order #10482",
"idempotency_key": "order-10482-v1"
}
That's the whole request. Two headers only — X-API-Key and Content-Type — and idempotency_key is a field in the body, not a header, so a retried create returns the same invoice instead of minting a second one. (More on that in idempotency.)
What "deferred" actually defers
The word is doing real work. A deferred invoice defers two decisions to the moment of activation, which is when the payer opens the checkout and selects how they want to pay:
- The asset. The payer is shown the rails you support and picks one — say USDC on Base. Only then does the invoice acquire a crypto asset and a crypto amount.
- The exchange rate. The $240 is converted into that asset at the rate that is current at selection time, and the rate lock starts counting down from there.
This is the part teams underestimate. With a fixed-asset invoice, your rate exposure starts when you create the invoice — which might be an email, a cron job, or a webhook ago. With a deferred invoice, the rate exposure starts when the human actually commits to paying. The gap between "we generated the invoice" and "the customer decided to pay" is exactly the window you no longer have to eat.
A fixed-asset invoice locks the rate when you act. A deferred invoice locks it when the payer acts. If those two moments are minutes or days apart, deferred is strictly less risk on your side.
When to reach for deferred
Use a fiat-anchored deferred invoice when the price is a fiat number and you don't care which supported asset settles it. Concretely:
- Checkout where the cart is priced in dollars or euros. Your catalog is fiat. The customer's wallet is whatever they brought. Don't force them onto the one coin you happened to hardcode — let them pay USDT on Tron or USDC on Solana and have both resolve to the same $240. The same logic behind USD-anchored cross-border billing applies here.
- Invoices with a shelf life. A quote you email today might be paid Thursday. You do not want Monday's rate frozen onto a Thursday payment. Defer the lock to when they pay.
- Multi-rail acceptance. If you support seven networks and want the payer to choose, deferred is the only shape that doesn't make you the one choosing. The asset menu is the product.
- Anywhere your accounting is in fiat. If your books, refunds, and reporting all think in USD, anchoring the invoice in USD keeps the whole chain consistent and leaves the crypto leg as an implementation detail.
When to use a fixed-asset invoice instead
Deferred is not the universal answer. Reach for fixed-asset when the asset itself is the obligation:
- You owe a specific coin. A treasury settlement, an on-chain obligation, a top-up to a specific custody account — the counterparty wants that asset, not its fiat equivalent. Naming
USDT_TRC20and anamountis the honest representation. - You priced in crypto to begin with. If your product is denominated in an asset (you sell something for "5 USDC, flat"), don't launder it through fiat and back. Bill the 5 USDC directly. The mechanics of why anchoring helps — and why it sometimes doesn't — are walked through in the FX anchor post.
- You must not present a choice. Some flows are single-rail by policy. A fixed-asset invoice removes the menu entirely.
The decision tree is short: Is the price a fiat number? If yes, and you accept more than one asset, deferred. Is the obligation a specific coin? If yes, fixed-asset. The rest is taste.
What both shapes share
Whichever you pick, the lifecycle after activation is identical, and that's the point — you are not signing up for a different state machine. Once an asset is attached and the rate is locked, the invoice runs the same path: it waits for a deposit, moves through invoice.confirming as confirmations accrue per chain, and lands on invoice.paid. Underpayment and overpayment are first-class — invoice.underpaid and invoice.overpaid fire with the real numbers rather than silently failing — and an unpaid invoice eventually emits invoice.expired. Crediting is reorg-aware, so a confirmation that later disappears is handled rather than trusted blindly.
You wire those webhooks once and they behave the same for fixed-asset and deferred invoices alike. The only difference deferred introduces is who filled in the asset and when the rate clock started — everything downstream is shared.
The mistake to avoid
The anti-pattern we see most: a team computes the crypto amount themselves, off their own rate feed, and sends a fixed-asset invoice for a fiat-priced order. Now they own a rate feed, a conversion, and a reconciliation step they didn't need — and every drift between their feed and the settlement rate becomes a manual cent-chase.
If your price is fiat, let the invoice be fiat. Anchor the amount, defer the asset, and let the rate lock land where the payer commits. You keep the dollar figure you actually care about, the payer keeps the coin they actually hold, and nobody reconciles a few cents at midnight.
L. Tanaka, halfin product team