← halfin journalApr 18, 2026 · 7 min read
Product

The pending-approval gate: why every payout waits

Every payout you create sits inert until someone releases it from the dashboard. Teams read that pause as friction. It is the opposite — it is the one place a wrong address gets caught before the chain makes it permanent.

LT
L. TanakaProduct
product · cover

A payout that goes out the instant you create it is not a feature. It is a missing control with good marketing.

The first question new merchants ask about our payouts is some version of "why doesn't it just send?" You call the API, you get back a payout, the money is reserved — and then nothing moves until a human releases it from the dashboard. To a developer mid-integration, that looks like a step that's in the way.

It is in the way on purpose. The gap between creating a payout and executing one is the entire point. This is the conversation we have with every team that wants us to add an auto-release flag, and the reason we keep saying no.

Two verbs, not one

Most payment tooling collapses "request a withdrawal" and "authorize a withdrawal" into a single act. You hit send, money leaves. That works right up until the moment it doesn't — a fat-fingered address, a compromised key, a script that loops twice — and on a blockchain there is no "doesn't." On-chain transfers are final. There is no settlement window to claw back inside of, no issuer to call, no chargeback. The transaction either confirms or it doesn't, and if it confirms to the wrong place, the money is gone.

So we split the verb. Creating a payout reserves the funds against your balance and records the intent. Releasing it is a separate, separately-permissioned act that signs the transaction and broadcasts it. The person who drafts a single payout is not necessarily the person who lets the money leave — and a leaked API key that can create payouts still cannot, on its own, move a cent.

That last part is worth sitting with. We've watched teams treat their payouts key as if it were a launch code, and then paste it into a CI log anyway. With the approval gate, a key that can only draft payouts has a small blast radius. The worst it can do is create a pile of pending payouts that an operator will look at, frown at, and cancel. Annoying. Not catastrophic.

The pending state is where mistakes are still cheap

Here is the timeline of a wrong address with no gate: you create the payout, it broadcasts, it confirms, you find out at month-end when the recipient says they never got paid. The error and the discovery are minutes apart on the chain and weeks apart in your awareness, and in between, the money became unrecoverable.

Here is the same timeline with the gate. You create the payout. It enters pending_approval — recorded, visible, inert. Funds are earmarked so a later release can't overdraw your balance, but nothing has been signed and nothing has touched the network. An operator opens the dashboard, sees the destination and the amount, and looks at it while it is still completely reversible. A TRC-20 address pasted into an ERC-20 payout never even gets that far; we validate the destination against the chosen chain at creation. But the typo that's a valid address on the right chain — wrong recipient, transposed digits, an old address from a stale spreadsheet — that one only gets caught by a person looking before the broadcast.

While a payout sits in pending_approval, you can cancel it and the reserved funds return to your balance. After it's signed and broadcast, you can't. The gate isn't a delay bolted onto the front of the flow for ceremony. It is positioned at the exact last moment the mistake is still cheap.

"It slows us down" is usually a permissions problem

When a team tells us the approval step is friction, what they almost always mean is that the same person is creating and approving, so it feels like a pointless double-click. Fair. If you're a solo operator clicking create and then clicking approve, the gate is doing nothing for you that a confirmation dialog wouldn't.

But that's a configuration to fix, not a control to remove. The value shows up the moment more than one person can move money. Scope your API key so your integration can draft payouts but not release them. Put release behind an operator who isn't the one running the script. Now the gate is doing real work: it's the segregation of duties an auditor expects on any outbound movement of funds, and it's the thing that turns "our payout service had a bug" from an incident report into a non-event, because the buggy run is sitting in pending_approval waiting for a human who is about to notice that it has 4,000 lines instead of 40.

We wrote more about building that drafting side to be safe under retries in designing an idempotent payout runner — the gate and idempotency are the two halves of the same idea. Idempotency makes sure your create step doesn't accidentally produce two payouts. Approval makes sure neither one leaves until someone meant it to.

The gate doesn't weaken at volume

A reasonable worry: fine for one-off withdrawals, but surely you batch this for mass runs? You don't, and that's deliberate. A mass payout isn't a different mechanism — it's a fan-out over the same single-payout primitive, where each recipient is its own line with its own amount, destination, and idempotency key. Which means each line carries its own approval and its own audit record. Releasing a thousand affiliate payments runs every one of them through the same gate as a single trader withdrawal, and you can see exactly which lines have been released and which are still waiting.

That's the property that makes reconciliation boring in the good way. The unit of approval is the same individual payout whether you created it alone or as line 612 of a run. There's no special "batch approved" state that papers over which lines actually went out.

What you're actually buying

Strip away the framing and the approval gate is three guarantees. No payout moves without someone who has the permission authorizing it. Every release is recorded — who created it, who approved it, when it executed, and the transaction it produced. And the one human checkpoint sits at the last reversible moment, before the chain makes anything permanent. We treat all three as part of how the platform protects the money path, alongside scoped keys and signed webhooks, which is the subject of our broader security model. If you want the mechanics of the release step itself, the payouts approval FAQ walks the states one by one.

The instinct to remove the pause is the instinct to optimize away the seatbelt because you haven't crashed yet. Crypto payments don't give you a second chance at the destination address. The gate is the second chance, moved to the only place it can still help.

L. Tanaka, halfin product team

↳ end of articlehalfin journal · Apr 18, 2026