Using Connect with Express Accounts

    Express enables your platform to manage payout schedules, customize the flow of funds, and control branding. Stripe will handle onboarding, account management, and identity verification for your platform.

    With Express accounts, you can quickly onboard users so they can be paid immediately. You can customize the branding of the Express onboarding flow and dashboard. Stripe hosts the dashboard; you can grant access through your platform so your users can access it.

    Express demo

    To see the complete Express onboarding flow in action, Stripe recommends trying our sample end-to-end Express integration before you start building your own. This demo includes an example of a user onboarding experience and account management for Rocket Rides, an on-demand marketplace.

    You can find the demo’s complete source code on GitHub.

    Prerequisites for using Express

    To create Express accounts, you must meet all of these requirements:

    • Minimum API version: Express requires the API version 2017-05-25 or later. Capabilities in Express require the API version 2019-02-19 or later.
    • Phone number: To authenticate with Express, U.S. users need to have an SMS-enabled U.S. phone number. Users outside the U.S. need any SMS-enabled phone number.
    • Platform in a supported country: Platforms in Australia, Austria, Belgium, Canada, Denmark, Estonia, Finland, France, Germany, Greece, Hong Kong, Ireland, Italy, Japan, Latvia, Lithuania, Luxembourg, the Netherlands, New Zealand, Norway, Poland, Portugal, Singapore, Slovakia, Slovenia, Spain, Sweden, Switzerland, the United Kingdom, and the United States can create Express accounts for any country Stripe supports.
    • Vetting for fraud: Your platform is ultimately responsible for losses incurred by Express accounts. To best protect against this, you need to scrutinize all accounts that sign up via your platform for potential fraud. Refer to our best practices guide for more information.
    • Platform profile: You need to complete your platform’s profile.

    Note there’s an additional cost for active Express accounts. An Express account is considered active if it has received at least one successful payout in a given month.

    Onboarding accounts outside of your platform’s country with Express

    You can enable onboarding on a per-country basis in the in the Connect Settings section of your dashboard.

    The Express account onboarding flow is currently localized in English, Spanish, French, German, Italian, Japanese, Simplified Chinese, Traditional Chinese, Danish, Finnish, Norwegian, Portuguese, Swedish, and Polish.

    Keep these things in mind when onboarding accounts globally:

    • International business: Your platform is responsible for understanding the implications of doing business internationally, such as tax and financial reporting.
    • Charge flows: Be sure to review your options for creating charges based on the countries you intend to operate in.

    The OAuth connection flow

    A user connects to your platform using the following OAuth connection flow:

    1. Starting on a page at your site, the user clicks a link that takes them to Stripe, passing along your platform’s client_id.
    2. On Stripe’s website, the user provides the necessary information for connecting to your platform.
    3. The user is then redirected back to your site along with an authorization code.
    4. Your site then makes a request to Stripe’s OAuth token endpoint to complete the connection and fetch the user’s account ID.

    When these steps are done, API requests can be made on behalf of the user with their account ID or authorization credentials.

    Step 1: Create the OAuth link

    To start your integration, you need two pieces of information from your platform settings:

    • Your client_id, a unique identifier for your platform that is generated by Stripe.
    • Your redirect_uri, the URL which your user will be redirected to after connecting their account. If you do not include the redirect_uri parameter in your request, Stripe defaults to using the first address you have configured as the redirect destination in your Dashboard.

    Stripe also provides a development client_id to make testing easier.

    With these two pieces of information in hand, you’re ready to create the OAuth link. We recommend showing a Connect button that sends users to our Express OAuth endpoint:

    https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}

    To prevent CSRF attacks, add the state parameter with the value set to a unique token. Stripe includes this state value in the redirect URL that sends the user back to your site.

    Here’s how the example URL can be presented:

    Connect with Stripe

    Customize Express with OAuth parameters

    You can change the behavior of the Express onboarding flow by including additional URL parameters in your OAuth link. A complete list of available parameters is available in the OAuth Reference Guide.

    Individual or company accounts

    You can specify whether Stripe should present an Express onboarding form for individuals or companies by setting the stripe_user[business_type] parameter to either individual or company.

    Stripe will collect the right information for each account type. For example, to onboard a company:

    https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}&stripe_user[business_type]=company

    Prefill form fields

    You can prefill some form fields on the user’s Stripe application by including the relevant URL parameters in your OAuth link.

    This example prefills the user’s email address with stripe_user[email]:

    https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}&stripe_user[email]=user@example.com

    Specify capabilities for an account

    If your platform is designated for one capability (either card_payments or transfers), you won’t need to specify additional capabilities. However, if your platform supports both, you can add a capability to an individual Express account by including the suggested_capabilities[] parameter in your OAuth link.

    An example with the transfers capability:

    https://connect.stripe.com/express/oauth/authorize?redirect_uri=https://connect.stripe.com/connect/default/oauth/test&client_id=ca_32D88BD1qLklliziD7gYQvctJIhWBSQ7&state={STATE_VALUE}&suggested_capabilities[]=transfers

    Stripe adds the suggested capability to this Express account, unless one of the following conditions is met:

    • If the user is in a country that doesn’t support transfers, Stripe attempts to designate the account as card_payments.
    • If transfers and card_payments are both not supported for the user, the connected account is marked as having no capabilities.

    You can verify that Stripe added the suggested capability by using the assert_capabilities[] parameter. This step is optional.

    Step 2: User creates or connects their account

    After the user clicks the link on your site, they are taken to Stripe’s website where they are prompted to provide contact and payout information.

    To test the onboarding process, you can provide (000) 000-0000 as a phone number. Instead of sending you an SMS message or e-mail, Stripe lets you complete verification with the code 000-000.

    Express displays your branding in the onboarding flow and the Express dashboard. You can provide your platform name, logo, and optional brand color in the Connect settings section of the Stripe Dashboard.

    Step 3: User is redirected back to your site

    After the user completes the onboarding process, they are redirected back to your site using the URL defined as your platform’s redirect_uri.

    For successful connections, the following values are passed back in the redirect URL:

    • The state value, if provided
    • An authorization code
    https://connect.stripe.com/connect/default/oauth/test?code={AUTHORIZATION_CODE}

    Step 4: Platform completes the Express account connection

    Include the provided authorization code in a POST request to Stripe’s token endpoint to complete the connection and fetch the user’s account ID:

    curl https://connect.stripe.com/oauth/token \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d code=ac_123456789 \
      -d grant_type=authorization_code
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    response = Stripe::OAuth.token({
      grant_type: 'authorization_code',
      code: 'ac_123456789',
    })
    
    # Access the connected account id in the response
    connected_account_id = response.stripe_user_id
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    $response = \Stripe\OAuth::token([
      'grant_type' => 'authorization_code',
      'code' => 'ac_123456789',
    ]);
    
    // Access the connected account id in the response
    $connected_account_id = $response->stripe_user_id;
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    response = stripe.OAuth.token(
      grant_type='authorization_code',
      code='ac_123456789',
    )
    
    # Access the connected account id in the response
    connected_account_id = response.stripe_user_id
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    stripe.oauth.token({
      grant_type: 'authorization_code',
      code: 'ac_123456789',
    }).then(function(response) {
      // asynchronously called
      var connected_account_id = response.stripe_user_id;
    });
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    Map<String, Object> params = new HashMap<>();
    params.put("grant_type", "authorization_code");
    params.put("code", "ac_123456789");
    
    TokenResponse response = OAuth.token(params, null);
    
    // Access the connected account id in the response
    String accountId = response.getStripeUserId();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
    
    params := &stripe.OAuthTokenParams{
      GrantType: stripe.String("authorization_code"),
      Code:      stripe.String("ac_123456789"),
    }
    token, _ := oauth.New(params)
    
    // Access the connected account id in the response
    connected_account_id = token.StripeUserID
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var options = new OAuthTokenCreateOptions
    {
      GrantType = "authorization_code",
      Code = "ac_123456789",
    };
    
    var service = new OAuthTokenService();
    var response = service.Create(options);
    
    // Access the connected account id in the response
    connected_account_id = response.StripeUserId
    

    Stripe returns a response containing the account ID (stripe_user_id) and authentication credentials for the user:

    {
      "access_token": "{ACCESS_TOKEN}",
      "livemode": false,
      "refresh_token": "{REFRESH_TOKEN}",
      "token_type": "bearer",
      "stripe_publishable_key": "{PUBLISHABLE_KEY}",
      "stripe_user_id": "{ACCOUNT_ID}",
      "scope": "express"
    }

    If there was a problem, a detailed error is returned:

    {
      "error": "invalid_grant",
      "error_description": "Authorization code does not exist: {AUTHORIZATION_CODE}"
    }

    The user is now connected to your platform. The stripe_user_id is the Stripe account ID for the new account. Store this value in your database and use it to authenticate as the connected account by passing it into requests in the Stripe-Account header.

    The refresh_token can be used to generate test access tokens for a production client_id or to roll your access token.

    Verify the account’s capability

    If you provide the suggested_capabilities[] parameter, you can add the assert_capabilities[] parameter to verify that the suggested capability was applied to the connected account. This step is optional, however. Stripe handles any failure to apply a capability silently. You might want to check if you’re particularly concerned about URL security.

    curl https://connect.stripe.com/oauth/token \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d code=ac_123456789 \
      -d grant_type=authorization_code \
      -d "assert_capabilities[]=transfers"
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    response = Stripe::OAuth.token({
      grant_type: 'authorization_code',
      code: 'ac_123456789',
      assert_capabilities: ['transfers'],
    })
    
    # Access the connected account id in the response
    connected_account_id = response.stripe_user_id
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    $response = \Stripe\OAuth::token([
      'grant_type' => 'authorization_code',
      'code' => 'ac_123456789',
      'assert_capabilities' => ['transfers'],
    ]);
    
    // Access the connected account id in the response
    $connected_account_id = $response->stripe_user_id;
    
    # Set your secret key: remember to change this to your live secret key in production
    # See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
    
    response = stripe.OAuth.token(
      grant_type='authorization_code',
      code='ac_123456789',
      assert_capabilities=['transfers'],
    )
    
    # Access the connected account id in the response
    connected_account_id = response.stripe_user_id
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc');
    
    stripe.oauth.token({
      grant_type: 'authorization_code',
      code: 'ac_123456789',
      assert_capabilities: ['transfers'],
    }).then(function(response) {
      // asynchronously called
      var connected_account_id = response.stripe_user_id;
    });
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    ArrayList<String> capabilities = new ArrayList<>();
    capabilities.add("transfers");
    Map<String, Object> params = new HashMap<>();
    params.put("grant_type", "authorization_code");
    params.put("code", "ac_123456789");
    params.put("assert_capabilities", capabilities);
    
    TokenResponse response = OAuth.token(params, null);
    
    // Access the connected account id in the response
    String accountId = response.getStripeUserId();
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
    
    params := &stripe.OAuthTokenParams{
      GrantType:          stripe.String("authorization_code"),
      Code:               stripe.String("ac_123456789"),
      AssertCapabilities: []*string{
        stripe.String("transfers"),
      },
    }
    token, _ := oauth.New(params)
    
    // Access the connected account id in the response
    connected_account_id = token.StripeUserID
    
    // Set your secret key: remember to change this to your live secret key in production
    // See your keys here: https://dashboard.stripe.com/account/apikeys
    StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc";
    
    var options = new OAuthTokenCreateOptions
    {
      GrantType = "authorization_code",
      Code = "ac_123456789",
      AssertCapabilities = new List<string>
      {
        "transfers",
      },
    };
    
    var service = new OAuthTokenService();
    var response = service.Create(options);
    
    // Access the connected account id in the response
    connected_account_id = response.StripeUserId
    

    A success response then looks like:

    {
      "access_token": "{ACCESS_TOKEN}",
      "livemode": false,
      "refresh_token": "{REFRESH_TOKEN}",
      "token_type": "bearer",
      "stripe_publishable_key": "{PUBLISHABLE_KEY}",
      "stripe_user_id": "{ACCOUNT_ID}",
      "scope": "express",
      "capabilities": "transfers"
    }

    If the specified capabilities[] value doesn’t match, the error response looks like:

    {
      "error": "invalid_request",
      "error_description": "assert_capabilities expects capability: card_payments"
    }

    The most common reason for this request failure is that the specified capability is not available in your user’s country. A less likely reason is a bad actor changing the URL.

    Webhooks

    After an account is created, all account change notifications can be delivered to your webhooks as account.updated events. You set your Connect webhook URL in your account settings. These events let you track connected account onboarding and verification status, which can be useful for providing user support and displaying relevant notices in your platform’s user interface. This isn’t necessary, though, because Stripe takes your users through the steps of the onboarding and verification process and handles any issues that arise. Your platform doesn’t need to do any additional work.

    Next steps

    Was this page helpful?

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    On this page