The commission run is a money-movement problem, not a reporting one
Your CRM already knows what each introducing broker earned. It tracks lots traded, the rebate per lot, the affiliate's CPA tier, the clawbacks for refunded deposits. By the time the period closes, the hard accounting is done — the number next to each partner is correct. What is left is the part nobody enjoys: turning two hundred correct numbers into two hundred crypto transfers that each land on the right wallet, on the right network, with a record finance can reconcile against the statement you already sent the partner.
That last mile is where IB and affiliate programs lose time and trust. A partner who was promised settlement on the first of the month notices a one-day slip. A wallet typo sends a USDT payment to an address that can never receive it. A transfer fired on the wrong network arrives as an asset the partner's exchange won't credit. None of these are accounting errors — they are payout-execution errors, and they happen under deadline pressure precisely when the manual process is most fragile.
halfin's job starts where your commission calculation ends. You keep the IB hierarchy, the rebate logic, and the partner statements in your own systems; halfin takes the resolved list and executes it as crypto payouts with the operational guarantees the manual version never had: per-line idempotency, per-line failure reporting, and a confirmation-aware status that means settled rather than merely sent.
One batch, one idempotency key per partner, one verdict
A mass payout in halfin is a fan-out over the single-payout API — POST /api/v1/payouts, once per partner line — not a separate batch endpoint you have to learn. Each line carries a currency, an amount as a string, a destination address, and its own idempotency_key. The key is the whole point of the design: derive it deterministically from your own ledger, typically the IB's account id joined with the payout period, so the same partner in the same run always produces the same key.
That determinism is what makes the run safe to interrupt. If your worker dies on partner 240 of 600 — a dropped connection, a rate limit, a restart — you do not have to figure out who already got paid. You re-run the entire list. The 239 keys that already created a payout come back unchanged, the network calls that timed out ambiguously resolve to the original payout instead of a second one, and only the missing partners are created. The batch becomes safe to retry from the top, which is exactly the property a commission run needs and a hand-rolled loop never has.
Partial failure is treated as normal, not exceptional. A malformed partner wallet, an amount below a chain's dust threshold, or a currency the destination can't receive rejects that one line and is reported against it — the rest of the run still goes through. You fix the flagged rows and re-submit the whole file: the lines that already succeeded are no-ops, only the corrected partners execute. A single bad IB row never blocks the other partners from getting paid on time.
What a partner payout line carries
Every IB, affiliate, or rebate line in the run is described by the same small set of fields. Amounts are always strings, never floating-point numbers, so a commission like a USDT rebate is transported exactly — no binary-rounding drift between the statement you sent the partner and the value that leaves your balance. Currency and network are chosen per line, because a partner program rarely shares one preference: settle an Asia-based IB in USDT on Tron, a US-facing affiliate in USDC on Ethereum or Base, and a creator-style partner in native SOL, all in one submission.
| Field | Type | What it means for a partner run |
|---|---|---|
| currency | string | Asset + network the partner nominated, e.g. USDT (TRC-20 / ERC-20 / Solana) or USDC (ERC-20 / Solana / Base). |
| amount | string | The commission from your statement, as a decimal string — never a JS number, so it matches the partner's books exactly. |
| destination | string | The partner's nominated wallet; validated for the chosen network before the payout is accepted. |
| idempotency_key | string | Deterministic per partner + period, e.g. ib account id joined with the payout cycle. Re-submitting returns the original payout. |
Submitting a commission run from your CRM
Because there is no batch endpoint, the integration is a loop over your resolved commission list. Pull the partner rows your CRM already produces, and submit each one as a single payout with a key derived from the partner and the cycle. Wrapping the whole loop in a retry is correct rather than dangerous: each call is idempotent, so a second pass converges on exactly one payout per partner instead of doubling the ones that already went through.
The example walks a commission file and submits each IB line with its own key. Re-run the same file after a crash and the keys collide — nobody is paid twice. Note that the call only stages the payout: it does not move funds yet.
# Pay an IB / affiliate commission run; each line is idempotent.
# Re-run the same file after a crash — keys collide, nobody is paid twice.
while IFS=, read -r ib_account currency amount destination; do
curl -sS -X POST https://api.thehalfin.com/api/v1/payouts \
-H "Content-Type: application/json" \
-H "X-API-Key: $HALFIN_API_KEY" \
-d "{
\"currency\": \"$currency\",
\"amount\": \"$amount\",
\"destination\": \"$destination\",
\"idempotency_key\": \"ib-2026-06-$ib_account\"
}"
done < commissions.csv
# See docs.thehalfin.com for the full payout request and response schema.Staging is programmatic, the release is human
A commission run touching the balance unattended is the thing a broker's finance lead worries about most, so halfin keeps the two halves apart. Submitting a payout does not move money — each one enters a pending-approval state and is released from the dashboard before any funds leave the balance. The batch you POST from your CRM is a proposal, not an irreversible action.
For an IB program that maps cleanly onto how the work is already split. Your integration stages the entire partner list from the resolved commission report — hundreds of lines, mixed assets and networks, every line idempotent. A treasurer then reviews the staged run against the period's statement total and approves once. The high-throughput programmatic path and the human control point stay separate, so you get unattended staging without unattended spending.
- Stage the full partner list programmatically from the commission report your CRM already produces.
- Every line idempotent — re-run the staging step after any failure without risk of a duplicate payout.
- Payouts wait in pending-approval; a treasurer releases the run from the dashboard before funds move.
- Per-line status and signed payout.completed webhooks give finance a clean per-partner result to reconcile.
Pay each partner on the network they actually use
An IB in one region settles in USDT on Tron because that is what their local exchange credits cheaply; a US-facing affiliate needs USDC on Ethereum for their own accounting; a newer partner prefers Solana for speed. Forcing the whole program onto one network produces rejected payments and addresses that can't receive the asset you sent. halfin lets each commission line pick its own currency and network, so a single run spans all of them without a separate process per chain.
Settlement is reorg-aware and uses per-chain confirmation thresholds, so a partner payout reported as complete has actually settled to the depth that chain requires — not just been broadcast. That matters most at the tail of a large run, where you want each partner's status to mean done, not submitted and hopefully fine. When a payout reaches that state, a signed payout.completed webhook lets your back office mark the commission settled against the original statement; verify the HMAC signature before you act on the event.
- Stablecoins: USDT on TRC-20 / ERC-20 / Solana; USDC on ERC-20 / Solana / Base.
- Native assets: BTC, ETH, SOL, XRP, plus EVM L2 and BSC native tokens.
- Per-line network choice — mix Tron, Ethereum, Base, and Solana partner payments in one run.
- payout.completed fires only after per-chain confirmation; reconcile the partner's commission on that event.
Where IB payouts sit in the broker's stack
IB and affiliate payouts are one corner of the broker's money flow, and they reuse the same primitive as the rest of it. The mass-payout guarantees described here are identical to the ones a broker uses for funded-account and trader withdrawals — the difference is only who is on the list and where the amounts come from. The mass payouts product page covers the full mechanics for teams that want the primitive in isolation.
The compliance boundary is the same as everywhere else in the broker's setup: halfin executes the payment rail, the broker owns who the partner is. Keep the IB agreement, the partner's identity, and any sanctions or wallet-risk screening inside your own controls; halfin receives the approved instruction, validates the address for the chosen network, processes the rail, and returns status. Affiliate payouts beyond FX — networks, CPA programs, and creator partners — share this exact pattern, so a team running a broader partner program can reason about all of it the same way.