# Migrate existing bank accounts

Learn how to migrate existing bank accounts to the Payment Intents API or Checkout Sessions API.

Stripe is removing support for [ACH Direct Debit](https://docs.stripe.com/ach-deprecated.md) using [legacy integrations](https://docs.stripe.com/payments/ach-direct-debit/migrating-from-charges.md#identify-legacy-payments).

If you create legacy ACH Direct Debit payments, you must migrate to the [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) or [Checkout Sessions API](https://docs.stripe.com/api/checkout/sessions.md).

If you previously collected customer payment details with Stripe using the [Tokens API](https://docs.stripe.com/ach-deprecated.md), you can continue using the saved `BankAccount` as a *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs).

You can only use customer [bank accounts](https://docs.stripe.com/api/customer_bank_accounts.md) with the [Payment Intents API](https://docs.stripe.com/api/payment_intents.md) or [Checkout Sessions API](https://docs.stripe.com/payments/quickstart.md) after you meet the following requirements:

- **Checkout Sessions API:** The customer’s bank account has been verified.

- **Payment Intents API:** The customer’s bank account has been verified, and an [active mandate](https://docs.stripe.com/payments/ach-direct-debit/migrating-from-charges.md?integration-path=payment-intent#mandate-acknowledgement) exists for that bank account.

You don’t need to re-verify Bank Accounts that are already verified to use them with Payment Intents or Checkout Sessions.

# Payment Intents API


## Collect mandate acknowledgement 

Confirming a PaymentIntent or SetupIntent requires having your customer authorize a [mandate](https://docs.stripe.com/api/setup_intents/create.md#create_setup_intent-mandate_data) to debit the account. Learn more about [SEC codes](https://docs.stripe.com/payments/ach-direct-debit/sec-codes.md) to understand which authorization type is right for your business.

In some cases, you might have pre-authorization from your customer from an earlier purchase or Setup Intent that you can use to create an *off-session* (A payment is described as off-session if it occurs without the direct involvement of the customer, using previously-collected payment information) payment. For example:

- If you previously collected an online mandate from the customer, you can use both the IP address and user agent information to create a `mandate` object. If you didn’t retain your customer’s IP address or user agent information, you can provide placeholder data. If you provide placeholder data, we recommend selecting any value you can easily identify in the future.
- If you previously collected payment and mandate information offline, you can create a [PPD mandate](https://docs.stripe.com/payments/ach-direct-debit/sec-codes.md#ppd-sec-code).

Authorization is only required the first time you use a `BankAccount` object with the Payment Intents or Setup Intents API. After that, you can use the `BankAccount` object as a PaymentMethod to [accept future payments](https://docs.stripe.com/payments/ach-direct-debit/set-up-payment.md?payment-ui=elements#web-future-payments).

You can create and confirm a Setup Intent to create a `mandate` without the need to charge a customer:

#### Accounts v2

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account" \
  -d "mandate_data[customer_acceptance][type]=offline" \
  -d "mandate_data[customer_acceptance][accepted_at]=123456789" \
  -d confirm=true
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account" \
  -d "mandate_data[customer_acceptance][type]=offline" \
  -d "mandate_data[customer_acceptance][accepted_at]=123456789" \
  -d confirm=true
```

You can also provide mandate data when confirming a `PaymentIntent`. Include `setup_future_usage: "off_session"` to create a multi-use mandate for future off-session payments:

```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d setup_future_usage=off_session \
  -d "mandate_data[customer_acceptance][type]=offline" \
  -d "mandate_data[customer_acceptance][accepted_at]=123456789" \
  -d "payment_method_options[us_bank_account][mandate_options][collection_method]=paper"
```

## Create a PaymentIntent with a Bank Account 

You can provide the ID of a saved [BankAccount](https://docs.stripe.com/api/customer_bank_accounts.md) as the *payment\_method* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) when creating a `PaymentIntent`, which eliminates the need to re-collect payment details. However, make sure to [update your integration](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md) to create `PaymentMethods` instead of `BankAccounts` when saving new payment methods.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account" \
  -d amount=1099 \
  -d currency=usd
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account" \
  -d amount=1099 \
  -d currency=usd
```

Similarly, you can provide the ID of a saved `BankAccount` as the `payment_method` when creating a `SetupIntent`.

#### Accounts v2

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/setup_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "payment_method={{BANKACCOUNT_ID}}" \
  -d "allowed_payment_method_types[]=us_bank_account"
```


## Retrieve a BankAccount as a PaymentMethod 

You can retrieve saved BankAccounts through the [Payment Methods API](https://docs.stripe.com/api/payment_methods.md):

```curl
curl https://api.stripe.com/v1/payment_methods/{{BANKACCOUNT_ID}} \
  -u "<<YOUR_SECRET_KEY>>:"
```

When using a BankAccount as a PaymentMethod, no new objects are created. The Payment Methods API simply provides a different view of the same underlying object.

#### PaymentMethod View

```json
{
  "id": "ba_1IsleZ2eZvKYlo2CI3To1g72",
  "object": "payment_method",
  "billing_details": {
    "address": {
      "city": null,
      "country": null,
      "line1": null,
      "line2": null,
      "postal_code": null,
      "state": null
    },
    "email": null,
    "name": "Jenny Rosen",
    "phone": null
  },
  "us_bank_account": {
    "last4": "6789",
    "routing_number": "110000000",
    "fingerprint": "1JWtPxqbdX5Gamtc",
    "account_holder_type": "individual",
    "bank_name": "STRIPE TEST BANK",
  },
  "created": 123456789,
  "customer": "cus_CY5bH92D99f4mn",
  "livemode": false,
  "metadata": {},
  "type": "us_bank_account"
}

```

#### BankAccount View

```json
{
  "id": "ba_1IsleZ2eZvKYlo2CI3To1g72",
  "object": "bank_account",
  "account_holder_name": "Jenny Rosen",
  "account_holder_type": "individual",
  "bank_name": "STRIPE TEST BANK",
  "country": "US",
  "currency": "usd",
  "customer": "cus_CY5bH92D99f4mn",
  "fingerprint": "1JWtPxqbdX5Gamtc",
  "last4": "6789",
  "metadata": {},
  "routing_number": "110000000",
  "status": "verified",
}
```

## Invoices

To create an [Invoice](https://docs.stripe.com/invoicing.md) using a saved `BankAccount`, you need to first record the mandate acknowledgement when confirming a `PaymentIntent` or `SetupIntent`. Then, either update your customer’s default payment method or set the `Invoice`’s `default_payment_method` parameter.

To update a customer’s default payment method:

#### Accounts v2

```curl
curl -X POST https://api.stripe.com/v2/core/accounts/{{CUSTOMERACCOUNT_ID}} \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-08-26.preview" \
  --json '{
    "configuration": {
        "customer": {
            "billing": {
                "default_payment_method": "{{BANKACCOUNT_ID}}"
            }
        }
    }
  }'
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/customers/{{CUSTOMER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "invoice_settings[default_payment_method]={{BANKACCOUNT_ID}}"
```

To create an invoice with a bank account as a payment method:

#### Accounts v2

```curl
curl https://api.stripe.com/v1/invoices \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "default_payment_method={{BANKACCOUNT_ID}}"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/invoices \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "default_payment_method={{BANKACCOUNT_ID}}"
```

## Subscriptions

To create a [Subscription](https://docs.stripe.com/subscriptions.md) using a saved `BankAccount`, you need to first record the mandate acknowledgement when confirming a `PaymentIntent` or `SetupIntent`. Then, either update your customer’s default payment method or set the `Subscription`’s `default_payment_method` parameter.

To update a customer’s default payment method:

#### Accounts v2

```curl
curl -X POST https://api.stripe.com/v2/core/accounts/{{CUSTOMERACCOUNT_ID}} \
  -H "Authorization: Bearer <<YOUR_SECRET_KEY>>" \
  -H "Stripe-Version: 2026-08-26.preview" \
  --json '{
    "configuration": {
        "customer": {
            "billing": {
                "default_payment_method": "{{BANKACCOUNT_ID}}"
            }
        }
    }
  }'
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/customers/{{CUSTOMER_ID}} \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "invoice_settings[default_payment_method]={{BANKACCOUNT_ID}}"
```

To create a subscription with a bank account as a payment method:

#### Accounts v2

```curl
curl https://api.stripe.com/v1/subscriptions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer_account={{CUSTOMERACCOUNT_ID}}" \
  -d "default_payment_method={{BANKACCOUNT_ID}}" \
  -d "items[0][price]={{PRICE_ID}}"
```

#### Customers v1

```curl
curl https://api.stripe.com/v1/subscriptions \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d "customer={{CUSTOMER_ID}}" \
  -d "default_payment_method={{BANKACCOUNT_ID}}" \
  -d "items[0][price]={{PRICE_ID}}"
```

## Identify Legacy ACH Payments 

On a [Charge](https://docs.stripe.com/api/charges/object.md) object, the `payment_method_details.type` property is `ach_debit` for the legacy integration and `us_bank_account` for the newer integration.

A legacy ACH payment is created when a legacy `BankAccount` is the payment [source](https://docs.stripe.com/api/charges/object.md#charge_object-source). This happens when:

- You call the [Create Charge API](https://docs.stripe.com/api/charges/create.md).
- A [Subscription](https://docs.stripe.com/billing/subscriptions/overview.md) or [Invoice](https://docs.stripe.com/invoicing/overview.md) charges a customer whose `default_source` is a legacy BankAccount, and no `default_payment_method` is set on the customer, subscription, or invoice.
- You call the Payment Intents API with a `payment_method_type` of `ach_debit`.
