Seller Onboarding
Before users can sell on the marketplace, they need to complete seller onboarding. This connects them to Stripe Connect so they can receive payments. The process varies based on whether they’re a casual seller or a business — we call these “Individual” and “Pro” sellers.
Seller Types
We offer two paths for becoming a seller, designed for different use cases:
Individual Sellers
Best for: Casual sellers, hobbyists, people selling their personal collection
- Can list items immediately after starting onboarding
- KYC (identity verification) is deferred until first payout request
- Lower friction to get started
- Example: “I have some cards to sell from my collection”
The Onboarding Flow
Here’s how the onboarding process works for each seller type:
Individual sellers can start selling right away, even before completing identity verification. We only require KYC when they try to withdraw their first payout. This reduces friction for casual sellers who might not complete verification unless they actually make a sale.
Seller Status States
Sellers go through various status states during and after onboarding. Understanding these helps when debugging why a seller can or can’t do something.
Here’s what each status means and what the seller can do:
| Status | What it means | Can list items? | Can receive payouts? |
|---|---|---|---|
not_started | Haven’t begun onboarding | No | No |
onboarding_incomplete | Started but missing required info | No | No |
pending_verification | All info submitted, Stripe reviewing | Maybe | No |
kyc_pending_payout | Individual seller, can sell, needs KYC to cash out | Yes | No |
kyb_pending | Pro seller waiting for business verification | No | No |
requirements_due | Stripe needs additional information | Depends | Depends |
active | Fully operational, all good! | Yes | Yes |
restricted | Account restricted by Stripe | No | No |
Country Selection
During onboarding, sellers choose their country. This is a one-time decision that can’t be changed later.
Country cannot be changed after onboarding. This is a Stripe Connect limitation. If a seller moves to a different country, they’d need to create a new account (which we generally don’t recommend).
The country determines:
- Which region they’re in (NA or EU)
- Which currency they receive payouts in
- What tax and regulatory requirements apply
Supported Countries
North America
| Country | Code | Currency |
|---|---|---|
| United States | US | USD |
| Canada | CA | CAD |
Shipping Rates
Every seller gets default shipping rates based on their country — they can start selling immediately after onboarding without any additional configuration.
Default Rates
We provide sensible default rates for each supported country, covering all three shipping zones:
| Zone | What it covers |
|---|---|
| Domestic | Same country as seller |
| Regional | EU countries (for EU sellers) or NA (for US/CA sellers) |
| Global | Rest of world |
Default rates vary by country to reflect typical postal costs. For example, a US seller’s defaults might be 12.00 global small — while a French seller’s defaults are €3.00 domestic small, €10.00 global small.
Customizing Rates
Sellers can override the defaults in their shipping settings:
Package sizes are determined by product type:
- Cards: XSmall (1-5 cards), Small (6-50 cards), Medium (51-250 cards), Large (251+ cards)
- Sealed products: Based on category — cases are XLarge, boxes/bundles are Large, boosters/packs are Medium
Disabling zones: Sellers can disable specific zones if they don’t want to ship there. For example, disabling “Global” means their items won’t appear for buyers outside their region. By default, all zones are enabled with the country’s default rates.
The Stripe Connect Data Model
Here’s what we store about a seller’s Stripe Connect account:
interface UserStripeConnect {
accountId: string // Stripe's acct_xxx ID
sellerType: 'individual' | 'pro'
country: string // ISO 3166-1 alpha-2 code
onboardingStatus: OnboardingStatus
payoutsEnabled: boolean // Can they receive money?
pendingRequirements: string[] // What Stripe needs
pendingVerification: string[] // What's being verified
requirementsDeadline?: Date // When requirements are due
firstSaleAt?: Date // For KYC leniency period
canSell: boolean // Can they list items?
}Embedded Components (Custom Mode)
We use Stripe Connect in custom mode with embedded components. Instead of redirecting sellers to Stripe’s hosted dashboard, we embed Stripe’s UI components directly in our app. This gives us full control over the seller experience while Stripe handles the secure data collection.
Available Embedded Components
The backend controls which components are enabled based on seller status:
| Component | Purpose |
|---|---|
ConnectAccountOnboarding | Initial onboarding flow, identity verification |
ConnectAccountManagement | Update account settings, business info |
ConnectPayouts | View and manage payout schedule |
ConnectBalances | View account balance |
ConnectNotificationBanner | Show action-required notifications |
Client secrets are short-lived — they expire quickly for security. The frontend requests a fresh secret each time the seller opens settings that require Stripe components.
Webhook Sync
Stripe sends account.updated webhooks whenever something changes about a seller’s account:
- Requirements added or removed
- Verification completed
- Payouts enabled or disabled
- Account restricted
Our webhook handler syncs this data back to the user’s profile so we always have an up-to-date view of their status.
Seller Fees
When a sale happens, we take a cut. The fee is calculated as a percentage of the order total (subtotal + shipping):
| Region | Seller Fee |
|---|---|
| Europe | 5% |
| North America | 8% |
Fees are deducted when funds are released to the seller, not at checkout time. See Fund Management for details on when and how funds are released.
Common Issues
Here are problems you might encounter and how to resolve them:
| Issue | Cause | Resolution |
|---|---|---|
| ”Can’t change country” | Stripe limitation | User needs to create new account (edge case) |
| “Requirements keep appearing” | Stripe’s progressive disclosure | Complete each requirement as it appears |
| ”Payouts disabled” | Missing info or verification failed | Check Stripe dashboard via account session |
| ”Account restricted” | Policy violation or fraud concern | User needs to contact Stripe support |
| ”Can’t list items” (Pro) | KYB not approved yet | Wait for Stripe review or check requirements |
API Endpoints
| Endpoint | Purpose |
|---|---|
seller.initiateOnboarding | Start the onboarding process |
seller.createAccountSession | Get a link to Stripe’s dashboard |
seller.getStatus | Check current seller status and requirements |
seller.updateShippingRates | Configure shipping rates |
Code Locations
| Component | Location |
|---|---|
| User Model (Stripe data) | packages/core/db/src/mongo/user.model.ts |
| Seller Onboarding Service | packages/backend/features/seller/src/seller-onboarding.service.ts |
| Stripe Connect Webhooks | packages/backend/features/seller/src/stripe-connect.webhook.ts |
| Shipping Rate Service | packages/backend/features/seller/src/shipping-rate.service.ts |
| Default Rates & Calculation | packages/backend/domains/shipping-rates/ |
| Seller Contracts | packages/core/api-dtos/src/lib/marketplace/seller/ |
| Stripe Connect (Frontend) | packages/integrations/stripe-frontend/ |
| Stripe Connect (Backend) | packages/integrations/stripe/src/connect/ |