The problem: a distributed bench paid through slow rails
The people who deliver a course rarely sit where the company is registered. A bootcamp records lessons with an instructor in one timezone, runs project reviews through teaching assistants across a handful of countries, holds office hours with mentors who freelance, and keeps the platform alive with contractors who could be anywhere. The work is global by design; the payment rail is not.
International wires are where that mismatch turns into cost. Each transfer is slow — two or three business days when correspondent banks are friendly — and the fee is per transaction, so a cohort of twenty teaching assistants is twenty separate slices taken out before the money lands. Some regions have no clean banking corridor at all, and the contractor waits while finance hunts for an intermediary. The amount that arrives often doesn't match the amount sent, which leaves reconciliation to guesswork at month-end.
There is also a control problem hiding underneath the speed problem. When an instructor payment gets pasted into a wallet UI and confirmed with a screenshot, nobody can answer the audit questions later: who authorized it, against what work, and that it went to the right address. The transfer itself is the easy part. Proving it afterward — and not paying the same person twice when a payroll run is retried — is what an education business actually struggles with.
One-off work: single payouts with operator approval
A guest lecturer, a one-time course-design fee, a mentor paid for a single intensive — these are one-off transfers, and a single payout treats each as a tracked operation rather than a manual wallet action. You name the currency, the amount, and the destination address; halfin validates the address against the chosen chain and reserves the funds, but broadcasts nothing yet. The payout sits in pending approval, recorded and visible and inert.
An operator with payout permissions reviews and approves it before anything moves on-chain. That gate is the point: the person who drafts a payment to a contractor is not necessarily the person who releases it, which is the separation an auditor expects to see. Only after approval does halfin sign and submit the transaction to the network, then wait for that chain's confirmation threshold with reorg-aware crediting so a dropped transaction never leaves you with a false completed state.
Every step is recorded against a stable payout ID — who created it, who approved it, when it executed, and the on-chain transaction it produced. When finance closes the month, that one-off lecturer payment points back to the work it satisfied and forward to the transaction that settled it, with no chat-thread reconstruction in between.
- Create — name currency, amount, and destination; halfin validates the address and reserves the funds.
- Approve — an operator with payout permissions authorizes the release; nothing moves on-chain before this.
- Execute — halfin signs and broadcasts to the contractor's network.
- Confirm — crediting waits for the per-chain threshold and is reorg-aware.
- Reconcile — the full history ties the payout back to the work and the payee.
The recurring run: idempotent mass payouts for a cohort
When the unit of work is a payroll cycle rather than a single payment — a whole cohort of teaching assistants, a batch of affiliate educators, a monthly contractor run — the thing you reason about is the run, not the individual line. A mass payout is exactly that: the set of payouts you submit together and approve once, where each line carries its own idempotency key.
Idempotency is the load-bearing property. Network calls time out and return ambiguously — the request may have landed on-chain even though your worker never saw the response. A naive retry would push a second payment; with a caller-supplied key, re-submitting the run returns the payout halfin already created instead of making a new one. Derive each key deterministically from your own records — for instance the TA's account id joined with the payout period — so the same person in the same cycle always maps to the same key, and the batch becomes safe to re-run from the top after any crash or restart.
Partial failure is the normal case, not an exception. A malformed address or a currency a destination can't receive is rejected per-line, and the rest of the run still goes through; you fix the rejected rows and re-submit the whole file, where the lines that already succeeded are no-ops. There is no separate batch endpoint to learn — a mass payout is a fan-out over the same single-payout API, with the same pending-approval gate, so your integration can stage an entire cohort unattended and a treasurer still signs off before anything leaves the balance.
Submitting an instructor payroll run from code
Every payee in a run is described by the same four fields — currency, amount, destination, and idempotency_key — whether you send one line or a few hundred. Amounts are always strings, never floating-point numbers, so a value transports exactly with no rounding drift between your books and the chain, and currency and network are chosen per line.
A monthly run is a loop over the single-payout API, where idempotency keys make the loop safe to interrupt and re-run. Derive each key from your own ledger so the same instructor in the same period always produces 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 ones are created. The example below walks a roster of payees and submits each line with its own key against a payouts-scoped API key.
Each payout comes back in pending approval; the actual release still happens behind operator approval in the dashboard. So wrapping the whole loop in a retry is correct rather than dangerous — the second pass converges on exactly one payout per payee, and nothing has left the balance yet.
# Pay an instructor / TA roster; each line is idempotent, so re-running is safe.
while IFS=, read -r payee 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\": \"june-2026-$payee\"
}"
done < roster.csv
# Each payout returns in "pending_approval"; an operator releases the run.
# Re-run the same roster after a crash: keys collide, nobody is paid twice.Pay each person on the chain they actually want
A distributed bench does not share a wallet preference, and forcing everyone onto one network means rejected payments and addresses that can't receive the asset you sent. An instructor settling in USDT may want it on Tron where fees are low and predictable; a US-facing contractor may need USDC on Base or Ethereum for their own accounting; a platform engineer might prefer native SOL for speed. Each payout line picks its own currency and network, so one run can span all of them.
Settlement is reorg-aware and uses per-chain confirmation thresholds, so a 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 cohort run, where you want a per-payee status to mean "done", not "submitted and hopefully fine". The table below is the real supported payout surface for an education business paying its bench.
| Asset | Networks for payout | Typical payee |
|---|---|---|
| USDT | Tron (TRC-20), Ethereum (ERC-20), Solana | Instructors and contractors who settle in stablecoin |
| USDC | Ethereum (ERC-20), Solana, Base | US / EVM-native contractors and affiliate educators |
| BTC | Bitcoin | Payees who prefer to receive into BTC-only wallets |
| ETH | Ethereum, plus L2s Base, Arbitrum, Polygon | EVM-native engineers and token-settling contractors |
| Native L1/L2 | BNB Smart Chain, Tron, XRP Ledger, Solana | Payees on a specific chain you fund and send from |
Approval, permissions, and a record finance can reconcile
Single and mass payouts are both built around the assumption that moving money is a controlled action. API keys carry scoped permissions, so the integration that stages an instructor payroll run holds only the ability to draft payouts, while a read-only export key never can. The operator approval step sits on top of that as the human authorization on-chain finality demands — one approval for a whole cohort run, not dozens of separate sign-offs.
Each transition also emits a webhook, so your systems learn that a payout completed without polling. halfin signs every event with HMAC, and your handler should verify that signature before acting — marking a contractor's invoice paid, updating a payroll ledger, or notifying the payee. Treat the webhook as a prompt to read the authoritative state, not as the source of truth: an unverified or replayed payload should never move money on your side.
Because the same dashboard and API hold both the payouts and the tuition collection that funds them, finance closes the loop in one place. Every payout record carries enough context to tie a wallet payment back to the instructor, the contractor, or the cohort — so the question "who did we pay, for what, and was it authorized" has a single answer instead of a folder of bank confirmations.
A payout draws from your merchant balance, so the asset you send has to be funded. When you collect USD-priced tuition mostly as USDT on Tron but owe a contractor USDC on Base, balance conversion is the step in front of the payout: convert part of your balance into the asset and network the payee expects, then pay out against the converted funds — automatically as a treasury rule or manually when you want to time it.
- Scoped API keys — the key that stages payroll is separate from a read-only accounting key.
- Operator approval — a human authorizes the release; one sign-off covers a whole cohort run.
- Idempotency keys — a retried payroll run settles each payee once, not twice.
- Address validation — every destination is checked against its chosen chain at creation.
- Full history — creator, approver, timestamps, and the on-chain tx, tied to the payee and cohort.