Migrate payouts from Stripe Connect
Move contractor payouts from Stripe Connect to Wingspan. Load your payees and their tax information, have each payee add a payout method once, and switch each payee to Wingspan once they're eligible.
This guide is for platforms that pay contractors through Stripe Connect and are moving those payouts to the Wingspan V3 API. Stripe keeps paying each payee until they're ready on Wingspan. Then you switch that payee over, and every payout after that comes from Wingspan.
Most of the work is loading data you already have. Payees add a bank account once, inside your app. If you already hold their bank details, they don't have to do anything.
How Stripe Connect maps to Wingspan
| Stripe Connect | Wingspan | What changes |
|---|---|---|
| Connected account | A Payee linked to the payee's own Account | The Payee is your record of the relationship. The payee's Account holds their tax and bank details. |
| Account Links (hosted onboarding) | Onboarding in your app | Payees finish setup inside your product through the Embedded SDK. |
| External account | Payout method | The payee adds it in the embedded payout step, or you route payouts to bank details you hold. |
| Requirements and capabilities | Requirements on an engagement | paymentsEligibility on the payee's engagement tells you whether you can pay them. |
| Transfer | Payable | A payable carries line items, your metadata and an approval state. Payroll runs pay approved payables. |
| Payout schedule | Payroll schedule | Set it once for your Account. |
| Topping up your platform balance | Payroll funding source | We debit your funding source for each payroll run. |
| Transfer reversal | Deduction | Recover the amount from the payee's next payable. |
| Connect webhooks | V3 webhooks and the event log | Subscribe once at /v3/platform/webhooks. |
| 1099 tax reporting | 1099 filing | Load the year's Stripe payments so each payee gets one form for the year. |
What carries over
Profiles and IDs
Send each payee's email, display name, business name and phone, plus your own payee ID as the Payee's externalId. To keep the Stripe connected account ID too, add it to the Payee's metadata with PATCH /v3/payments/payees/{payeeId}. You can pull these from your own database or from Stripe's API. The payee doesn't do anything.
Tax information
If you hold your payees' W-9 information, supply it and we run a TIN match against IRS records. A payee whose name and TIN match skips the W-9 step. A payee whose information you don't hold, or whose information doesn't match, certifies a W-9 once in the embedded onboarding flow.
Bank details
Stripe's data export covers saved customer cards only. You get payout bank accounts on file one of two ways:
- Each payee adds a bank account once in the embedded payout step. They can also choose Wingspan Wallet or instant payouts there.
- If your platform collected bank details itself, and your agreement with payees lets you use them for payouts, you route payouts to them yourself. The steps are in If you already hold payees' bank details.
Identity verification
Stripe's identity checks stay in Stripe, and we pay payees without them. ID and selfie checks run only when you add an identity verification requirement to the engagement.
Payment history
Load this year's Stripe payouts as paid payables (Step 7) so each payee's 1099 covers the whole year. Earlier history stays in Stripe.
What a payee may be asked to do
| Step | When the payee sees it | Where |
|---|---|---|
| Add a bank account | Unless you route payouts to bank details you hold | The embedded payout step in your app |
| Certify a W-9 | Only when you don't supply their tax information, or it doesn't match IRS records | Embedded onboarding |
| Verify their identity | Only if your engagement requires it | Embedded onboarding |
| Complete your own requirements, such as signing an agreement | Only the requirements you add | Embedded onboarding |
We don't email payees during the import. Tell them about the change from your own channels, at the point where you ask them to add a bank account.
Before you start
- A ServiceAccount and API key for your platform. See Embedded SDK, Step 1.
- An engagement to put your payees on, with any requirements you want them to complete.
- A payroll funding source on your Account. See Payroll settings and funding.
- A payee list from your own systems: your payee ID, email, name, business name, and W-9 information where you have it.
Step 1: Import your payees
Create a PayeeImport batch with POST /v3/platform/batches. Set configuration.engagementId to place every payee on your engagement.
curl -X POST https://api.wingspan.app/v3/platform/batches \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"type": "PayeeImport",
"name": "Stripe Connect migration",
"configuration": { "context": "Contractor", "engagementId": "Eg7nQ2vLx4pRt8mKw1sZbA" }
}'Add one item per payee with POST /v3/platform/batches/{batchId}/items. Use your own payee ID as externalId, so every later call can find the payee by your key.
curl -X POST https://api.wingspan.app/v3/platform/batches/S8iq9y7AjzQHb6BAEcn6zJ/items \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"data": {
"externalId": "prov-1042",
"email": "[email protected]",
"displayName": "Priya Shah"
}
}'Process the batch with POST /v3/platform/batches/{batchId}/process, check progress with GET /v3/platform/batches/{batchId}/summary, and read the payeeId each item created with GET /v3/platform/batches/{batchId}/items. Save each payeeId against your own payee record.
Leave configuration.sendInvites at its default of false for a migration, so the import sends no invitations. The full batch lifecycle is in Batches and bulk operations.
Step 2: Create each payee's Account and link it
A payee needs their own Account to use Wingspan screens in your app. Create it under your Account, then link it to the Payee from step 1 with POST /v3/payments/payees/{payeeId}/associate-account. Set authority.type to MigrationImported and record the agreement your payee accepted with you. We store the authority for audit.
# 1. Create the payee's Account under your Account
curl -X POST https://api.wingspan.app/v3/platform/accounts \
-H "Authorization: Bearer $WINGSPAN_ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "parentAccountId": "Ap4tYs8KqW2nLm6xRb1cVe", "externalId": "prov-1042" }'
# 2. Link the Payee to that Account
curl -X POST https://api.wingspan.app/v3/payments/payees/Py3mQw7kLx2tRb9nVd4sHa/associate-account \
-H "Authorization: Bearer $WINGSPAN_ADMIN_API_KEY" \
-H "X-Wingspan-Account: Ap4tYs8KqW2nLm6xRb1cVe" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"payeeAccountId": "Kc2vRn8mTq4wLx6pZb1dHs",
"authority": {
"type": "MigrationImported",
"basis": "MigrationAgreement",
"externalAgreementId": "prov-1042",
"acceptedAt": "2025-03-14T16:20:00Z",
"consentVersion": "2025.1"
}
}'This is the same pattern as Embedded SDK, Step 2, which lists the scopes these calls need. The payee accepts banking terms separately, in onboarding, if they choose a product that needs them, such as Wingspan Wallet.
Step 3: Supply the tax information you hold
For each payee whose W-9 information you hold, create a ComplianceEntity on your own Account with the Payee as its subject, using POST /v3/platform/compliance-entities.
{
"subject": { "type": "Payee", "id": "Py3mQw7kLx2tRb9nVd4sHa" },
"type": "Individual",
"jurisdictionCountry": "US",
"individualLegalName": { "givenName": "Priya", "familyName": "Shah" },
"physicalAddress": { "line1": "100 Example St", "city": "Austin", "state": "TX", "postalCode": "78701", "country": "US" },
"taxIdentifiers": [ { "countryCode": "US", "type": "SSN", "value": "000000000" } ]
}Then run the TIN match with POST /v3/platform/compliance-entities/{complianceEntityId}/verify and { "level": "Tax" }.
By default a Payee's w9SourcePolicy is PayeeSuppliedFallbackToPayer. We use your record until the payee certifies their own, and your record stays on file after that. See Tax information.
Step 4: Have payees add a payout method in your app
Show the embedded onboarding flow to each migrated payee. Leave out certify_tax_payer_info for payees whose tax information matched in step 3.
ws.createOnboarding({
modules: [
{ type: "payout_method", allowedUsMethods: ["ach", "wallet", "instant"] },
{ type: "eligibility_requirements" },
],
}).mount("#wingspan-setup");If a payee only needs the bank step, ws.createManagePayoutMethod() shows it on its own. The Embedded SDK guide covers sessions, components and events.
Step 5: Switch each payee when they're eligible
Keep paying a payee through Stripe until we report them eligible. Read paymentsEligibility on the payee's engagement with GET /v3/payments/payees/{payeeId}/engagements. When it reads Eligible, mark the payee as paid by Wingspan in your own database and send their new work to Wingspan from then on.
Make the switch once per payee, and route each piece of work to one system. If you create a payable for a payee who isn't eligible yet, it waits as Pending with a pendingStatusReason until they finish setup. Find incomplete payables shows how to list those.
Step 6: Create and pay payables
Create a payable for each piece of work with POST /v3/payments/payables. Use your own transfer or job ID as externalId, and put any IDs you reconcile on in each line item's metadata.
curl -X POST https://api.wingspan.app/v3/payments/payables \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"payeeId": "Py3mQw7kLx2tRb9nVd4sHa",
"externalId": "job-88213",
"currency": "USD",
"dueDate": "2026-11-06",
"lineItems": [
{ "description": "Shift on Nov 2", "lineItemType": "Services", "quantity": 8, "unitCost": 65, "metadata": { "jobId": "job-88213" } }
]
}'Open it with POST /v3/payments/payables/{payableId}/open and approve it with PATCH /v3/payments/payables/{payableId}/workflow-status and { "payerApprovalStatus": "Approved" }. Your payroll schedule pays approved payables, or you start a run yourself with POST /v3/payments/payroll-runs.
To create many payables at once, use a PayableImport batch. It creates them already opened and approved. Bulk payables walks through the batch.
Step 7: Load this year's Stripe payouts for 1099s
A payee paid through Stripe early in the year and through Wingspan later should get one 1099 for the year. Load the Stripe payouts as paid payables with a PayableImport batch. Set payableStatus to Paid and give each item the date Stripe paid it. No money moves.
curl -X POST https://api.wingspan.app/v3/platform/batches \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"type": "PayableImport",
"name": "Stripe payouts this year",
"configuration": { "processingStrategy": "Single", "payableStatus": "Paid" }
}'{
"data": {
"payeeId": "Py3mQw7kLx2tRb9nVd4sHa",
"uniqueReferenceKey": "tr_1PqR2sT3uV4wX5yZ",
"description": "Paid through Stripe Connect",
"totalCost": 1240.00,
"dueDate": "2026-03-06",
"paidDate": "2026-03-06"
}
}Turn on Off-platform payments in your 1099 calculation settings so these payments count. Set up 1099 filing and Add contractors and off-platform payments cover the settings.
If Stripe files 1099s for your connected accounts, turn off Stripe's filing for the year you load, so Stripe and Wingspan don't both report the same payments.
Step 8: Retire Stripe Connect
When you've switched every active payee to Wingspan and the last Stripe transfers have settled, stop creating connected accounts and remove your transfer code. Keep your Stripe account open so you can still read past payouts and refunds.
If you already hold payees' bank details
If your platform collected payees' bank details itself, and your agreement with them lets you use those details for payouts, you can set up their payouts without asking them for anything. The bank account belongs to your Account and names the Payee as its subject.
- Create the bank account with
POST /v3/finance/external-bank-accounts, withsubjectset to{ "type": "Payee", "id": "<payeeId>" }, the routing and account numbers,accountType,accountHolderTypeandaccountHolderName. - Route the payee's payouts to it with
POST /v3/payments/payout-settings.
This works for US bank accounts paid in USD. You can skip step 2 for these payees. The full request bodies are in Payout methods.
Track progress
Subscribe to Payable.Paid and Payable.Returned with POST /v3/platform/webhooks to follow each payout. Read a payee's readiness from paymentsEligibility on their engagement, and a batch's progress from its summary. Webhook event types lists every event you can subscribe to.
Common mistakes
- Paying the same work through both systems. Record which system pays each payee, switch once, and route each payout by that field.
- Turning on
sendInvitesfor the import. Payees then get a Wingspan email before you've told them about the change. - Matching payees by email. Use your own payee ID as
externalIdinstead. A second create with the sameexternalIdreturns409, which makes a re-run safe. - Leaving out the cutover year's Stripe payouts, or leaving Off-platform payments off. The payee's 1099 then misses part of the year.
- Expecting Stripe's export to include payout bank accounts. It covers customer cards only.
Related pages
Updated 16 days ago