Bank transfers

Let a customer pay invoices by ACH or wire into a bank account that is theirs alone

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:

CodeWhat it meansHow to fix
ORG_TEST_MODEYour organization is in test mode.Virtual bank accounts need a live Stripe account. Use your live-mode organization.
STRIPE_NOT_CONNECTEDNo Stripe account is connected.Connect Stripe under Settings → Billing.
NO_STRIPE_CUSTOMERThe 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_ENABLEDYour 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_CURRENCYA 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.

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 '{}'
{
"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.

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.

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