> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lead.bank/llms.txt
> Use this file to discover all available pages before exploring further.

# Collecting and Disbursing ACH Payments

> Collect funds from one party and pay them out to another over ACH, including the returns and reversals you'll handle along the way.

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

| Object                  | Surface       | Role in this flow                                                                                                                                   | Reference                    |
| ----------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| Core account            | core          | Where collected funds sit between the legs                                                                                                          | \[Banking Infrastructure]    |
| Account Number          | core          | Per-payer routable address on your core account, attributed via `entity_id`; also where origination identity binds via `ach_controls.originator_id` | \[Account Numbers endpoints] |
| Entity                  | user          | The payer/payee, represented per your program's structure                                                                                           | \[Entity endpoints]          |
| `ach` (outgoing debit)  | execution     | Pull collection from the payer's external account                                                                                                   | \[ACH endpoints]             |
| `ach` (incoming credit) | execution     | Push collection arriving on the payer's account number                                                                                              | \[ACH endpoints]             |
| `ach` (outgoing credit) | execution     | The payout to the payee's external account                                                                                                          | \[ACH endpoints]             |
| Return                  | execution     | A payment sent back: by the other bank on your originations, or by you on incoming payments                                                         | \[ACH returns]               |
| Reversal                | execution     | Your correction of an erroneous origination, within the reversal window                                                                             | \[ACH returns]               |
| Originator              | configuration | The identity payments send under: your program's default, or per-client via the API, bound to sending Account Numbers                               | \[Originators]               |

## 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`:

```text theme={null}
POST /v0/originators

{
  "entity_id": "the client's entity",
  "account_id": "...",
  "company_name": "MAPLE PROPERTIES",
  "ach": {
    "allowed_sec_codes": ["PPD"],
    "limits": { "...": "..." }
  }
}
```

2. 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]).
3. 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)

```text theme={null}
POST /v1/account_number

{
  "account_id": "your core account_id",
  "entity_id": "the payer's entity_id",
  "...": "..."
}
```

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:

```text theme={null}
POST /v1/simulate/ach/incoming_ach

{
  "account_number_id": "Marcus's dedicated number",
  "transaction_type": "credit",
  "amount": 180000,
  "currency_code": "USD",
  "sec_code": "PPD",
  "statement_descriptor": "RENT MAY",
  "sender_name": "MARCUS BAKER",
  "sender_company_id": "1234567890",
  "sender_routing_number": "021000021"
}
```

**Pull:** originate the debit, from the Account Number that carries the right originator if you're on the per-client model from Step 2:

```text theme={null}
POST /v1/ach

{
  "account_number_id": "...",
  "transaction_type": "debit",
  "amount": 180000,
  "currency_code": "USD",
  "delivery_type": "next_business_day",
  "sec_code": "PPD",
  "statement_descriptor": "RENT MAY",
  "counterparty": {
    "name": "Marcus Baker",
    "routing_number": "...",
    "account_number": "...",
    "account_type": "checking"
  }
}
```

**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

```text theme={null}
POST /v1/ach

{
  "account_number_id": "...",
  "transaction_type": "credit",
  "amount": 180000,
  "currency_code": "USD",
  "delivery_type": "same_business_day",
  "sec_code": "PPD",
  "statement_descriptor": "RENT PAYOUT",
  "counterparty": {
    "name": "Dana Whitfield",
    "routing_number": "...",
    "account_number": "...",
    "account_type": "checking"
  }
}
```

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):

| Code family                                            | What it means                  | Your move                                                                                                                                                                                                                                                                                                                                                        |
| ------------------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| R01 / R09 (insufficient / uncollected funds)           | The money wasn't there         | Retriable. Nacha permits up to two retries of a returned entry; time them sensibly (after payday beats tomorrow morning).                                                                                                                                                                                                                                        |
| R02 / R03 / R04 (account closed / not found / invalid) | The details are wrong or dead  | Don't retry as-is. Fix the details, check for a NOC, or go back to the counterparty for new ones.                                                                                                                                                                                                                                                                |
| R10 / R11 (unauthorized / not per authorization)       | The payer disputes the debit   | Stop. Do not re-originate on the existing authorization. This is a dispute, not a failure: pull your authorization records, and re-collect only on a fresh authorization. Repeated unauthorized returns are also a program-health metric Lead monitors; if your dispute rate clusters on payers who don't recognize the descriptor, revisit the Step 2 decision. |
| R29 (corporate not authorized)                         | Business-account analog of R10 | Same discipline; business rules and shorter windows apply.                                                                                                                                                                                                                                                                                                       |

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:

```text theme={null}
POST /v1/ach/{ach_id}/return

{
  "return_code": "R23"
}
```

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:

```text theme={null}
POST /v1/ach/{ach_id}/reverse

{
  "reversal_reason": "duplicate"
}
```

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:

| Collection leg                            | Payout leg                              | What it means                                                | What to do                                                                                                                                                                                                                                  |
| ----------------------------------------- | --------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Pulled debit returned before payout sent  | Not yet sent                            | The common protected case                                    | Don't send the payout; notify the payer; retry per the Step 7 code table.                                                                                                                                                                   |
| Pulled debit returned after payout posted | Posted                                  | You've paid out funds you no longer have                     | Reversal is not available; the payout wasn't erroneous when sent. Recover from the payer through your own process; this exposure is what your Step 5 hold policy prices.                                                                    |
| Pulled debit returned R10, weeks later    | Long since posted                       | An unauthorized-debit dispute landed outside any hold window | Funds claw back from your account. Stop collection on that authorization, work the dispute with your records, recover out of band. No hold policy prices a 60-day window; payer-quality controls and a recognizable descriptor (Step 2) do. |
| Pushed payment can't be attributed        | (none)                                  | Funds arrived on an unknown or canceled number               | Return it (Step 8) on your runbook clock.                                                                                                                                                                                                   |
| Incoming reversal hits your account       | (none)                                  | A counterparty is correcting their own erroneous push        | It posts like any incoming debit; reconcile it against the original credit. If it doesn't correspond to one, return it.                                                                                                                     |
| Posted                                    | Payout returned (R02/R03)               | Payee's details wrong or account closed                      | Funds come back; check for a NOC, fix, re-originate.                                                                                                                                                                                        |
| Posted                                    | Duplicate/wrong payout, caught same-day | Your origination error                                       | Reverse it (Step 9) immediately; this is the reversal's home case.                                                                                                                                                                          |
| Posted                                    | Your reversal is returned               | The payee's funds were already gone                          | The rail is exhausted; recover through your own process with the payment identifiers.                                                                                                                                                       |
| NOC on either originated leg              | (none)                                  | Details corrected, payment still succeeded                   | Update stored details before the next cycle.                                                                                                                                                                                                |

## 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.
