Embedded Finance integration guide
Build a US embedded financial services offering using Stripe Issuing and Treasury. Use Issuing to create cards, and Treasury to store balances and fund card spend.
By the end of this guide, you’ll know how to:
- Create verified connected accounts representing your business customers with relevant Issuing and Treasury capabilities
- Create financial accounts that you can use as a wallet for your business customers and add funds to using an external bank account
- Create virtual cards for your business customers and use these cards to spend funds from a wallet
Before you begin
- Sign up for a Stripe account.
- Activate Issuing and Treasury in test mode from the Dashboard. For more information, see API access to Issuing and Treasury.
- Configure your Connect platform branding settings for your business and add an icon.
Create connected accounts
Create a connected account
Create a connected account to represent a business customer of your platform. For example, if your product is a SaaS platform for restaurants, each restaurant would be represented as a connected account
Connect account types
Issuing and Treasury only support custom Connect account types. If you’re an existing platform on standard or express account types, you need to migrate to custom Connect.
The following request creates a new US-based custom connected account and requests the requisite capabilities:
The user’s account information appears in the response:
{ ... "id": "{{CONNECTED_ACCOUNT_ID}}", "type": "custom" ...` }
Note the connected account’s id
. You’ll provide this value to authenticate as the connected account by passing it into requests in the Stripe-Account
header. If a connected account already exists, you can add the requisite capabilities by specifying the connected account id
in the API request:
Verify the connected account
After you create a connected account, Stripe needs to verify you before you can use Issuing and Treasury. The easiest way to collect this information is to integrate Hosted Connect Onboarding, which lets Stripe take care of the verification complexity. To use Hosted Onboarding, make a call to Account Links to retrieve a URL that your customer can use to submit onboarding information for the Treasury financial account:
The response includes a URL the customer uses to access the application, which they must do before the link expires:
{ "object": "account_link", "created": 1612927106, "expires_at": 1612927406, "url": "https://connect.stripe.com/setup/s/iCtLfmYb2tEU" }
Give the link to the connected account to have them complete onboarding. As part of the Hosted Connect Onboarding process, each connected account is given the relevant terms, and their acceptance is recorded. It’s necessary to complete all onboarding steps to fully enable the required capabilities.
Using Custom Connect onboarding
You can also use Custom Connect onboarding to build your own verification process. This approach requires you to write your own API calls for the initial integration. You must also continue to present and record the acceptance of relevant terms and to check for regulation changes that could impact onboarding requirements. To learn more about Custom Connect onboarding, see Required verification information.
Listen for the account.updated
webhook to confirm the following fields and capabilities on the connected account:
{ "object": { "id": "{{CONNECTED_ACCOUNT_ID}}", "object": "account", "capabilities": { "treasury": "active", "card_issuing": "active", // Only appears if requesting the `card_issuing` capability. "us_bank_account_ach_payments": "active", // Only appears if requesting the `us_bank_account_ach_payments` capability. }, ... } }
At this point, Stripe has created and verified the connected account with active
relevant capabilities to use Issuing and Treasury.
To learn more, see:
Create financial accounts and add funds
After you enable Treasury on your platform, add FinancialAccount objects to your platform architecture to enable the efficient storing, sending, and receiving of funds. Stripe attaches a financial account to your platform account after enablement, and lets you provision an individual financial account for each eligible custom connected account on your platform.
In the Stripe API, FinancialAccount
objects serve as the source and destination of money movement API requests. You request Features
through the API to assign to FinancialAccounts
that provide additional functionality for the financial accounts on your platform.
A financial account operates a distinct balance of funds from the connected account payments balance of the account it’s linked to. For example, the owner of a custom connected account on your platform might have a 100 USD connected account balance and a 200 USD financial account balance. In this scenario, the connected account owner has a sum of 300 USD spread between their financial account and connected account balances. These two balances remain separate, but the API provides the ability to move money from the connected account balance to the financial account balance.
Multiple financial accounts
The multiple financial account beta feature enables you to open multiple financial accounts for a single custom connected account. Contact treasury-support@stripe.com to access test mode for this feature and join the wait list.
Create a Financial Account
After Stripe adds the treasury
capability to an account and it’s marked active
, you can create a FinancialAccount
object for the connected account. To do this, call FinancialAccounts
and request the Features
you want to provide:
The response, when you request features on financial account creation, indicates their status in the active_features
, pending_features
, and restricted_features
parameters:
{ "object": "treasury.financial_account", "created": 1612927106, "id": "fa_123", "country": "US", "supported_currencies": ["usd"], "active_features": ["card_issuing"], "pending_features": ["financial_addresses.aba"], "restricted_features": [], // No FinancialAddress added as the financial_addresses.aba feature is not yet active "financial_addresses": [], "livemode": true, "status": "open", ... }
Activation might be instantaneous for some features (for example, card_issuing
). However, other features, like financial_addresses.aba
, activate asynchronously, might stay pending
for up to 30 minutes while Stripe communicates with external systems. After all of the relevant features are active, you get confirmation on the treasury.financial_account.features_status_updated
webhook listener. See Available features for more information on financial account features.
Link a bank account
To let your customers transfer money to and from an external account, create a SetupIntent
with the required parameters and attach it to self
to denote that the external account is owned by your customer:
The API response includes a unique identifier for the payment_method
that’s used to reference this bank account when making ACH transfers:
{ "id": "{{SETUP_INTENT_ID}}", "object": "setup_intent", "next_action": { "type": "verify_with_microdeposits", "verify_with_microdeposits": { "arrival_date": 1642579200, "hosted_verification_url": "https://payments.stripe.com/microdeposit/sacs_test_xxx", "microdeposit_type": "amounts" } }, ... "payment_method": "{{PAYMENT_METHOD_ID}}", "payment_method_types": [ "us_bank_account" ] }
Before you can use a bank account, it must be verified using microdeposits (which we focus on here) or the faster financial connections option. The SetupIntent
response from the previous step includes a hosted_verification_url
which you must present to your customer for them to input the associated descriptor code of the microdeposit. Use the value SM11AA
to verify the bank account, or test a variety of other cases by using the test account numbers Stripe provides.
Add funds to the financial account
The following request transfers 200 USD using an account-attached
payment method into the financial account with the provided ID. The Stripe-Account
header value identifies the Stripe account that owns both the financial account and the payment method:
If successful, the response provides the InboundTransfer
object. The object includes a hosted_regulatory_receipt_url
that provides access to details of the transaction for the account holder on the Homebox platform:
{ "id": "{{INBOUND_TRANSFER_ID}}", "object": "inbound_transfer", "amount": 20000, "created": 1648071297, "currency": "usd", "description": "Funds for repair", "financial_account": "{{FINANCIAL_ACCOUNT_ID}}", "hosted_regulatory_receipt_url": "https://payments.stripe.com/regulatory-receipt/{{IBT_URL}}", "linked_flows": null, "livemode": false, "metadata": {}, "origin_payment_method": "{{PAYMENT_METHOD_ID}}", ... "statement_descriptor": "Invoice 12", "status": "processing", ... }
At this point, the connected account has a FinancialAccount
that has been loaded with funds received from an InboundTransfer
that you can spend using cards or OutboundPayments
like ACH or wires.
To learn more, see:
- Getting permissions for InboundTransfers
- Working with Treasury financial accounts
- Using Treasury to move money
- Requesting features on a Financial Account
- Working with SetupIntents, PaymentMethods, and BankAccounts
- Moving money with Treasury using InboundTransfer objects
- Moving money with Treasury using ReceivedCredit objects
Create cardholders and cards
Create a cardholder
The Cardholder is the individual (that is, employee or contractor) that’s authorized by your business customer to use card funding by the FinancialAccount
. The Cardholder
object includes relevant details, such as a name to display on cards and a billing address, which is usually the business address of the connected account or your platform.
The following API call creates a new Cardholder
:
Stripe returns a Cardholder
object that contains the information you provided and sends the issuing_cardholder.created
webhook event.
Create a card
Create a card and attach it to the Cardholder
that you want to make the authorized user of the card, and the FinancialAccount
that will be used to support card spend.
In the following examples, we show you how to create a virtual card. You can, however, create physical cards and ship them to cardholders in live mode.
Stripe returns a Card
object on creation, and sends the issuing_card.created
webhook event:
{ "id": "ic_1NvPjF2SSJdH5vn2OVbE7r0b", "object": "issuing.card", "brand": "Visa", ... "status": "inactive", "type": "virtual" }
You need to activate the card before a user can use it. While you can activate virtual cards in the same API call you used to create it, physical cards must be activated separately. When ready, activate the card by marking the status
as active
:
At this point, there’s now an active card attached to a cardholder and financial account. See the Issuing page for the connected account to view the card and cardholder information.
{ "id": "ic_1NvPjF2SSJdH5vn2OVbE7r0b", "object": "issuing.card", "brand": "Visa", ... "status": "active", "type": "virtual", }
To learn more, see:
Use the card
Create an authorization
To observe the impact of card activity on the financial account, generate a test authorization. You can do this in the Issuing page of the Dashboard for the connected account, or with the following call to the Authorization API:
After approval, Stripe creates an Authorization
in a pending
state while it waits for capture. Note the authorization id
that you’ll use to capture the funds:
{ "id": "iauth_1NvPyY2SSJdH5vn2xZQE8C7k", "object": "issuing.authorization", "amount": 1000, ... "status": "pending", "transactions": [], }
You can use retrieve the balance details of the financial account and see the impact of the authorization:
The API response is a FinancialAccount
object with a balance
hash that details the funds and their availability:
{ "object": "treasury.financial_account", "id": "{{FINANCIAL_ACCOUNT_ID}}", ... "balance": { "cash": {"usd": 19000}, "inbound_pending": {"usd": 0}, "outbound_pending": {"usd": 1000} } }
The response indicates 190 USD is currently available for use with an additional 10 USD held in outbound_pending
from the pending
authorization. You can now simulate capture of the authorization with the API.
Capture the funds
Capture the funds using the following code:
After the authorization is captured, Stripe creates an Issuing Transaction, the status
of the authorization is set to closed
, and a ReceivedDebit
webhook is created with these details. Retrieving the balance details of the financial account again shows the outbound_pending
is now 0 USD while the available cash is remains 190 USD:
{ "object": "treasury.financial_account", "id": "{{FINANCIAL_ACCOUNT_ID}}", ... "balance": { "cash": {"usd": 19000}, "inbound_pending": {"usd": 0}, "outbound_pending": {"usd": 0} } }