---
title: "ACH pull vs ACH push: two ways to fund a USD to USDC on-ramp"
seoTitle: "ACH pull vs ACH push for a USD to USDC on-ramp"
description: "Push means the payer sends an ACH transfer. Pull means you debit a connected account. How each funds a USD to USDC on-ramp, the fees, and the return risk."
date: "2026-10-08"
updated: "2026-10-08"
category: "payments"
author: "BlindPay Team"
howto:
  name: "How to fund a USD to USDC on-ramp with ACH pull"
  steps:
    - name: "Have the customer connect their bank account"
      text: "Call POST /v1/instances/{instance_id}/customers/{customer_id}/bank-accounts/plaid and open the returned url in an iframe or a new tab. The link is single use and expires after at most 4 hours. BlindPay creates an ach bank account with plaid_connected_at set once the customer finishes."
    - name: "Create a payin quote with ach_pull"
      text: "Call the payin quotes endpoint with payment_method ach_pull, funding_bank_account_id set to the connected ba_ id, the destination wallet, currency_type, cover_fees, request_amount in minor units, and token. The returned sender_amount already includes the 1.00 USD pull fee."
    - name: "Create the payin within 5 minutes"
      text: "Create the payin from the quote before it expires. The pull starts automatically. There are no memo codes or bank details for the payer to act on. If the pull cannot be initiated, the payin fails right away with PAYINS_FUNDING_PULL_FAILED."
    - name: "Wait for payin.complete"
      text: "Listen for payin.new, payin.update, and payin.complete webhooks. payin.complete fires when the stablecoins reach the destination wallet set on the quote."
faq:
  - q: "What is the difference between ACH push and ACH pull?"
    a: "With ACH push, the payer logs into their own bank and sends a transfer to the account details you show them. With ACH pull, the payer authorizes you once by connecting their account, and each payment is a debit you initiate. On BlindPay, push is payment_method ach and pull is payment_method ach_pull."
  - q: "Does BlindPay support ACH pull for on-ramps?"
    a: "Yes, as a private beta since September 18, 2026. The customer connects their bank account once through a hosted Plaid page, and payins with payment_method ach_pull debit that account directly. Ask your BlindPay contact to enable it on your instance, since the instance needs the Plaid feature turned on."
  - q: "How much does an ACH pull cost on BlindPay?"
    a: "A pull is priced like a standard ACH payin, plus a flat 1.00 USD fee that BlindPay's ACH pull provider charges back to the payer. The sender_amount on the quote already includes that dollar, so it is the full amount to show the payer. The rest of the pricing is disclosed in the live quote."
  - q: "Can a customer connect someone else's bank account for ACH pull?"
    a: "No. A connected account must belong to the customer it is connected for. BlindPay runs a Plaid Identity Match on the account holder's name before creating the bank account, and the resulting account is always first party. If the names don't match at Plaid's recommended threshold, the connection is rejected."
  - q: "Can an ACH pull be reversed after the USDC is delivered?"
    a: "An ACH debit can be returned by the payer's bank. Nacha rules give most returns two banking days, while unauthorized consumer debits can come back for up to 60 calendar days. Factor that window into how much you let a new customer pull before you trust the account."
---

ACH push and ACH pull are the two ways US dollars enter a stablecoin on-ramp. With push, the payer sends an ACH transfer from their own bank to details you show them. With pull, the payer connects their account once and each payment is a debit you start. On BlindPay, push is `ach`, and pull is `ach_pull`, in private beta.

Both end the same way: dollars land, BlindPay converts them, and USDC or USDT arrives in the customer's wallet. What changes is who starts the money moving, what can go wrong, and what the payer has to do.

**Key takeaways**

- Push (`payment_method: "ach"`): the payer sends a transfer using a memo code and BlindPay's bank details, or their own virtual account details.
- Pull (`payment_method: "ach_pull"`): BlindPay debits a Plaid-connected account. Private beta since September 18, 2026.
- Pull adds a flat 1.00 USD fee, already included in the quote's `sender_amount`.
- Connected accounts for pull are always first party, checked with Plaid Identity Match.
- Pulled debits can be returned: two banking days for most returns, up to 60 calendar days for unauthorized consumer debits.

## How does ACH push fund an on-ramp?

The payer does the work. You create a payin quote with `payment_method: "ach"`, create the payin, and the response gives the payer what to send ([payins](/docs/payins)).

If the customer has no approved virtual account, the payer sees a `memo_code` plus BlindPay's shared bank details, and the memo is how the deposit gets matched to the payin. If the customer has an approved virtual account, the payer sees that account's own routing and account numbers, and the memo is ignored. [Virtual account vs one-time deposit instructions](/resources/more/virtual-account-vs-one-time-deposit-instructions) covers which one to use.

Then you wait. BlindPay documents a typical arrival window of up to 5 business days for an `ach` payin, because the payer decides when to send and the ACH network runs in batches. Payers who forget the memo are the classic failure, and [why on-ramp deposits fail](/resources/more/why-crypto-on-ramp-deposits-fail) walks through the fixes.

## How does ACH pull fund an on-ramp?

You do the work, once the payer authorizes it. The flow has two parts: connect the account, then pull per payment.

**Connect.** `POST /v1/instances/{instance_id}/customers/{customer_id}/bank-accounts/plaid` returns a `url` to a BlindPay-hosted page that renders Plaid Link. Show it in an iframe or a new tab. The link is single use and expires after at most 4 hours. When the customer finishes, BlindPay creates a bank account of `type: "ach"` with `plaid_connected_at` set and fires `bankAccount.new` ([bank accounts](/docs/bank-accounts)).

**Pull.** Create a payin quote with `payment_method: "ach_pull"` and `funding_bank_account_id` set to that connected account. Create the payin and the pull starts on its own. No memo, no bank details, no waiting on the payer.

ACH pull is in private beta since September 18, 2026 ([changelog](/changelog/2026-09-18-yield-ach-pull-tempo-arc-prompts)). The instance needs the Plaid feature, so ask your BlindPay contact to turn it on.

## How do push and pull compare side by side?

| Factor | ACH push (`ach`) | ACH pull (`ach_pull`) |
| --- | --- | --- |
| Who starts the payment | The payer, from their bank | You, through BlindPay |
| One-time setup | None, or a virtual account | Customer connects the account through Plaid |
| What the payer sees per payment | Memo code and bank details, or virtual account details | Nothing to act on |
| Matching risk | Missing or wrong memo, wrong amount | Low: the amount comes from the quote |
| Extra fee | None | Flat 1.00 USD per pull, inside `sender_amount` |
| Account ownership | Any account the payer controls | First party only, name-matched |
| Availability on BlindPay | Generally available | Private beta since 2026-09-18 |
| Main risk | The payer never sends | The debit gets returned later |

Pick push when payers are businesses that pay by their own AP process, or when amounts vary and the payer wants control. Pick pull for recurring top-ups by the same customer, where a forgotten memo costs you a support ticket every month.

## Illustrative example: what does the payer pay on a $1,000.00 pull?

A customer wants 1,000.00 USDC delivered, and the sender pays the fees (`cover_fees: true`). Assume an illustrative BlindPay fee of 5.00 USD on this payin.

| Line | ACH push | ACH pull |
| --- | --- | --- |
| USDC to deliver | 1,000.00 | 1,000.00 |
| Illustrative BlindPay fee | 5.00 | 5.00 |
| Pull provider fee | 0.00 | 1.00 |
| `sender_amount` debited or sent | 1,005.00 | 1,006.00 |
| Amount the pull actually moves | n/a | 1,006.00 - 1.00 = 1,005.00 |

The arithmetic: 1,000.00 + 5.00 = 1,005.00 for push. For pull, 1,005.00 + 1.00 = 1,006.00 debited, and the pull itself moves 1,006.00 - 1.00 = 1,005.00 because the provider keeps its dollar.

Show the payer `sender_amount` as returned. It already includes the pull fee, so don't add it again on your screen. Real fees come from your live quote. For USD in and USDC out there's no currency conversion to read, but the quote fields work the same way as in [how to read an FX quote](/resources/more/how-to-read-an-fx-quote). [On-ramp fees explained](/resources/more/crypto-on-ramp-fees-explained) breaks down the rest.

## What errors does an ACH pull quote return?

Three validation errors cover most mistakes, all returned at quote time ([payin quotes](/docs/payin-quotes)):

1. `funding_bank_account_id_required`: you sent `ach_pull` without a connected account.
2. `funding_account_not_plaid_connected`: the account exists but was never connected through Plaid, or belongs to a different customer.
3. `funding_account_invalid`: you passed `funding_bank_account_id` with a method other than `ach_pull`.

On an instance without the Plaid feature, the quote fails earlier with `BANK_ACCOUNTS_PLAID_NOT_ENABLED`. And if the pull can't be initiated after you create the payin, it fails immediately with `PAYINS_FUNDING_PULL_FAILED` instead of sitting in `processing`. That's a real improvement over push, where a payin with no deposit just waits.

## Why are pulled accounts always first party?

Because a debit you start needs a clear owner. BlindPay only accepts a connected account that belongs to the customer it is connected for. Before creating the bank account, it runs Plaid Identity Match between the account holder on file at the bank and the customer record, starting with the legal name.

Plaid describes Identity Match as returning match scores for how well your user data matches the account owner's information at their bank ([Plaid](https://plaid.com/docs/identity/)). A mismatch fails the connection and no bank account is created. The identity fields on the resulting account come from the customer record, and `recipient_relationship` is always `first_party`. [Paying yourself vs paying someone else](/resources/more/first-party-vs-third-party-payouts) explains why that field matters elsewhere in BlindPay.

## What happens if a pulled payment is returned?

A pull is an ACH debit, and debits can come back. That's the trade you make for a payer who never has to remember a memo code.

The timing is set by the network, not by BlindPay. Most ACH returns, including insufficient funds and closed accounts, must go back within two banking days of settlement, while consumer entries have an extended window of up to 60 calendar days ([Plaid](https://plaid.com/resources/ach/ach-return/)). Nacha's own rules material confirms the 60-day timeframe for the unauthorized return reason R11 ([Nacha](https://www.nacha.org/rules/differentiating-unauthorized-return-reasons)).

Push transfers carry far less of this risk, because the payer sent the money themselves. So treat pull like a credit decision: cap first pulls for new customers, raise limits as accounts age, and keep the bank account id with every payin so you can trace a return. [How long a stablecoin on-ramp takes](/resources/more/how-long-does-a-stablecoin-onramp-take) has the timing for each rail.

## What to do next

Create a Plaid link for a test customer on a development instance, connect with Plaid's `user_good` / `pass_good` sandbox credentials, and request one `ach_pull` payin quote. The [bank accounts docs](/docs/bank-accounts) have the request body.
