Where a poker cashout actually goes wrong
The transfer itself is trivial — name an asset, an amount, and an address, and the chain does the rest. What makes poker payouts dangerous is everything around that transfer. A cashout is irreversible the moment it confirms, so a fat-fingered amount or a stale wallet address is money you do not get back. Unlike a card refund, there is no issuer to call and no reversal button. The control has to live before the broadcast, not after.
The second failure mode is the silent double-pay. Network calls time out ambiguously: your worker fires the withdrawal, the connection drops before the response lands, and your code does not know whether the transfer happened. Retry it and you may pay the player twice; skip it and you may pay nobody. On a busy Saturday night with hundreds of cashouts queued, a naive retry loop is how a treasury quietly bleeds.
The third is accountability. A regulated operator — and the operator, not halfin, is the one that holds any gaming license — has to show who authorized each withdrawal, against which player balance, and where it went. A cashout that lives only in a chat thread and a block explorer screenshot is a control gap an auditor will flag and a chargeback-style dispute you cannot defend. The fix is to make every payout a tracked object with an approver, not a manual wallet action.
halfin closes all three by treating a cashout as a first-class payout: funds are reserved but not broadcast until an operator approves, every line carries a caller-supplied idempotency key so a retry is safe, and each transition is recorded against a stable payout ID your finance team can reconcile.
One cashout: a single payout with an approval gate
When a player requests a withdrawal, your platform owns the decision — KYC status, wagering and bonus clearing, collusion and bot checks — and halfin owns the execution. Once your back office approves the cashout, you create a single payout to the player's wallet: a currency, an amount as a string, and a destination address. halfin validates the address against the chosen chain and reserves the funds, but it does not broadcast anything yet. The payout enters a pending-approval state — recorded, visible in the dashboard, and inert.
That pending state is the control point. An operator with payout permissions reviews the destination and the amount and releases it; the person who drafts a cashout need not be the person who authorizes it. Only after approval does halfin sign and submit the transaction. From there it follows the chain: crediting waits for the per-chain confirmation threshold and is reorg-aware, so a transfer dropped in a short reorg never leaves you with a false completed state.
Because there is no card issuer in the loop, an approved cashout goes out as fast as the network confirms it — high approval rates here are structural, not a setting. The player withdraws to a wallet on a chain you already settle, often the same one they deposited from. Your ledger does not move on the redirect or the dashboard view; it moves on the signed payout.completed webhook, which you verify before crediting the player's withdrawal as done.
- Your platform approves the cashout — KYC, wagering, and fraud checks are your decision.
- Create a single payout — currency, amount, destination; halfin validates the address and reserves funds.
- Operator approval — a human releases it before anything is broadcast on-chain.
- Reorg-aware settlement at the per-chain confirmation threshold, not just a broadcast.
- payout.completed webhook — verify the HMAC signature, then mark the cashout settled in your ledger.
A final table: a batch of idempotent payouts
Tournaments invert the shape of the problem. Instead of one player at a random moment, you have a fixed prize structure paying many players the instant the event ends — the final nine, the bubble, every min-cash. You know the payee list and the amounts ahead of time, and you want them all to go out together, once, even if your payout process restarts halfway through.
halfin handles this as a mass payout, which is not a separate endpoint to learn but a fan-out over the same single-payout API. Each prize line is one payout carrying its own deterministic idempotency key — derive it from your own records, like the tournament id joined with the player's seat or finishing position. Because the key is reproducible, re-submitting the whole prize list after a crash reproduces the same keys, and halfin returns the payouts it already created instead of paying anyone a second time. The run becomes safe to retry from the top.
Partial failure is the normal case, not an exception. A line with a malformed address, an amount below a chain's dust threshold, or an asset the player's wallet cannot receive is rejected per-line; the rest of the prize pool still pays out. You fix the rejected seats and re-submit the entire list — the lines that already succeeded are no-ops, and only the corrected ones execute. One bad wallet never blocks the other eight players at the table.
The same approval discipline applies: the batch you POST is a proposal, not an irreversible action. Your integration can stage the whole prize pool unattended, and an operator approves the run before funds leave the balance. One authorization for the run, not nine separate ones.
Single vs. batch: which shape fits the cashout
Both run on the same merchant balance, the same reorg-aware crediting, and the same signed webhook envelope, so moving a flow from one to the other does not re-plumb your reconciliation. The choice is about whether each transfer is its own decision or part of a run.
| Payout job | Shape | Why |
|---|---|---|
| Ring-game cashout | Single payout | One player, one ad-hoc decision; operator reviews the destination and amount before release. |
| Tournament prize pool | Mass payout (batch) | Fixed payee list and amounts paid together; one approval, idempotent keys absorb a mid-run restart. |
| Scheduled rakeback / rewards | Mass payout (batch) | Recurring run over many accounts on a cadence; deterministic keys make every retry exactly-once. |
| One-off correction or goodwill payment | Single payout | Discrete, individually authorized transfer with a clean record tied to one request. |
Pay each player on the chain they want
Poker players do not share a wallet preference, and forcing everyone onto one network means rejected payouts and addresses that cannot receive the asset you sent. Stablecoins are the workhorse for cashouts because the player gets a predictable amount that does not drift between the cashout decision and the confirmation — a USDT-on-Tron grinder and a USDC-on-Solana regular both get paid in the same run. Native assets cover the rest. halfin lets each payout line pick its own currency and network, so a single tournament batch can span all of them.
Each payout validates the destination address against the chosen chain at creation, so a TRC-20 address pasted into an Ethereum payout is caught before funds are reserved — not lost on-chain. Settlement uses each chain's confirmation threshold and is reorg-aware, so a per-player status of completed means the prize has actually settled to the depth that chain requires.
- Stablecoins: USDT on Tron (TRC-20), Ethereum (ERC-20), and Solana; USDC on Ethereum (ERC-20), Solana, and Base.
- Native assets: BTC, ETH and its L2s (Base, Arbitrum, Polygon), BNB on BSC, TRX, XRP, and SOL.
- Per-line network choice — mix Tron, Solana, Ethereum, and Base payouts in one prize-pool run.
- Address validation per chain at creation; reorg-aware crediting at the per-chain threshold before a payout reports complete.
Pay a final table from code
There is no batch endpoint to learn: a prize pool is a loop over the single-payout API where idempotency keys make the run safe to interrupt and re-run. Derive each key from your own records — here the tournament id joined with the player's finishing position — so the same player in the same event always maps to the same key. If the process dies mid-run, you re-run the entire loop: already-created payouts come back unchanged, and only the missing seats are paid. The full request and response schema lives at docs.thehalfin.com; the call below shows the headers and the four fields each line carries.
Each line comes back in a pending-approval state — funds reserved, nothing broadcast — until an operator releases the run from the dashboard. Then the payout.completed webhook for each line is what moves the player's withdrawal to settled in your ledger. Verify the HMAC signature on every event before acting on it.
# Pay a tournament prize pool; each line is idempotent, so re-running is safe.
# CSV columns: position,currency,amount,destination
while IFS=, read -r position 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\": \"tourney-2026-0814-seat-$position\"
}"
done < prize_pool.csv
# Re-run the same file after a crash: keys collide, nobody is paid twice.
# Each payout is created in pending_approval; an operator releases the run.The audit trail a regulated operator needs
A poker room answers to its own regulator, and the question that regulator asks about money leaving the platform is always the same: who authorized this, against which balance, and where did it go. halfin makes that answerable by recording every payout transition against a stable ID — who created the cashout, who approved it, when it executed, and the on-chain transaction it produced. A tournament prize and a ring-game cashout both reconcile back to the request that triggered them and forward to the transaction that settled them, with no chat-thread reconstruction in between.
Scoped API keys back this up. The integration that stages cashouts holds a payout-scoped key; a read-only key for your accounting export never carries the ability to draft a withdrawal. The operator approval step then sits on top, the human authorization that on-chain finality demands. The division of responsibility is clean and stays clean: the operator owns player identity, jurisdiction, responsible-gaming controls, and the decision to release a cashout; halfin receives the approved instruction, runs the rail, keeps the payment record, and returns signed status your compliance and finance teams reconcile.