Payouts: money out
Money out of the loop: the ledger moves first, and the payout record is the instruction to make the real transfer.
A merchant earns into a settlement account. A payout moves that balance out of the loop and produces the instruction to pay it.
This is a semi-closed wallet: consumers cannot cash out to a bank. Money leaves through merchant payouts and nowhere else. That is a product decision with regulatory consequences, not a missing feature.
Books first, transfer second
The ledger moves first. The payout record is the instruction to make a real transfer, and it carries external_ref for the reference your bank rail gives back.
That ordering is deliberate. Books that lead reality can be reconciled; books that trail it cannot answer "did we already pay this?" during an incident.
Run one
curl -sS -X POST "$WALLETD_API/v1/clients/$CLIENT_ID/payouts" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: payout-2026-08-11-$CLIENT_ID" \
-d '{}'{
"id": "019fee0d-be2a-7090-97ef-c1f7c3162ef6",
"client_id": "019f...",
"amount": 2000,
"rail": "manual",
"status": "completed",
"external_ref": null,
"created_at": "2026-08-11T05:41:52.006Z"
}An empty body pays out the full settlement balance. The settlement account is zero afterwards, and a second run against a zero balance is refused with 422 nothing_to_pay rather than creating an empty payout.
Derive the idempotency key from the payout period, not the moment: payout-2026-08-11-{client} retried after a timeout pays once, where a timestamped key would pay twice.
func (c *Client) RunPayout(ctx context.Context, clientID, period string) (*Payout, error) {
var payout Payout
// The key is the period, so a retry after a timeout cannot pay twice.
key := "payout-" + period + "-" + clientID
err := c.Do(ctx, http.MethodPost, "/v1/clients/"+clientID+"/payouts", map[string]any{}, &payout, key)
return &payout, err
}List and reconcile
curl -sS "$WALLETD_API/v1/clients/$CLIENT_ID/payouts" -H "Authorization: Bearer $TOKEN"
curl -sS "$WALLETD_API/v1/merchants/$MERCHANT_ID/statement" -H "Authorization: Bearer $WALLETD_API_KEY"The statement is the merchant-facing document: what they earned, what was deducted, what was paid out, and when. It is what a merchant's finance team reconciles against their own books.
Rails
rail is manual today: the payout is a ledger movement plus an instruction a human or a treasury job executes. external_ref is where you record the bank reference once the transfer is made, which closes the loop for reconciliation.
The field is free-form on purpose. When a local payout rail is added, existing payouts keep their shape and your integration does not change: the same call, a different rail value, and external_ref filled in automatically instead of by you.
The float, before and after
A payout shrinks both sides of the float equation at once: settlement goes down, treasury goes up, and money that left the loop is accounted for in treasury rather than vanishing.
curl -sS "$WALLETD_API/v1/ledger/summary" -H "Authorization: Bearer $WALLETD_API_KEY"If you reconcile against your bank, this is the endpoint that tells you what should have left. See The money model.
What can go wrong
| Code | Status | What happened |
|---|---|---|
nothing_to_pay | 422 | The settlement balance is zero |
not_client_member | 403 | The caller is not a member of that client's organization |
client_not_found | 404 | Bad client id, or not visible to you |
Next
- The money model for the float equation.
- Ecosystem for how a client earns a settlement balance.
Limits and credit lines
Tier caps that refuse money before anything irreversible happens, and credit lines that let a business account spend below zero.
The ecosystem: clients, offerings, discovery
Many businesses in one shared consumer loop: clients, offerings, discovery, what a sale earns, and client-funded rewards.