Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to onboard sellers with Stripe Connect

Seller onboarding in Stripe Connect involves creating a connected account, collecting identity and business information, and handling verification requirements. This guide covers the complete onboarding lifecycle including progressive onboarding, requirement deadlines, handling failures, and monitoring onboarding status with webhooks for all three account types.

What you'll learn

  • How to implement progressive seller onboarding with Stripe Connect
  • How to handle verification requirements and deadlines
  • How to build retry flows for failed verification
  • How to monitor onboarding status with account.updated webhooks
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced6 min read25 minutesStripe API v2024-12+, Node.js 18+March 2026RapidDev Engineering Team
TL;DR

Seller onboarding in Stripe Connect involves creating a connected account, collecting identity and business information, and handling verification requirements. This guide covers the complete onboarding lifecycle including progressive onboarding, requirement deadlines, handling failures, and monitoring onboarding status with webhooks for all three account types.

Complete Seller Onboarding Lifecycle with Stripe Connect

Seller onboarding is the most critical part of a marketplace integration. Stripe requires identity verification, business information, and banking details before a seller can accept payments. The onboarding requirements vary by country and business type. This guide covers progressive onboarding — letting sellers start accepting payments quickly while collecting remaining requirements over time.

Prerequisites

  • Stripe account with Connect enabled
  • Node.js 18 or later installed
  • Stripe Node.js SDK: npm install stripe
  • Express.js: npm install express
  • A database to store seller account IDs and onboarding status

Step-by-step guide

1

Create a connected account with pre-filled data

Pre-fill as much seller information as possible when creating the account. This reduces friction during onboarding and speeds up verification.

typescript
1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
2
3async function createSellerAccount(sellerData) {
4 const account = await stripe.accounts.create({
5 type: 'express',
6 country: sellerData.country,
7 email: sellerData.email,
8 capabilities: {
9 card_payments: { requested: true },
10 transfers: { requested: true },
11 },
12 business_type: 'individual',
13 individual: {
14 first_name: sellerData.firstName,
15 last_name: sellerData.lastName,
16 email: sellerData.email,
17 },
18 business_profile: {
19 url: sellerData.websiteUrl,
20 mcc: '5734', // Computer software stores
21 },
22 metadata: {
23 platform_user_id: sellerData.userId,
24 },
25 });
26 return account;
27}

Expected result: A connected account is created with pre-filled data, reducing the number of fields the seller needs to complete.

2

Implement progressive onboarding

Stripe has two types of requirements: currently_due (needed now) and eventually_due (needed later). Progressive onboarding lets sellers start accepting payments after meeting currently_due requirements.

typescript
1async function checkOnboardingStatus(accountId) {
2 const account = await stripe.accounts.retrieve(accountId);
3
4 const status = {
5 chargesEnabled: account.charges_enabled,
6 payoutsEnabled: account.payouts_enabled,
7 currentlyDue: account.requirements.currently_due,
8 eventuallyDue: account.requirements.eventually_due,
9 pastDue: account.requirements.past_due,
10 currentDeadline: account.requirements.current_deadline,
11 errors: account.requirements.errors,
12 };
13
14 if (status.currentlyDue.length === 0 && status.chargesEnabled) {
15 status.phase = 'complete';
16 } else if (status.pastDue.length > 0) {
17 status.phase = 'restricted'; // Action needed urgently
18 } else if (status.currentlyDue.length > 0) {
19 status.phase = 'in_progress';
20 }
21
22 return status;
23}

Expected result: Returns a status object showing the onboarding phase and any outstanding requirements.

3

Handle requirement deadlines

Stripe sets deadlines for requirements. If not met, the account may be restricted. Monitor current_deadline and send reminders to sellers.

typescript
1async function checkDeadlines(accountId) {
2 const account = await stripe.accounts.retrieve(accountId);
3 const deadline = account.requirements.current_deadline;
4
5 if (deadline) {
6 const deadlineDate = new Date(deadline * 1000);
7 const daysLeft = Math.ceil((deadlineDate - Date.now()) / (1000 * 60 * 60 * 24));
8
9 console.log(`Deadline: ${deadlineDate.toISOString()}`);
10 console.log(`Days remaining: ${daysLeft}`);
11 console.log('Requirements due:', account.requirements.currently_due);
12
13 if (daysLeft <= 7) {
14 // Send urgent reminder to seller
15 console.log('URGENT: Send reminder to seller!');
16 }
17 } else {
18 console.log('No active deadline.');
19 }
20}

Expected result: Console shows the deadline date, days remaining, and outstanding requirements.

4

Handle verification failures

When Stripe rejects a verification document or finds issues, the requirements.errors array contains the specific reasons. Generate a new Account Link so the seller can resubmit.

typescript
1async function handleVerificationFailure(accountId) {
2 const account = await stripe.accounts.retrieve(accountId);
3 const errors = account.requirements.errors;
4
5 if (errors && errors.length > 0) {
6 errors.forEach((error) => {
7 console.log(`Requirement: ${error.requirement}`);
8 console.log(`Reason: ${error.reason}`);
9 console.log(`Code: ${error.code}`);
10 });
11
12 // Generate a new onboarding link for the seller to fix issues
13 const accountLink = await stripe.accountLinks.create({
14 account: accountId,
15 refresh_url: `https://yourplatform.com/onboarding/refresh?acct=${accountId}`,
16 return_url: `https://yourplatform.com/onboarding/complete?acct=${accountId}`,
17 type: 'account_onboarding',
18 });
19
20 return { errors, fixUrl: accountLink.url };
21 }
22
23 return { errors: [], fixUrl: null };
24}

Expected result: Returns verification errors with human-readable reasons and a URL for the seller to resubmit.

5

Set up webhook monitoring for onboarding events

The account.updated webhook fires whenever a connected account's status changes. Use it to track onboarding progress and notify sellers.

typescript
1const express = require('express');
2const app = express();
3
4app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
5 const sig = req.headers['stripe-signature'];
6 let event;
7 try {
8 event = stripe.webhooks.constructEvent(
9 req.body,
10 sig,
11 process.env.STRIPE_WEBHOOK_SECRET
12 );
13 } catch (err) {
14 return res.status(400).send(`Webhook Error: ${err.message}`);
15 }
16
17 if (event.type === 'account.updated') {
18 const account = event.data.object;
19 const accountId = account.id;
20
21 if (account.charges_enabled && account.payouts_enabled) {
22 console.log(`${accountId}: Fully onboarded`);
23 // Update DB, notify seller
24 } else if (account.requirements.past_due.length > 0) {
25 console.log(`${accountId}: Has overdue requirements`);
26 // Send urgent notification
27 } else if (account.requirements.errors.length > 0) {
28 console.log(`${accountId}: Verification failed`);
29 // Notify seller to resubmit
30 }
31 }
32
33 res.json({ received: true });
34});

Expected result: Webhook handler processes account updates and takes appropriate action based on the onboarding state.

Complete working example

seller-onboarding.js
1const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
2const express = require('express');
3const app = express();
4
5app.use(express.json());
6
7app.post('/api/sellers/onboard', async (req, res) => {
8 try {
9 const { email, firstName, lastName, country } = req.body;
10 const account = await stripe.accounts.create({
11 type: 'express',
12 country: country || 'US',
13 email,
14 capabilities: { card_payments: { requested: true }, transfers: { requested: true } },
15 individual: { first_name: firstName, last_name: lastName, email },
16 });
17
18 const link = await stripe.accountLinks.create({
19 account: account.id,
20 refresh_url: `https://yourplatform.com/onboarding/refresh?acct=${account.id}`,
21 return_url: `https://yourplatform.com/onboarding/complete?acct=${account.id}`,
22 type: 'account_onboarding',
23 });
24
25 res.json({ accountId: account.id, onboardingUrl: link.url });
26 } catch (err) {
27 res.status(400).json({ error: err.message });
28 }
29});
30
31app.get('/api/sellers/:accountId/status', async (req, res) => {
32 try {
33 const account = await stripe.accounts.retrieve(req.params.accountId);
34 res.json({
35 chargesEnabled: account.charges_enabled,
36 payoutsEnabled: account.payouts_enabled,
37 currentlyDue: account.requirements.currently_due,
38 pastDue: account.requirements.past_due,
39 deadline: account.requirements.current_deadline,
40 errors: account.requirements.errors,
41 });
42 } catch (err) {
43 res.status(400).json({ error: err.message });
44 }
45});
46
47app.post('/api/sellers/:accountId/fix', async (req, res) => {
48 try {
49 const link = await stripe.accountLinks.create({
50 account: req.params.accountId,
51 refresh_url: `https://yourplatform.com/onboarding/refresh?acct=${req.params.accountId}`,
52 return_url: `https://yourplatform.com/onboarding/complete?acct=${req.params.accountId}`,
53 type: 'account_onboarding',
54 });
55 res.json({ url: link.url });
56 } catch (err) {
57 res.status(400).json({ error: err.message });
58 }
59});
60
61app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
62 const sig = req.headers['stripe-signature'];
63 let event;
64 try {
65 event = stripe.webhooks.constructEvent(
66 req.body, sig, process.env.STRIPE_WEBHOOK_SECRET
67 );
68 } catch (err) {
69 return res.status(400).send(`Webhook Error: ${err.message}`);
70 }
71 if (event.type === 'account.updated') {
72 const acct = event.data.object;
73 console.log(`${acct.id}: charges=${acct.charges_enabled} payouts=${acct.payouts_enabled}`);
74 console.log(' Due:', acct.requirements.currently_due);
75 }
76 res.json({ received: true });
77});
78
79app.listen(3000, () => console.log('Onboarding server on port 3000'));

Common mistakes when onboarding sellers with Stripe Connect

Why it's a problem: Not monitoring requirements.eventually_due

How to avoid: Eventually_due requirements become currently_due over time. Track them proactively to avoid account restrictions.

Why it's a problem: Ignoring current_deadline on requirements

How to avoid: If requirements are not met by the deadline, Stripe may disable charges or payouts. Send reminders before deadlines.

Why it's a problem: Not handling verification errors gracefully

How to avoid: When verification fails, show the seller a clear message about what went wrong and provide a link to resubmit.

Why it's a problem: Relying only on return_url to determine onboarding completion

How to avoid: The return URL just means the seller left the form. Use webhooks and charges_enabled as the source of truth.

Best practices

  • Pre-fill as much seller data as possible to reduce onboarding drop-off
  • Use account.updated webhooks as the authoritative source for onboarding status
  • Send reminder emails to sellers with upcoming requirement deadlines
  • Store requirement errors in your database for support troubleshooting
  • Implement a seller dashboard that shows their onboarding progress and any action items
  • Test all onboarding scenarios in test mode including verification failures
  • For complex multi-seller onboarding, consider working with RapidDev to build a robust onboarding pipeline

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I need to build a complete seller onboarding system with Stripe Connect. How do I handle progressive onboarding, requirement deadlines, verification failures, and webhook monitoring? Show me Node.js code.

Stripe Prompt

Help me implement seller onboarding for my marketplace using Stripe Connect Express accounts. I need account creation with pre-filled data, onboarding link generation, status checking, deadline monitoring, and webhook handlers.

Frequently asked questions

What is progressive onboarding in Stripe Connect?

Progressive onboarding lets sellers start accepting payments after meeting a minimum set of requirements (currently_due). Additional requirements (eventually_due) are collected over time, with deadlines set by Stripe.

How do I know which requirements a seller must complete?

Use the accounts.retrieve API to check requirements.currently_due for immediate needs and requirements.eventually_due for future needs. The exact requirements depend on country, business type, and capabilities.

What happens if a seller misses a requirement deadline?

Stripe may disable charges, payouts, or both on the account. The seller must complete the overdue requirements before functionality is restored.

Can I collect verification documents myself instead of using Account Links?

Yes, with Custom accounts you can collect documents via your own UI and upload them via the API. With Express accounts, Stripe's hosted onboarding handles document collection.

How long does Stripe take to verify seller documents?

Automated checks (SSN, address) complete in seconds to minutes. Manual document reviews (ID, proof of address) typically take 1-3 business days.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.