Verify bank account ownership with Financial ConnectionsPrivate preview
Use the Ownership Match API to request match scores on ownership data.
The Ownership Match API helps you verify that your user owns a given bank account. It computes match scores by comparing Financial Connections ownership data with input owner information.
Before you begin
You must have a completed Financial Connections registration to access ownership in live mode. Visit your Dashboard settings to check the state of your registration or begin the registration process. Financial Connections test data is always available.
To access Ownership Match, you must set the API version and beta header to financial_
in each API request.
Request access to an account's ownership dataServer-side
You must collect a Financial Connections Account before you can access its ownership data.
Specify the data permissions you need access to with the permissions
parameter in the API. Ownership match requires the ownership
permission.
Your user can view the set of requested data permissions in the authentication flow.
We recommend that you use prefetch to initiate the ownership refresh as soon as your user connects their account in the authentication flow.
When the ownership data becomes available, we send a financial connections webhook.
Call the Ownership Match APIServer-side
You can pass ownership data to the API using one of the following methods:
- Create a Customer object for Stripe to compare against
- Collect a Financial Connections Account with an account holder of type Customer
- Submit the data through the
owner_
parameterinformation
Ownership match accepts up to four input fields, one of which must be from the following list.
- Name
- Email address
- Phone number: Include the plus symbol (
+
) and country code for international - Address: Requires all address fields except line 2
Note
Including all ownership fields increases the score accuracy. For example, you can weigh each field’s match score and use it in your risk modeling.
Create a Customer object Recommended
We recommend that you create a Customer to represent your user. Include all the data you have available that you want to match against.
Stripe loads and matches the data without requiring you to handle sensitive customer data when not strictly required.
For example, you can collect both a Customer and a Financial Connections Account through Stripe products, such as ACH payments in Checkout. After doing so, you can request an ownership match and make a risk decision based on the match result without seeing the customer’s ownership data, and possibly triggering PCI compliance liability.
This returns a Customer with the ownership details ownership match can use.
{ "id": "cus_NffrFeUfNV2Hib", "object": "customer", "created": 1651783000, "name": "Jenny Rosen", "address": { "line1": "354 Oyster Point Blvd", "line2": null, "city": "South San Francisco", "state": "CA", "postal_code": "94080", "country": "US" }, "email": "jennyrosen@example.com", "phone": "+1 212-555-5555", "livemode": false // ... }
Pass the Customer id
into the API to match against the Financial Connections Account.
Collect a Financial Connections Account with account holder of type Customer
You can collect a Financial Connections Account with an associated Customer by setting customer
to the existing Customer id, and setting its account holder to type customer
when creating a Financial Connections Session.
This creates a Financial Connections Account with the necessary ownership
permissions and an associated Customer. Successful prefetch or refresh of ownership returns the account, including its associated Financial Connections ownership data when ownership
is expanded.
{ "id": "fca_1MwVK82eZvKYlo2Cjw8FMxXf", "object": "financial_connections.account", "created": 1651783222, "account_holder": { "customer": "cus_NffrFeUfNV2Hib", "type": "customer" }, "ownership": { "id": "fcaowns_1NtI9uBHO5VeT9SUSRe21lqt", "object": "financial_connections.account_ownership", "created": 1651784999, "owners": { "object": "list", "data": [ { "name": "Jennifer Rosen", "email": "jennyrosen@test.com", "phone": null, "ownership": "fcaowns_1NtI9uBHO5VeT9SUSRe21lqt", "raw_address": "354 Oyster Point Blvd South San Francisco, CA 94080 USA", "refreshed_at": 1651784999 } ], "has_more": false, "url": "/v1/financial_connections/accounts/fca_zbyrdjTrwcYZJZc6WBs6GPid/owners?ownership=fcaowns_1NtI9uBHO5VeT9SUSRe21lqt" } }, "ownership_refresh": { "status": "succeeded", "last_attempted_at": 1651784999, "next_refresh_available_at": 1651785000 }, "permissions": ["ownership", "payment_method"], "prefetch": ["ownership"] // ... }
You can pass the Financial Connections Account id
without a specified input type
to match its Financial Connections ownership data with its associated account holder Customer data.
Submit the data through the owner information parameter
If you choose not to collect ownership data through a Customer, you can also pass user-provided ownership data directly to the API through the owner_
parameter.
{ "owner_information": { "name": "Jenny Rosen", "email": "jennyrosen@example.com", "phone": "+1 212-555-5555", "address": { "line1": "354 Oyster Point Blvd", "line2": null, "city": "South San Francisco", "state": "CA", "postal_code": "94080", "country": "US" } } }
Pass the owner information in the API to match against the Financial Connections Account.
Make decisions based on match resultsServer-side
The Ownership Match API returns a 0-100 match score for each input field you supply. You can feed this match score into your own risk logic and modeling, as shown in the examples provided. For example, you can set score thresholds in order to proceed with a payment, or to require more verification such as identity documentation upload before proceeding.
Depending on your risk tolerance and other risk signals from your customer data, you can adjust your logic to require an exact or strong match to proceed with an action, such as continuing a payment or allowing a payout to the account.
function makeRiskDecision(matchResult: Stripe.FinancialConnections.OwnershipMatch) { const nameScore = matchResult.results.name?.match_score? || 0; const addressScore = matchResult.results.address?.match_score? || 0; const emailScore = matchResult.results.email?.match_score? || 0; const phoneScore = matchResult.results.phone?.match_score? || 0; const overallScore = (nameScore + addressScore + emailScore + emailScore) / 4 if (overallScore >= 70) { proceedWithPayment(); } else if (overallScore >= 60) { stepUpVerification(); } else { cancelPayment(); } }
Examples of score interpretations
Name, Email, Address
Sample of match scores compared to the name “Jenny Jane Rosen”, the email “jennyrosen@example.com”, and the following address:
{ "line1": "354 Oyster Point Blvd", "line2": "Fl 1", "city": "South San Francisco", "state": "CA", "postal_code": "94080", "country": "US" }
Score | Interpretation | Name example | Email example | Address example |
---|---|---|---|---|
100 | Exact match | “Jenny Jane Rosen” | “jennyrosen@example.com” |
|
85-99 | Very strong match, such as a few characters off | “Jneny Jane Rosen” | “jenny.rosen@example.com” |
|
70-84 | Strong match, such as majority matches | “Jenny Rosen” | “jennyrosen12345@example.com” |
|
60-69 | Possible match, such as partial matches or nicknames | “Jennifer Rosen” | “jennyrosen@test.com” |
|
30-59 | Weak match, such as possible household member or similar fields | “Mark Rosen” | “marksmith@example.com” |
|
0-29 | No or unlikely match | “Mark Smith” | “marksmith@test.com” |
|
Phone
Sample of phone match scores compared to the phone number “+1 212-555-5555”.
Note
The phone sample interpretation score ranges are broader, because there are fewer granular variations in phone numbers.
Score | Interpretation | Examples |
---|---|---|
100 | Exact match | “+12125555555”, “(212) 555-5555” |
70-99 | Possible match, such as a digit off or a missing number | “212 555 5559” “212 555 555” |
50-69 | Weak match, such as all the same except area code | “347 555 5555” |
20-49 | Unlikely match, such as only the same area code | “212 999 9999” |
0-19 | No match | “347 999 9999” |
Sample response
The Ownership Match API also returns the matched owner information and ownership id, as shown in the following sample API response:
{ "id": "fcom_1NtI9uBHO5VeT9SUKLJU5suZ", "object": "financial_connections.ownership_match", "created": 1745858181, "financial_connections_account": "fca_1MwVK82eZvKYlo2Cjw8FMxXf", "type": "customer", "owner_information": { "name": "Jenny Rosen", "address": { "line1": "354 Oyster Point Blvd", "line2": null, "city": "South San Francisco", "state": "CA", "postal_code": "94080", "country": "US" }, "email": "jennyrosen@example.com", "phone": "+1 212-555-5555" }, "customer": "cus_NffrFeUfNV2Hib", "ownership": "fcaowns_1NtI9uBHO5VeT9SUSRe21lqt", "results": { "name": { "match_score": 85, "missing_data": false }, "address": { "match_score": 100, "missing_data": false }, "email": { "match_score": 65, "missing_data": false }, "phone": { "match_score": null, "missing_data": true } } }