Skip to main content
This guide walks through the most common money-movement pattern on Lead: collect funds from one party, then pay them out to another. Rent collection, invoice payment, marketplace payouts, and bill pay are all versions of this flow. The guide covers three things:
  • The two ways to collect: debit the payer’s account yourself, or give the payer a dedicated account number to send to.
  • When to release the payout, since a collected payment can still be returned after it posts.
  • How to handle returns and reversals: payments the other bank sends back, payments you need to send back, and how to undo your own mistakes.

How it works

Your program’s funds sit in a core account at Lead (your FBO or operating account). Collection works one of two ways. If you pull, you originate an ACH debit against the payer’s external account. If the payer pushes, their bank sends an ACH credit to the dedicated Account Number you provisioned for them (attached to your core account, attributed to the payer via entity_id). The payout is an ACH credit you originate to the payee’s external account. One concept to get right early: on the ach object, direction tracks who originated the entry, not which way the money moved. A pulled debit is an outgoing entry that brings money in; a pushed payment is the only incoming entry in this flow. Return windows and correction rights follow the entry’s direction, not the money’s, which is why the guide keeps the two separate. Every origination goes out under an originator: the identity on the payer’s bank statement. That identity is either your program’s default, configured at onboarding, or a per-client originator bound to the Account Numbers you originate from. The collection and payout legs are independent payments with independent lifecycles and return windows, and the key design decision is when to release the payout. Unlike wires, ACH is built for correction: a payment you originated can come back as a return (the other bank sends it back, within code-specific windows); a payment you received can be returned by you; and a payment you originated in error can be reversed by you within a short window. Each is a distinct flow with its own clock.

Example scenario

RentRelay collects rent for landlords. Tenant Marcus’s rent comes in one of two ways: RentRelay originates an ACH debit for $1,800 against his checking account, or Marcus’s own bank pushes $1,800 to the dedicated account number RentRelay provisioned for him. Either way the funds land on RentRelay’s core account and attribute to Marcus. Because RentRelay collects on behalf of its property-manager clients, it creates an originator per client and originates each client’s payments from Account Numbers carrying that client’s originator, so Marcus’s statement reads “MAPLE PROPERTIES,” the name on his lease, rather than a platform he’s never heard of. RentRelay waits out its risk window, then originates an ACH credit for $1,800 (less its fee) to landlord Dana’s bank account. Marcus and Dana never open accounts anywhere new. The correction flows all have RentRelay stories too: Marcus’s debit comes back R01 (insufficient funds) and RentRelay retries it after payday; a push arrives on a number belonging to a tenant who moved out last month, and RentRelay returns it; a duplicate payout goes to Dana, and RentRelay originates a reversal the same afternoon; and eight weeks later, a different tenant disputes a debit as unauthorized, and the return arrives long after the landlord was paid.

Objects involved

Before you start

Sandbox credentials with ACH, entity, and account-number scopes; your core account’s account_id from onboarding. Your program’s default origination identity is configured at onboarding. You only integrate the [Originators] API if you originate on behalf of your own clients (the Step 2 decision). There is no separate originator scope; confirm access with your Lead team when you request approval for the Originator API. For pull collection, authorization to debit the payer is yours to obtain and retain. Returns for unauthorized debits are the expensive failure mode, and your authorization records are what a dispute turns on. This flow is fully API-based. (Under the unified model, account numbers will attach to balances rather than accounts; same concept, typed IDs; [Platform Evolution].)

Integration steps

Step 1: Choose how each payment comes in

For any given collection there are exactly two options:
  • You pull the payment. You originate an ACH debit against the payer’s account, under an authorization you hold. You control timing and amount, which fits scheduled obligations like rent. The risk: the payer’s bank can return the debit after it appears settled.
  • The payer pushes it. They send from their own bank to the dedicated account number you provisioned for them. This nearly eliminates unauthorized-return risk and fits invoice-style “pay when ready” flows. The cost: you wait on the payer.
This is not a program-wide setting; choose per collection type. Most programs pull their scheduled payments and accept pushes for one-off ones.

Step 2: Decide whose name the money moves under: the default identity, or per-client originators

Every origination is sent under an originator: the identity that carries the company name onto the counterparty’s bank statement, along with the SEC codes and limits Lead enforces on that identity. Decision point: there are two models, and most programs only ever need the first. Program-default (no Originators integration). Your program originates under a single identity, your own, configured by Lead at onboarding. Payments pick it up automatically; you never call the Originators endpoints. If everything you originate should say your name, this is you: skip to Step 3. Per-client originators (integrate the API). You originate on behalf of your own clients (property managers, merchants, employers) and each should appear under their own name. The identity does not ride on the payment call; it binds to the Account Numbers you originate from. Three moves:
  1. Create an originator per client and get it to active:
  1. Provision the Account Number(s) you will originate that client’s payments from (on your core account, per Step 3), and set the client’s originator on each: ach_controls.originator_id, at creation or via update ([Payment Controls]).
  2. Originate that client’s pulls (Step 4) and payouts (Step 6) from those Account Numbers. Entries sent from them carry the client’s company_id and company_name in the batch header, subject to the originator’s allowed_sec_codes and limits.
This looks like a branding decision, but it is really a returns decision: the statement descriptor is what the payer sees, and an unrecognized descriptor is a leading driver of the R10 unauthorized-return disputes in Step 7. “MAPLE PROPERTIES” on Marcus’s statement, matching the name on his lease and his authorization, generates fewer disputes than a platform name he’s never heard of. If your payers authorized a client of yours rather than you, per-client originators aren’t cosmetic; they keep the descriptor consistent with the authorization.

Step 3: Provision a dedicated Account Number per payer (push mode)

The account_id is your core account’s: account numbers attach to the core account, never to a user Account, with the payer carried on entity_id ([Object Model]). Assign and activate per the [Account Number lifecycle]; the number can’t transact until active. Incoming funds attribute to the payer automatically by which number they arrived on; this attribution is the whole point of per-payer numbers. Use per-number [controls] to scope what each address accepts (e.g. incoming ACH only), and cancel numbers when the relationship ends. A retired tenant’s number left active is how Step 8’s misdirected-push case happens. Decision point: one number per payer is the common pattern; issue additional numbers when the product needs distinct addresses per purpose (e.g. a dedicated direct-deposit number), and use per-number [controls] to scope each.

Step 4: Receive the push (or originate the pull)

Push: handle ach.posted on incoming payments; attribute by account number. In sandbox:
Pull: originate the debit, from the Account Number that carries the right originator if you’re on the per-client model from Step 2:
Decision point, same-day vs. standard windows: same-day costs more and compresses your operational day but shortens time to (apparent) settlement. Choose per leg via delivery_type; many programs collect standard and pay out same-day. Cutoffs and settlement times: [ACH Timing & Availability].

Step 5: Decide how long to hold before paying out

Posted is not final on a pulled debit: insufficient-funds returns typically arrive within two banking days, and certain consumer unauthorized returns can arrive up to 60 calendar days later. Decision point (the core one): your hold policy trades payee experience against return risk. Hold 2 to 3 banking days to clear the NSF window, pay out immediately and absorb returns as a business cost, or tier by payer history and collection mode (pushed funds warrant shorter holds than pulled funds, since the payer initiated them).

Step 6: Pay out

Handle ach.posted on this leg to mark the payout delivered.

Step 7: Handle returns on payments you originated, by code, not generically

When the other bank returns your origination, the return carries a return code, and the code determines your next move. A single “payment failed” handler is the mistake to avoid ([ACH returns] carries the full code reference): Returns of your credit originations (the payout leg) are simpler: funds come back; fix and re-originate per the exceptions table below.

Step 8: Return an incoming payment you received

Sometimes money arrives that you shouldn’t keep: a push to a canceled or unrecognized account number, a duplicate, or a debit posted against your account that you didn’t authorize. Return it within the return window (two banking days for most cases; 60 calendar days plus a WSUD for unauthorized consumer debits) rather than letting it sit:
The return_code must fit the case ([ACH returns] maps codes to situations and deadlines). Decision point: define the auto-return cases up front. Unattributable pushes and duplicates should return on a runbook clock, not accumulate in a suspense process. Unexplainable funds are a compliance problem that compounds with age.

Step 9: Reverse your own mistakes, fast

When you originated in error (a duplicate payout, wrong amount, or wrong account) ACH gives you a correction tool the wire rail doesn’t: the reversal, an offsetting entry you originate within five banking days of settlement, and only for those three error types. It is not a dispute mechanism or a change-of-mind button:
A reversal is best-effort. It pulls from the receiver’s account, and if the funds are gone it can itself be returned. A same-afternoon reversal of a duplicate payout usually succeeds; a day-four reversal is a coin flip. Notify the receiver that a reversal is coming (Nacha expects it, and surprise debits generate disputes). Past five banking days, the reversal window is closed and recovery is a conversation, not a rail operation.

Step 10: Keep counterparty details current

Handle NOCs ([ACH NOCs]) on originated legs. A NOC means the receiving bank corrected the details you used; Lead normalizes it into the correction object on the original entry, and the ach.corrected webhook tells you it arrived. Use the corrected details on the next origination; Lead may reject re-use of the old details with the previously_corrected rejection reason.

Handling exceptions

Failures on the two legs interact; handle them as combinations:

Testing this flow in sandbox

If you’re on the per-client model, create a sandbox originator first, activate it, set it on a sandbox Account Number’s ach_controls, and confirm entries originated from that number carry it. Simulate the push leg with POST /v1/simulate/ach/incoming_ach against a dedicated account number and verify your attribution logic. Originate a pull debit and a payout credit with POST /v1/ach; outgoing entries stay in scheduled until you advance them with POST /v1/simulate/ach/{ach_id}/advance ([ACH Sandbox Testing]). The correction machinery tests as follows. Returning an incoming payment and reversing your own origination are real endpoints (/return, /reverse) and work in sandbox directly; no simulator is involved. Counterparty returns of your originations use POST /v1/simulate/ach/{ach_id}/incoming_return; exercise the code families in Step 7, including an R01-then-retry sequence, an R10 (verify your handler stops collection rather than retrying), and a return arriving after the payout leg has been sent, which is the scenario your hold policy exists for. For the reversal-returned outcome, call the incoming_return simulator against the reversal’s own ach_id (a reversal is an ordinary outgoing entry). [VERIFY in a live sandbox run before publish: the reversal-returned sequence above.] Per the sandbox-honesty convention: a sandbox transfer that trips a validation rule enters under_review and stays there permanently (manual review is not simulated). If a test entry gets stuck, create a new one; see [ACH Sandbox Testing] for avoiding review triggers.