TreasuryGuides
Send a payout
Move money from your funding account to a recipient.
Goal
Initiate, confirm, and track a single payout to a US bank account over ACH, same-day ACH, RTP, or wire.
Prerequisites
- A sandbox or production API key (
sk_sandbox_…/sk_live_…). @easylabs/nodeinstalled and the client constructed.- A linked funding bank account (visible in
client.listBankAccounts()). - A recipient with at least one payment method — either created up-front with banking populated, or invited via the self-serve invite flow.
Implementation
1. Initiate
const { data: payout } = await client.createPayout({
recipient_id: "rcp_123…",
source_account_id: "ba_456…",
amount: 12_500, // $125.00, integer USD cents, > 0
method: "ach", // "ach" | "same_day_ach" | "rtp" | "wire"
memo: "Sept. retainer", // ≤500 chars, appears on bank statement (rail-permitting)
notes: "internal ref 9921", // ≤2000 chars, internal-only
});
// payout.status === "pending"If your account has security rules requiring 2FA on payouts above a
threshold, the response will include approval_required: true and an
approval_request_id. Collect a 6-digit code from the operator and pass it on
confirm.
2. Confirm
await client.confirmPayout({
transaction_id: payout.id,
security_code: "482917", // omit if approval_required was false
});After confirm, the payout is handed to the rail. From here it cannot be cancelled — see Tradeoffs.
3. Track
const { data } = await client.getTransaction(payout.id);
// data.status: "pending" | "processing" | "completed" | "failed" | "returned" | "cancelled"Once completed, fetch the rolled-up settlement:
const { data: settlement } = await client.getTransactionSettlement(payout.id);
console.log(settlement.id, settlement.net_amount);Or subscribe to the settlement.created webhook and post the journal entry on
receipt:
import { constructEvent } from "@easylabs/node";
app.post("/webhooks/easy", async (req, res) => {
const event = constructEvent(req.rawBody, req.headers["easy-signature"], secret);
if (event.type === "settlement.created") {
// event.data.id, event.data.net_amount, event.data.status === "FUNDED"
}
res.sendStatus(200);
});4. Cancel (only before confirm)
await client.cancelPayout({ transaction_id: payout.id });cancelPayout is a no-op once the transaction has moved past pending.
Tradeoffs
- Rail choice. RTP and wire are irreversible the moment they're confirmed — there is no Easy-side rollback. ACH allows up to two business days for return processing but is slower (1–3 day settlement) and cheaper. Same-day ACH funds same business day for a per-transfer fee.
- Idempotency.
createPayoutis not implicitly idempotent. If you retry on a network error without checking, you may double-send. Persist the returnedtransaction_idbefore retrying, or checkclient.listTransactions()first. - Funding shortfall. If your funding account lacks sufficient balance at
rail submission time, the payout will reach
failedrather thancompleted— listen for the status change rather than assuming success afterconfirmPayout.