Migrating from V1
What changed between the Wingspan V1 and V3 APIs, how V1 concepts and endpoints map to V3, and a suggested order for moving your integration.
Move an existing V1 integration (/payments/collaborator, /payments/payable, /users/session) to the V3 API. Learn what changed, how V1 concepts and endpoints map to V3, which field changes break code, and the recommended migration order.
The underlying product has not changed. Your contractors, payables, invoices, and payment history remain the same records. V1/V2 and V3 work on the same data; the API version used by an Account changes. Once an Account moves to V3, it uses the V3 API.
Should you move to V3?
Yes, if you're building anything new. New integrations should use the V3 API. V1 and V2 still work, but Wingspan plans to deprecate them. V3 covers more of the product, and some capabilities are available only through V3.
If you already run on V1, check the endpoint mapping for everything your integration calls before an Account moves. Anything you depend on that isn't in V3 yet has to be settled first, because V1/V2 calls stop working on an Account once it's on V3.
What happens to your Account when you start using V3
- Signing in through V3 moves the Account to V3. When a person signs in through the V3 API (
POST /v3/platform/sessions), Wingspan moves that Account to V3. You need a V3 session to create your first V3 API key, so this happens when you start. - After that, V1/V2 calls on the Account fail. A V1 or V2 API call on a migrated Account returns a
NotMigratederror with the message "this v1/v2 API call is deprecated and must be migrated to the v3 API." Move every call your integration makes for that Account to V3 at the same time. - V1 and V2 tokens don't work on V3. V3 endpoints do not accept V1 or V2 session tokens, including tokens copied from the V1 web app. Create a V3 session or API key instead. See Environments and authentication.
- Provisioning and sign-in are separate flows. If you're a partner that creates Accounts for your clients through the V1/V2 API, you can keep provisioning that way. Your clients who use the V1/V2 APIs can keep logging in directly. When a user signs in through the V3 API, their Account moves to V3.
- Your Wingspan team QAs each migrated Account. There's nothing you need to do for the migration itself. Talk to your account team before your first V3 sign-in so they can plan it with you.
What changed and why
Domains. Every V3 path starts with /v3/ and one of five domains: payments, finance, onboarding, compliance, or platform. V1 paths were grouped by the internal service that served them (/payments, /users, /files, /integrations). The domain tells you where a capability lives without knowing how Wingspan is built.
Identity model. V1 used "user", "member", and "client" for overlapping things. V3 separates them. A Person is a human who logs in. An Account is the business entity that holds money and tax records. A Payee is your record of someone you pay, and a Payer is a payee's record of someone who pays them. A payee no longer has to sign up before you can create payables for them: the Payee record exists on its own, and linking it to the contractor's own Account happens later without changing its ID. See Concepts.
Errors. V1 errors returned a code (the HTTP status) and a free-text message. V3 returns one RFC 9457 shape everywhere. It includes a stable text code, such as EligibilityBlocked, that you can branch on; a requestId to quote to support; and per-field errors[] on validation failures. See Errors.
Pagination. Every V3 list endpoint pages the same way: send page[size] and page[token], read pagination.nextPageToken, and stop when it's an empty string. Tokens are opaque. See Pagination.
Idempotency. Creates and money movement take an Idempotency-Key header, and some calls, such as paying a payable, require it. A retry with the same key and body returns the original result instead of creating a second payee or paying twice. See Idempotency.
State changes. In V1 you often changed state by writing a field, such as status: "Open" on a payable. In V3, send a POST to a verb such as /open, /cancel, or /pay for each state change. Use PATCH only to edit fields. Each verb has a matching events.*At timestamp and webhook event, which records the action.
Webhooks. In V1, you configured webhooks through /integrations/webhooks/preference, with event names such as payoutFailedAt. In V3, create and manage subscriptions at /v3/platform/webhooks. Events use the Resource.Event format, such as Payable.Paid; deliveries are signed; and /v3/platform/events retains a 30-day event log for recovery. See Webhooks overview.
Concept mapping
| V1 concept | V3 concept | Notes |
|---|---|---|
| User | Person | A human with a login. |
| Member (the contractor) | Payee (your record of them); the contractor's own Account | memberId becomes payeeAccountId on a Payee, and it's absent until the contractor links. |
| Client (the company paying) | Your Account; Payer (the contractor's record of you) | clientId is implied by your credential and is never in the URL or body. |
| Collaborator | Payee | collaboratorId becomes payeeId. Each payee has a required context, Contractor or Employee. |
| Member-client relationship | Payee and Payer, plus their engagements | |
| Collaborator group | Group, for organizing payees; Engagement, for requirements | V3 groups don't carry requirements. Attach requirements to an engagement instead. |
| Eligibility requirement | RequirementDefinition (template) and PayeeRequirement (per payee) | Under /v3/onboarding. |
| Engagement and assignment (app terms since February 2026) | Engagement and PayeeEngagement | Engagement types are Contractor, Employee, EmployeeOfRecord, and AgentOfRecord (EngagementType). |
| Payable | Payable | Same name, new shape. See Field changes. |
| Invoice (member invoice) | Invoice | The payee's view of what they're owed. |
| Client invoice | Payable, or paying an Invoice | A payer sees what it owes as payables, and pays a payee's invoice with POST /v3/payments/invoices/{invoiceId}/pay. |
| Invoice template | Recurring invoice | /v3/payments/recurring-invoices. |
| Pay approved | Payroll run | Create a Contractor run, then finalize it. |
| Bulk collaborator or payable batch | Batch with type: PayeeImport or PayableImport | /v3/platform/batches. |
| Organization child account | Child Account (parentAccountId) | Act on it with X-Wingspan-Account. |
| API token | API key, owned by a Person or ServiceAccount | New: ServiceAccount, a machine identity for backend jobs. |
| Files (private and public) | Vault files | One upload location: /v3/compliance/vault-files. |
labels | metadata | Up to 50 keys, values up to 500 characters. |
Endpoint mapping
This table covers the V1 endpoints most integrations use. Every V3 endpoint listed is available now. Rows marked "Not available yet" have no V3 equivalent today. Contact your Wingspan account team before you move an Account that depends on one.
Payees (V1 collaborators)
| V1 | V3 |
|---|---|
POST /payments/collaborator | POST /v3/payments/payees (with the required context), then POST /v3/payments/payees/{payeeId}/invite |
GET /payments/collaborator | GET /v3/payments/payees |
GET /payments/collaborator/{id} | GET /v3/payments/payees/{payeeId} |
PATCH /payments/collaborator/{id} | PATCH /v3/payments/payees/{payeeId} |
DELETE /payments/collaborator/{id} | DELETE /v3/payments/payees/{payeeId} |
GET /payments/collaborator/{id}/events | GET /v3/payments/payees/{payeeId}/events |
GET /payments/collaborator/{id}/download-w9 | Not available yet |
PATCH /payments/collaborator/{id}/add-group/{groupId} | POST /v3/payments/groups/{groupId}/members |
PATCH /payments/collaborator/{id}/remove-group/{groupId} | DELETE /v3/payments/groups/{groupId}/members/{memberId} |
GET, POST /payments/collaborator-group | GET, POST /v3/payments/groups |
/payments/collaborator-group/{id}/eligibility-requirement/... | POST /v3/payments/engagements/{engagementId}/requirements |
/payments/collaborator-settings/payment-eligibility | Requirement definitions at /v3/onboarding/requirement-definitions, attached to engagements |
GET /payments/memberClient/{id} | GET /v3/payments/payee-engagements/{payeeEngagementId} |
POST /payments/bulk-collaborator-batch and its items | POST /v3/platform/batches with type: PayeeImport, then POST /v3/platform/batches/{batchId}/items and POST /v3/platform/batches/{batchId}/process |
Payables and payroll
| V1 | V3 |
|---|---|
POST /payments/payable | POST /v3/payments/payables |
GET /payments/payable | GET /v3/payments/payables |
GET /payments/payable/{id} | GET /v3/payments/payables/{payableId} |
PATCH /payments/payable/{id} (fields) | PATCH /v3/payments/payables/{payableId} |
PATCH /payments/payable/{id} with status: Open | POST /v3/payments/payables/{payableId}/open |
PATCH /payments/payable/{id} with status: Approved | PATCH /v3/payments/payables/{payableId}/workflow-status with payerApprovalStatus: Approved |
PATCH /payments/payable/{id} with status: Cancelled | POST /v3/payments/payables/{payableId}/cancel |
DELETE /payments/payable/{id} | DELETE /v3/payments/payables/{payableId} |
| No equivalent in the V1 reference | POST /v3/payments/payables/{payableId}/pay |
GET /payments/payroll/immediate/payable | GET /v3/payments/payables?filter[payerApprovalStatus][eq]=Approved&filter[status][eq]=Opened |
POST /payments/pay-approved | POST /v3/payments/payroll-runs with type: Contractor, then POST /v3/payments/payroll-runs/{payrollRunId}/finalize. See Payroll runs. |
GET, PATCH /payments/payroll-settings/{id} | GET, PATCH /v3/payments/payer-settings (your own Account; no ID in the path) |
GET /payments/summary/payables, /payments/reports/* | Not available yet. Report and export endpoints aren't in V3. Build reports from list endpoints and the read-only search rows at /v3/search. |
POST /payments/bulk-payable-batch and its items | POST /v3/platform/batches with type: PayableImport, then add items and process |
POST /payments/client-deduction, /payments/collaborator-deduction | POST /v3/payments/deductions |
GET /payments/client-deduction | GET /v3/payments/deductions |
Invoices
| V1 | V3 |
|---|---|
POST /payments/invoice | POST /v3/payments/invoices |
GET /payments/invoice | GET /v3/payments/invoices |
POST /payments/invoice/{id}/send | POST /v3/payments/invoices/{invoiceId}/send |
POST /payments/invoice/{id}/generate | GET /v3/payments/invoices/{invoiceId}/pdf (returns a 302 to a short-lived download URL) |
POST /payments/invoice/{id}/refund | POST /v3/payments/invoices/{invoiceId}/refund |
POST /payments/client/invoice/{id}/pay | POST /v3/payments/invoices/{invoiceId}/pay |
GET /payments/client/invoice | GET /v3/payments/payables (what you owe) |
/payments/invoice-template | /v3/payments/recurring-invoices |
Users, sessions, and accounts
| V1 | V3 |
|---|---|
POST /users/session | POST /v3/platform/sessions with sessionType: Person |
POST, PATCH /users/session/otp | POST, PATCH /v3/platform/sessions/otp |
GET /users/session/token/{token} | No direct equivalent. Any authenticated call, such as GET /v3/platform/api-keys, tells you whether a token is valid. |
DELETE /users/session/token/{id} | DELETE /v3/platform/sessions/{sessionId} |
/users/session/api (API tokens) | /v3/platform/api-keys |
GET /users/user/{id} | GET /v3/platform/persons/{personId} |
GET /users/user/member/{id} | GET /v3/platform/accounts/{accountId} |
POST /users/organization/user (create a child account) | POST /v3/platform/accounts with parentAccountId |
GET /users/organization/user | GET /v3/platform/accounts/{accountId}/children |
GET /users/organization/user/USERID/session | Send X-Wingspan-Account: {childAccountId} with your own token, or mint an Account session with POST /v3/platform/accounts/{accountId}/sessions from a service account |
/users/authorization | /v3/platform/authorizations |
Files and webhooks
| V1 | V3 |
|---|---|
POST /files/member/private/upload, /files/member/public/upload | POST /v3/compliance/vault-files/uploads for an upload URL, then POST /v3/compliance/vault-files. See Files and documents. |
GET /files/member/private/{id}/download | GET /v3/compliance/vault-files/{fileId}/file (returns a 302 to a short-lived download URL) |
POST /integrations/webhooks/preference | POST /v3/platform/webhooks |
GET /integrations/webhooks/eventnames | GET /v3/platform/webhook-event-types |
Field changes that break code
| Area | V1 | V3 |
|---|---|---|
| Party IDs on a payable | collaboratorId, memberId, clientId | payeeId (or payeeEngagementId). Your own Account is implied. payeeAccountId and payerAccountId are read-only on responses. |
| Create a draft payable | status: "Draft" in the create body | Payables always start Created. Don't send status. |
| Why a payable is pending | metadata.pendingStatusReason | pendingStatusReason on the payable, set only while status is Pending (for example, MemberPayoutMethodNotSelected). |
| Create a payee | Collaborator create with an email | email and context (Contractor or Employee) are required. PATCH can't change context; use POST /v3/payments/payees/{payeeId}/change-context. profile takes only displayName and doingBusinessAs; legal and tax details live on the compliance entity. |
| Open or approve a payable | status: "Open" or "Approved" | POST .../open, then payerApprovalStatus: Approved through PATCH .../workflow-status. Approval is a separate field from status. |
| Payable statuses | Draft, Open, Approved, Paid, Cancelled | Created, Opened, PaymentInTransit, Paid, PartiallyPaid, PaidOffPlatform, Cancelled, Refunded, PartiallyRefunded, Returned, Pending, with approval in payerApprovalStatus |
| Reimbursable line items | reimbursableExpense: true | lineItemType: "Reimbursement" (or Expense, Services, Bonus, Commission, Custom) |
| Card fee split | creditFeeHandling: { clientPays, memberPays } on payables | Not on payables. Invoices take creditFeeHandling: { payerFeePercentage, payeeFeePercentage }. |
| Money | Decimal amounts such as 20.00 | Unchanged: decimal numbers in major units, such as 1250.00, never cents. Every document carries currency. |
| Enum values | Mostly TitleCase | All TitleCase, such as Opened and PaymentInTransit. Compare exactly. |
| Dates | ISO 8601 | Calendar dates are YYYY-MM-DD (dueDate: "2026-10-02"). Timestamps are ISO 8601 in UTC with Z. |
| Custom data | labels | metadata (up to 50 keys, 500-character values). Your own ID goes in the first-class externalId, not in metadata. A duplicate externalId on create returns 409 ResourceConflict; creates are never upserts. |
| Timestamps and attribution | events.*At on some resources | events.*At and actors.*By on every resource. |
| Acting on a child account | A session token for the child user | X-Wingspan-Account header with your own credential |
| Errors | { code, message } | { type, title, status, detail, code, requestId, errors?, detailCode? } |
IDs across versions
For a business that already has a V1 user, the V3 Account ID is the same as the V1 user ID. Person IDs are new in V3, and V3 Payee IDs are the IDs the V3 API returns. Don't build V3 Payee IDs from V1 collaborator or member IDs. To match your existing records, look payees up by your own ID or by email:
curl "https://api.wingspan.app/v3/payments/payees?filter[externalId][eq]=NW-CONTRACTOR-0042" \
-H "Authorization: Bearer $WINGSPAN_TOKEN"filter[email][eq] works the same way.
Suggested migration order
Plan the move per Account. V1/V2 and V3 use the same records, so your existing payees, payables, and invoices are available in V3. After an Account moves to V3, its V1/V2 calls fail. Build and test the complete V3 integration in your sandbox first. Then move each production Account with your Wingspan account team (see above).
Build in the sandbox first:
- Credentials. Create a V3 API key, or a service account with an API key for backend jobs. See Environments and authentication.
- Webhooks. Create a V3 subscription and store the event log cursor. Handle
Payable.Paidand the other events you need, deduplicating on eventid. See Webhooks overview. - Reads. Switch payee, payable, and invoice reads to V3. Store the V3 IDs next to your own IDs as you go.
- Payees. Move payee creation and invites to
POST /v3/payments/payeesand/invite. SetexternalIdon every create. - Requirements. Recreate collaborator-group eligibility rules as requirements on engagements.
- Payables. Move payable creation, open, and approval to V3. Add
Idempotency-KeyandIf-Matchhandling. - Paying. Replace
POST /payments/pay-approvedwith payroll runs, or pay single payables with/pay. - Everything else. Invoices, deductions, files, and child Accounts.
Then cut over each production Account with your Wingspan account team:
- Turn off the Account's V1 webhook preference immediately before the move. V1 calls fail afterward.
- Switch all of that Account's calls to V3 together.
- Confirm that the first V3 events arrive.
If your integration depends on anything in the "Not available yet" rows above, talk to your Wingspan account team before you move that Account.
Related pages
Updated 10 days ago