> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://docs.paid.ai/v-2/llms.txt
> - v1: https://docs.paid.ai/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paid.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paid.ai/_mcp/server.

# Bank transfers

A **virtual bank account** is a US bank account number issued through your connected Stripe account and dedicated to one customer. The customer pushes an ACH or wire transfer to it from their own bank, Stripe credits the funds to your account, and Paid matches the transfer to the customer's open invoice and marks it paid.

Use it for customers who cannot or will not pay by card or by Stripe-hosted checkout, such as enterprise accounts paying from accounts-payable systems.

## Before you start

Creating a virtual bank account checks these conditions and returns a `422` with a specific `code` when one is not met:

| Code                                | What it means                                                                                 | How to fix                                                                                                                            |
| ----------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `ORG_TEST_MODE`                     | Your organization is in test mode and this Paid environment has no sandbox Stripe configured. | Paid production has sandbox Stripe configured, so test-mode organizations can create accounts there. Contact support if you see this. |
| `STRIPE_NOT_CONNECTED`              | No Stripe account is connected.                                                               | Connect Stripe under **Settings → Billing**.                                                                                          |
| `NO_STRIPE_CUSTOMER`                | The customer has no Stripe customer record and Paid could not create one.                     | Turn on automatic Stripe customer creation in **Settings → Billing**, or create the Stripe customer first.                            |
| `STRIPE_BANK_TRANSFERS_NOT_ENABLED` | Your Stripe account has not been granted the Bank Transfer payment method.                    | In the Stripe Dashboard, go to **Settings → Payments → Payment methods** and request access to Bank Transfers, then retry.            |
| `UNSUPPORTED_CURRENCY`              | A currency other than USD was requested.                                                      | Only USD virtual accounts are available today.                                                                                        |

## Create the account

One call per customer. Calling it again for the same customer and currency returns the existing account with a `200`.

```bash
curl -X POST https://api.paid.ai/api/v2/customers/cus_abc123/funding-instructions \
  -H "Authorization: Bearer $PAID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```json
{
  "customerId": "cus_abc123",
  "currency": "USD",
  "bankName": "Evolve Bank & Trust",
  "accountHolderName": "Acme Billing Inc",
  "routingNumber": "084106768",
  "accountNumber": "9000000000001234",
  "accountType": "checking",
  "status": "active",
  "memo": "Include the invoice number, e.g. INV-0042",
  "createdAt": "2026-09-22T14:03:11.000Z"
}
```

You can also address the customer by your own identifier with `/api/v2/customers/external/{externalId}/funding-instructions`. Read the account back at any time with `GET` on the same path.

The account details are printed automatically on every invoice PDF and hosted invoice page issued to that customer from then on, together with the memo instruction.

## Test it in sandbox

Test-mode organizations work the same way against Stripe's sandbox, so you can rehearse the whole flow without moving money:

1. Connect a sandbox Stripe account to your test-mode organization and, in that account's Stripe Dashboard, enable **Bank transfers** under **Settings → Payments → Payment methods**.
2. Create the virtual account as above and issue an invoice to the customer.
3. Simulate the transfer with Stripe's test helper, putting the invoice number in the reference:

```bash
curl https://api.stripe.com/v1/test_helpers/customers/{stripeCustomerId}/fund_cash_balance \
  -u "$STRIPE_TEST_SECRET_KEY:" \
  -H "Stripe-Account: {connectedAccountId}" \
  -d amount=50000 \
  -d currency=usd \
  -d reference=INV-0042
```

Stripe emits the same event a real transfer would, and Paid matches it exactly as described below, except that the funds are available immediately rather than after 2 to 3 business days.

## How a payment is matched

When a transfer arrives, Paid looks for an invoice number in the transfer memo and compares the amount received with the invoice's amount due.

* **Memo names an open invoice and the amount matches exactly:** the payment is recorded, allocated to the invoice, and the invoice is marked paid. Your `invoice.paid` and `payment.succeeded` webhooks fire.
* **Anything else** (no memo, unknown invoice number, partial payment, overpayment, bank fee deducted, one transfer covering two invoices): the payment is recorded against the customer as **unallocated** and nothing is marked paid. Allocate it to invoice lines from the payment's page or with the [payment allocations API](/api-reference/payment-allocations).

Tell your customer to pay the exact invoice amount and to put the invoice number in the memo or reference field. Funds are typically credited within 2 to 3 business days of the transfer.

## Good to know

* The account is issued by Stripe, so the bank name and account holder shown are Stripe's, not your own bank's.
* One account per customer and currency. Disconnecting Stripe removes the accounts; reconnecting issues new numbers.
* Virtual bank accounts can only be created and read through the API for now. Deactivating an account and non-USD currencies are not yet available.