The withdrawal is approved in your system, not ours
A trader withdrawal is a decision your back office makes. The risk desk confirms the balance is real, the profit-split rules are applied, KYC is current, and the wallet belongs to the trader on file. halfin does not make that call and should not — it sits downstream of it. Once you have decided to pay, the payout is a mechanical step, and that is the step halfin owns.
Concretely: your CRM or trading platform stays the system of record for the trader's account, the balance, and the eligibility to withdraw. When the withdrawal is approved there, you create a halfin payout that names the asset, the amount, and the destination address. halfin executes the transfer against your balance and returns status — it never reaches back into your trading logic to decide who gets paid.
That boundary is also where the audit trail lives. Every payout carries the trader's instruction from your side and the on-chain result from halfin's side, so a finance review can walk from "the trader requested $1,000" to "this transaction settled to this address" without leaving the two systems guessing about each other.
- Your back office owns approval, balance truth, and the trader's verified wallet.
- halfin owns execution: it pays the named asset and amount to the named address.
- Each payout links your withdrawal record to a settled on-chain transaction for review.
A payout is described by a few fields
There is no special trader-withdrawal object to learn. A payout is a small, flat instruction: which asset, how much, where to, and a key that makes the request safe to retry. Amounts are strings end to end — never floating-point numbers — so a value the trader is owed transports exactly, with no binary-rounding drift between your books, halfin, and the chain.
The currency code names both the asset and the network it settles on, so a stablecoin that runs on several chains stays unambiguous. Pay a withdrawal in USDT on Tron (USDT_TRC20), USDC on Ethereum (USDC_ERC20), or a single-chain asset like SOL — the per-payout code means you pay each trader on the rail they hold value on instead of forcing one chain on everyone, and halfin validates the destination against that exact network before reserving funds.
| Field | What it is | Note |
|---|---|---|
| currency | The asset to pay — a crypto code such as USDT_TRC20, USDC_ERC20, or SOL. | The code pins the network, so a multi-chain stablecoin is unambiguous. |
| amount | The payout amount, as a string. | Strings only — never a JS number; no rounding drift. |
| destination | The trader's verified wallet address. | Sourced from your KYC record, not from the payout request. |
| idempotency_key | A caller-supplied key derived from your own ledger. | Re-sending the same key returns the original payout, never a duplicate. |
Idempotency is what makes a retry safe
The failure that costs a broker real money on a payout is the double-send: a request times out, a worker restarts, someone re-runs the file, and a trader is paid twice. halfin closes that with a caller-supplied idempotency key on every payout. Derive the key from your own records — the trader's account id joined with the withdrawal id, say — so the same withdrawal always produces the same key.
Because the key is deterministic, a retry is a no-op when it needs to be. If you submit a payout and never see the response, you submit it again with the same key; halfin recognizes the key and returns the original payout instead of creating a second one. The trader is paid exactly once, and your worker can be as crash-happy as it likes.
This is the same guarantee whether you pay one trader or a whole queue. A single withdrawal is one payout with one key. A payout day is a fan-out over the same single-payout API, one key per line, and the whole run is safe to re-execute from the top — already-created payouts come back unchanged, and only the missing ones are created. There is no separate batch endpoint and no batch state to reason about.
Single payout or a payout run?
Both are the same primitive at different volumes, and the operating model is identical: stage from your ledger, approve, reconcile per recipient. Reach for a single payout when a trader withdraws on their own schedule and you want a clean one-off. Reach for the mass-payout fan-out when withdrawals, profit splits, or IB commissions stack up into a run you'd otherwise process by hand.
| Single payout | Mass payout (fan-out) | |
|---|---|---|
| Shape | One payout, one idempotency key. | Many single payouts submitted together, one key per line. |
| When | An individual trader withdrawal as it's approved. | A batch of withdrawals, profit splits, or IB commissions. |
| Failure handling | Succeeds or is rejected up front; you fix and re-send. | Per-line — a bad row is reported alone; the rest proceed. |
| Reconciliation | One settled transaction against one withdrawal record. | One verdict per recipient against your ledger. |
Approval is a control point, not an afterthought
Submitting a payout does not move funds. Each payout enters a pending-approval state and is released from the dashboard before anything leaves your balance. That separates the high-throughput programmatic path — your integration staging payouts unattended — from the human control point where a treasurer signs off. The batch you POST is a proposal, not an irreversible action, which is exactly the property you want standing between an automated trader-withdrawal queue and your treasury.
Settlement is reorg-aware and respects per-chain confirmation thresholds, and the completion of a payout is reported through a signed webhook. halfin emits a payout.completed event when the transfer has settled to the depth the chain requires — not merely been broadcast. Verify the HMAC signature before you act on it, then mark the withdrawal paid in your own ledger and tell the trader. Treat that verified event as the source of truth, not the dashboard glance or the broadcast hash.
- POST stages a payout in pending-approval; funds move only when it's released from the dashboard.
- Automated staging and human sign-off stay separate, so nothing leaves the balance unattended.
- Mark the withdrawal paid on the verified payout.completed webhook, not on broadcast.
- Settlement is reorg-aware at the chain's confirmation depth, so "completed" means settled.
Paying a withdrawal from code
A trader withdrawal is one call to the single-payout API. You authenticate with a scoped API key, name the asset, the amount as a string, the trader's verified destination address, and an idempotency key derived from your own records. A payout day is the same call in a loop — derive each key deterministically and the loop is safe to interrupt and re-run.
The example below pays a single approved withdrawal. The payout lands in pending-approval; a treasurer releases it from the dashboard, and your backend learns it settled from the verified payout.completed webhook. The exact request and response shapes are defined in the docs and the @halfin/sdk-merchant types — the point here is that one authenticated call, plus a deterministic key, turns an approved withdrawal into a payout you can reconcile.
curl -sS -X POST https://api.thehalfin.com/api/v1/payouts \
-H "X-API-Key: $HALFIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"currency": "USDT_TRC20",
"amount": "1000.00",
"destination": "TXxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"idempotency_key": "withdrawal-8842-trader-00000000-0000-4000-8000-000000000001"
}'
# The payout enters pending-approval and is released from the dashboard.
# Mark the withdrawal paid on the verified payout.completed webhook —
# verify the HMAC signature first. See docs.thehalfin.com for the full schema.Where trader payouts sit in the FX cluster
Payouts are the exit side of the broker money flow, and they share a model with everything around them. The same single-payout primitive that pays one trader withdrawal is the unit a mass-payout fan-out repeats across a whole run — IB commissions, rebates, and affiliate splits ride the identical idempotency-keyed loop. If your payouts arrive in a mix of stablecoins and chains, you consolidate the balance you pay from with balance conversion rather than juggling assets by hand.
On the inbound side, the trader deposits that funded these accounts run through halfin invoicing and hosted checkout, credited reorg-aware on the invoice.paid event. Withdrawals close the loop the same way in reverse. The FX brokerage hub ties the deposit, withdrawal, and affiliate-payout flows together so finance reconciles one platform's records instead of three disconnected tools.