# Collect an account to build data-powered products
Collect your user's account and use data such as balances, ownership details, and transactions to build products.
Available in: US
Not sure about which Financial Connections integration to use? See our [overview of integration options](https://docs.stripe.com/financial-connections/use-cases.md).
Financial Connections lets your users securely share their financial data by linking their external financial accounts to your business. You can use Financial Connections to access user-permissioned financial data such as tokenized account and routing numbers, account balances, account owner information, and historical transactions.
Some common examples of how you can use Financial Connections to improve product experiences for your users include:
- Mitigate fraud when onboarding a customer or business by verifying the [ownership](https://docs.stripe.com/financial-connections/ownership.md) information of an account, such as the name and address of the bank account holder.
- Help your users track expenses, handle bills, manage their finances and take control of their financial well-being with [transactions](https://docs.stripe.com/financial-connections/transactions.md) data.
- Speed up underwriting and improve access to credit and other financial services with transactions and balances data.
- Enable your users to connect their accounts in fewer steps with Link, allowing them to save and reuse their bank account details across Stripe businesses.
## Set up Stripe [Server-side] [Client-side]
### Server-side
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
#### Ruby
```bash
# Available as a gem
sudo gem install stripe
```
```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```
### Client-side
The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-android) SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
#### yarn
```bash
yarn add @stripe/stripe-react-native
```
#### npm
```bash
npm install @stripe/stripe-react-native
```
Next, install some other necessary dependencies:
- For iOS, go to the **ios** directory and run `pod install` to ensure that you also install the required native dependencies.
- For Android, there are no more dependencies to install.
> We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) to add TypeScript support.
### Stripe initialization
To initialize Stripe in your React Native app, either wrap your payment screen with the `StripeProvider` component, or use the `initStripe` initialization method. Only the API [publishable key](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialize Stripe using the `StripeProvider` component.
```jsx
import { useState, useEffect } from 'react';
import { StripeProvider } from '@stripe/stripe-react-native';
function App() {
const [publishableKey, setPublishableKey] = useState('');
const fetchPublishableKey = async () => {
const key = await fetchKey(); // fetch key from your server here
setPublishableKey(key);
};
useEffect(() => {
fetchPublishableKey();
}, []);
return (
{/* Your app code here */}
);
}
```
> Use your API [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app.
## Create or retrieve a customer [Server-side]
Create a customer-configured [Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) or [Customer](https://docs.stripe.com/api/customers/create.md) when users create an account with your business. Financial Connections can use the email address to identify returning [Link](https://support.stripe.com/questions/link-for-financial-connections-support-for-businesses) users and optimize the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow) by dynamically showing a streamlined user interface.
> #### Use the Accounts v2 API to represent customers
>
> The Accounts v2 API is generally available for Connect users, and in public preview for other Stripe users. If you’re part of the Accounts v2 preview, you need to specify a [specify a preview version](https://docs.stripe.com/api-v2-overview.md#sdk-and-api-versioning) in your code.
>
> To request access to the Accounts v2 preview,
>
> For most use cases, we recommend [modeling your customers as customer-configured Account objects](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md) instead of using [Customer](https://docs.stripe.com/api/customers.md) objects.
#### Accounts v2
```curl
curl -X POST https://api.stripe.com/v2/core/accounts \
-H "Authorization: Bearer <>" \
-H "Stripe-Version: 2026-05-27.preview" \
--json '{
"contact_email": "{{CUSTOMER_EMAIL}}",
"display_name": "{{CUSTOMER_NAME}}",
"configuration": {
"customer": {}
},
"include": [
"configuration.customer"
]
}'
```
#### Customers v1
```curl
curl https://api.stripe.com/v1/customers \
-u "<>:" \
-d email={{CUSTOMER_EMAIL}} \
-d name={{CUSTOMER_NAME}}
```
## Create a Financial Connections Session [Server-side]
> You can find a running implementation of this endpoint [available on Glitch](https://glitch.com/edit/#!/remix/stripe-mobile-connections-example) for quick testing.
Before you can retrieve data from a user’s bank account with Financial Connections, your user must authenticate their account with the [authentication flow](https://docs.stripe.com/financial-connections/fundamentals.md#authentication-flow).
Your user begins the authentication flow when they want to connect their account to your site or application. Insert a button or link on your site or in your application, which allows a user to link their account—for example, your button might say “Link your bank account”.
Create a Financial Connections Session by posting to `/v1/financial_connections/sessions`:
#### Accounts v2
```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
-u "<>:" \
-d "account_holder[type]=customer" \
-d "account_holder[customer_account]={{CUSTOMERACCOUNT_ID}}" \
-d "permissions[]=balances" \
-d "permissions[]=ownership" \
-d "permissions[]=payment_method" \
-d "permissions[]=transactions"
```
#### Customers v1
```curl
curl https://api.stripe.com/v1/financial_connections/sessions \
-u "<>:" \
-d "account_holder[type]=customer" \
-d "account_holder[customer]={{CUSTOMER_ID}}" \
-d "permissions[]=balances" \
-d "permissions[]=ownership" \
-d "permissions[]=payment_method" \
-d "permissions[]=transactions"
```
1. Use the customer’s ID to set [account_holder.customer_account](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-account_holder-customer_account) (for customer-configured `Account` objects) or [account_holder.customer](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-account_holder-customer) (for `Customer` objects).
2. Add the data required by your use case to the [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) array.
3. (Optional) Set the [prefetch](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) parameter for retrieving the data on account creation.
The [permissions](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-permissions) parameter controls which account data you can access. You must request at least one permission. When completing the authentication flow, your user can see the data you’ve requested access to, and provide their consent to share it.
Consider the data required to fulfill your use case and request permission to access only the data you need. Requesting permissions that go well beyond your application’s scope might erode your users’ trust in how you use their data. For example, if you’re building a personal financial management application or a lending product, you might request `transactions` data. If you’re mitigating fraud such as account takeovers, you might want to request `ownership` details.
After your user authenticates their account, you can expand data permissions only by creating a new Financial Connections Session and specifying a new value for the `permissions` parameter. Your user must complete the authentication flow again, where they’ll see the additional data you’ve requested permission to access, and provide consent to share their data.
The optional [prefetch parameter](https://docs.stripe.com/api/financial_connections/sessions/create.md#financial_connections_create_session-prefetch) controls which data you retrieve immediately after the user connects their account. Use this option if you know you always want a certain type of data. It removes the need to make an extra API call to initiate a [data refresh](https://docs.stripe.com/api/financial_connections/accounts/refresh.md).
To preserve the option to [accept ACH Direct Debit payments](https://docs.stripe.com/financial-connections/other-data-powered-products.md#accept-ach-direct-debit), request the `payment_method` permission.
## Collect a Financial Connections account [Client-side]
Import the `collectFinancialConnectionsAccounts` function from Stripe’s React Native SDK.
```javascript
import {collectFinancialConnectionsAccounts} from '@stripe/stripe-react-native';
```
Use `collectFinancialConnectionsAccounts` to collect the bank account by passing in the `client_secret` from above, and then handle the result appropriately:
```javascript
// Assume you have a to collect payout accounts, whose onPress is handleCollectPress
const handleCollectPress = async () => {
// Fetch the clientSecret you created above and pass it to collectFinancialConnectionsAccounts
const {session, error} = await collectFinancialConnectionsAccounts(
clientSecret,
);
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else {
Alert.alert('Success');
console.log(
'Successfully obtained session: ',
JSON.stringify(session, null, 2),
);
}
};
```
`collectFinancialConnectionsAccounts` returns a `Promise`. When your user completes the modal flow, the `Promise` resolves with one of the following:
- A `session`, representing the completed Financial Connections Session, if the user can successfully link their account. This `session` includes an `accounts` array that contains the list of connected accounts.
- An `error` if the Session fails or is canceled.
By default your connected accounts can only add account types like a checking or savings account in the authentication flow because Stripe can only facilitate payouts to an ACH-enabled account.
## Optional: Customize the bank account collector [Client-side]
### Dark mode
By default, the bank account collector automatically switches between light and dark mode compatible colors based on device settings. If your app doesn’t support dark or light mode, you can set `style` to `alwaysLight` or `alwaysDark`, respectively.
```javascript
const {session, error} = await collectFinancialConnectionsAccounts(
clientSecret,
{
style: 'alwaysLight',
},
);
```
To achieve an always light or always dark appearance in your Android app, make use of Android’s `AppCompatDelegate`. Our UI automatically respects this setting.
#### Kotlin
```kotlin
// force dark
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
// force light
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
```
## Set up a return URL (iOS only) [Client-side]
When a customer exits your app (for example to authenticate in Safari or their banking app), provide a way for them to automatically return to your app. Many payment method types *require* a return URL. If you don’t provide one, we can’t present payment methods that require a return URL to your users, even if you’ve enabled them.
To provide a return URL:
1. [Register](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app#Register-your-URL-scheme) a custom URL. Universal links aren’t supported.
2. [Configure](https://reactnative.dev/docs/linking) your custom URL.
3. Set up your root component to forward the URL to the Stripe SDK as shown below.
> If you’re using Expo, [set your scheme](https://docs.expo.io/guides/linking/#in-a-standalone-app) in the `app.json` file.
```jsx
import { useEffect, useCallback } from 'react';
import { Linking, View } from 'react-native';
import { useStripe } from '@stripe/stripe-react-native';
export default function MyApp() {
const { handleURLCallback } = useStripe();
const handleDeepLink = useCallback(
async (url: string | null) => {
if (url) {
const stripeHandled = await handleURLCallback(url);
if (stripeHandled) {
// This was a Stripe URL - you can return or add extra handling here as you see fit
} else {
// This was NOT a Stripe URL – handle as you normally would
}
}
},
[handleURLCallback]
);
useEffect(() => {
const getUrlAsync = async () => {
const initialUrl = await Linking.getInitialURL();
handleDeepLink(initialUrl);
};
getUrlAsync();
const deepLinkListener = Linking.addEventListener(
'url',
(event: { url: string }) => {
handleDeepLink(event.url);
}
);
return () => deepLinkListener.remove();
}, [handleDeepLink]);
return (
);
}
```
For more information on native URL schemes, refer to the [Android](https://developer.android.com/training/app-links/deep-linking) and [iOS](https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) docs.
## Retrieve data on a Financial Connections account [Server-side]
After your user has successfully completed the authentication flow, access or refresh the account data you’ve specified in the `permissions` parameter of the Financial Connections Session.
To protect the privacy of your user’s data, account data accessible to you is limited to the data you’ve specified in the `permissions` parameter.
Follow the guides for [balances](https://docs.stripe.com/financial-connections/balances.md), [ownership](https://docs.stripe.com/financial-connections/ownership.md) and [transactions](https://docs.stripe.com/financial-connections/transactions.md) to start retrieving account data.
## Optional: Accept an ACH Direct Debit payment from a Financial Connections account
You can optionally accept ACH Direct Debit payments on a previously collected Financial Connections account as long as the `supported_payment_method_types` attribute on the account includes `us_bank_account`.
Only US bank accounts are eligible to accept ACH Direct Debits such as a checking or savings account.
To accept an ACH Direct Debit payment on a previously collected account, you must have specified `payment_method` in the [data permissions](https://docs.stripe.com/financial-connections/fundamentals.md#data-permissions) parameter on the Financial Connections Session.
### Create a Payment Intent or Setup Intent
Use the *Payment Intents API* (The Payment Intents API tracks the lifecycle of a customer checkout flow and triggers additional authentication steps when required by regulatory mandates, custom Radar fraud rules, or redirect-based payment methods) to accept an ACH Direct payment for an account you want to charge now. Use the *Setup Intents API* (The Setup Intents API lets you build dynamic flows for collecting payment method details for future payments. It tracks the lifecycle of a payment setup flow and can trigger additional authentication steps if required by law or by the payment method) to save details for future ACH Direct Debit payments. [Learn more about the difference between a](https://support.stripe.com/questions/payment-intents-api-vs-setup-intents-api) Payment Intent and Setup Intent.
This path shows you how to accept an ACH Direct Debit payment using the Payment Intents API.
Use the Financial Connections account ID and the customer ID to create a `PaymentIntent`:
#### Accounts v2
```curl
curl https://api.stripe.com/v1/payment_intents \
-u "<>:" \
-d "customer_account={{CUSTOMERACCOUNT_ID}}" \
-d "payment_method_types[]=us_bank_account" \
-d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
-d "payment_method_data[type]=us_bank_account" \
-d "payment_method_data[billing_details][name]=J. Customer" \
-d amount=100 \
-d currency=usd
```
#### Customers v1
```curl
curl https://api.stripe.com/v1/payment_intents \
-u "<>:" \
-d "customer={{CUSTOMER_ID}}" \
-d "payment_method_types[]=us_bank_account" \
-d "payment_method_data[us_bank_account][financial_connections_account]={{FINANCIALCONNECTIONSACCOUNT_ID}}" \
-d "payment_method_data[type]=us_bank_account" \
-d "payment_method_data[billing_details][name]=J. Customer" \
-d amount=100 \
-d currency=usd
```
This returns a `PaymentIntent` similar to:
```json
{
"id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
"object": "payment_intent",
"amount": 100,
"amount_capturable": 0,
"amount_details": {
"tip": {
"amount": null
}
},
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
},
"client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
"confirmation_method": "automatic",
"created": 1651010665,
"currency": "usd",
"customer": null,
"description": null,
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {
},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
"payment_method_options": {
"us_bank_account": {
"verification_method": "automatic"
}
},
"payment_method_types": [
"us_bank_account"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "requires_confirmation",
"transfer_data": null,
"transfer_group": null
}
```
### Collect mandate acknowledgement and submit the payment
Before you can initiate the payment, you must obtain authorization from your customer by displaying mandate terms for them to accept.
To be compliant with Nacha rules, you must obtain authorization from your customer before you can initiate payment by displaying mandate terms for them to accept. For more information on mandates, see [Mandates](https://docs.stripe.com/payments/ach-direct-debit.md#mandates).
### Confirm the Payment Intent
To confirm this Payment Intent, you need to provide either a [`mandate` parameter](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate) with an existing Mandate ID, or provide [mandate_data](https://docs.stripe.com/api/payment_intents/confirm.md#confirm_payment_intent-mandate_data) to create a new mandate.
For example, if you have a checkout page with a “Place Order” button, clicking that button could trigger a `POST` to an endpoint on your server. That endpoint could extract the request’s User Agent and the client’s IP address, and then make the following request to Stripe’s API:
```curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENTINTENT_ID}}/confirm \
-u "<>:" \
-d "mandate_data[customer_acceptance][accepted_at]=1647448692" \
-d "mandate_data[customer_acceptance][type]=online" \
-d "mandate_data[customer_acceptance][online][ip_address]=71.183.194.54" \
--data-urlencode "mandate_data[customer_acceptance][online][user_agent]=Mozilla/5.0 ..."
```
After successfully confirming the Payment Intent, it will look similar to:
```json
{
"id": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
"object": "payment_intent",
"amount": 100,
"amount_capturable": 0,
"amount_received": 0,
"application": null,
"application_fee_amount": null,
"automatic_payment_methods": null,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"charges": {
"object": "list",
"data": [
{
"id": "py_17F8CPDyDglZKgWE3uzOAUe9",
"object": "charge",
"amount": 100,
"amount_captured": 100,
"amount_refunded": 0,
"application": null,
"application_fee": null,
"application_fee_amount": null,
"balance_transaction": null,
"billing_details": {
"address": {
"city": null,
"country": null,
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": null,
"name": "J. Customer",
"phone": null
},
"calculated_statement_descriptor": null,
"captured": true,
"created": 1647448692,
"currency": "usd",
"customer": "cus_LKe65xcPnrCiTZ",
"description": null,
"destination": null,
"dispute": null,
"disputed": false,
"failure_code": null,
"failure_message": null,
"fraud_details": {
},
"invoice": null,
"livemode": false,
"metadata": {
},
"on_behalf_of": null,
"order": null,
"outcome": null,
"paid": false,
"payment_intent": "pi_1GszXf2eZvKYlo2Ce7rjvnPP",
"payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
"payment_method_details": {
"type": "us_bank_account",
"us_bank_account": {
"account_holder_type": "individual",
"account_type": "checking",
"bank_name": "STRIPE TEST BANK",
"fingerprint": "QnXqpAeqjjh8pPFa",
"last4": "6789",
"routing_number": "110000000"
}
},
"receipt_email": null,
"receipt_number": null,
"receipt_url": null,
"refunded": false,
"refunds": {
"object": "list",
"data": [
],
"has_more": false,
"total_count": 0,
"url": "/v1/charges/py_17F8CPDyDglZKgWE3uzOAUe9/refunds"
},
"review": null,
"shipping": null,
"source": null,
"source_transfer": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "pending",
"transfer_data": null,
"transfer_group": null
}
],
"has_more": false,
"total_count": 1,
"url": "/v1/charges?payment_intent=pi_1GszXf2eZvKYlo2Ce7rjvnPP"
},
"client_secret": "pi_1GszXf2eZvKYlo2Ce7rjvnPP_secret_m38BKZvm3vSdpGdra360hPEov",
"confirmation_method": "automatic",
"created": 1647447792,
"currency": "usd",
"customer": "cus_LKe65xcPnrCiTZ",
"description": null,
"invoice": null,
"last_payment_error": null,
"livemode": false,
"metadata": {
},
"next_action": null,
"on_behalf_of": null,
"payment_method": "pm_1Mb4UkJGNKiWrCEmJ1PS72Cd",
"payment_method_options": {
"us_bank_account": {
"verification_method": "automatic"
}
},
"payment_method_types": [
"us_bank_account"
],
"processing": null,
"receipt_email": null,
"review": null,
"setup_future_usage": null,
"shipping": null,
"source": null,
"statement_descriptor": null,
"statement_descriptor_suffix": null,
"status": "processing",
"transfer_data": null,
"transfer_group": null
}
```
ACH Direct Debit is a *delayed notification payment method* (A payment method that can't immediately return payment status when a customer attempts a transaction (for example, ACH debits). Businesses commonly hold an order in a pending state until payment is successful with these payment methods). This means that it can take up to four business days to receive notification of the success or failure of a payment after you initiate a debit from your customer’s account.
The PaymentIntent you create initially has a status of `processing`. After the payment succeeds, the PaymentIntent status is updated from `processing` to `succeeded`. Learn about the events that are sent when the [PaymentIntent status is updated](https://docs.stripe.com/payments/ach-direct-debit/accept-a-payment.md#web-confirm-paymentintent-succeeded).