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 NotMigrated error 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 conceptV3 conceptNotes
UserPersonA human with a login.
Member (the contractor)Payee (your record of them); the contractor's own AccountmemberId 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.
CollaboratorPayeecollaboratorId becomes payeeId. Each payee has a required context, Contractor or Employee.
Member-client relationshipPayee and Payer, plus their engagements
Collaborator groupGroup, for organizing payees; Engagement, for requirementsV3 groups don't carry requirements. Attach requirements to an engagement instead.
Eligibility requirementRequirementDefinition (template) and PayeeRequirement (per payee)Under /v3/onboarding.
Engagement and assignment (app terms since February 2026)Engagement and PayeeEngagementEngagement types are Contractor, Employee, EmployeeOfRecord, and AgentOfRecord (EngagementType).
PayablePayableSame name, new shape. See Field changes.
Invoice (member invoice)InvoiceThe payee's view of what they're owed.
Client invoicePayable, or paying an InvoiceA payer sees what it owes as payables, and pays a payee's invoice with POST /v3/payments/invoices/{invoiceId}/pay.
Invoice templateRecurring invoice/v3/payments/recurring-invoices.
Pay approvedPayroll runCreate a Contractor run, then finalize it.
Bulk collaborator or payable batchBatch with type: PayeeImport or PayableImport/v3/platform/batches.
Organization child accountChild Account (parentAccountId)Act on it with X-Wingspan-Account.
API tokenAPI key, owned by a Person or ServiceAccountNew: ServiceAccount, a machine identity for backend jobs.
Files (private and public)Vault filesOne upload location: /v3/compliance/vault-files.
labelsmetadataUp 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)

V1V3
POST /payments/collaboratorPOST /v3/payments/payees (with the required context), then POST /v3/payments/payees/{payeeId}/invite
GET /payments/collaboratorGET /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}/eventsGET /v3/payments/payees/{payeeId}/events
GET /payments/collaborator/{id}/download-w9Not 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-groupGET, POST /v3/payments/groups
/payments/collaborator-group/{id}/eligibility-requirement/...POST /v3/payments/engagements/{engagementId}/requirements
/payments/collaborator-settings/payment-eligibilityRequirement 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 itemsPOST /v3/platform/batches with type: PayeeImport, then POST /v3/platform/batches/{batchId}/items and POST /v3/platform/batches/{batchId}/process

Payables and payroll

V1V3
POST /payments/payablePOST /v3/payments/payables
GET /payments/payableGET /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: OpenPOST /v3/payments/payables/{payableId}/open
PATCH /payments/payable/{id} with status: ApprovedPATCH /v3/payments/payables/{payableId}/workflow-status with payerApprovalStatus: Approved
PATCH /payments/payable/{id} with status: CancelledPOST /v3/payments/payables/{payableId}/cancel
DELETE /payments/payable/{id}DELETE /v3/payments/payables/{payableId}
No equivalent in the V1 referencePOST /v3/payments/payables/{payableId}/pay
GET /payments/payroll/immediate/payableGET /v3/payments/payables?filter[payerApprovalStatus][eq]=Approved&filter[status][eq]=Opened
POST /payments/pay-approvedPOST /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 itemsPOST /v3/platform/batches with type: PayableImport, then add items and process
POST /payments/client-deduction, /payments/collaborator-deductionPOST /v3/payments/deductions
GET /payments/client-deductionGET /v3/payments/deductions

Invoices

V1V3
POST /payments/invoicePOST /v3/payments/invoices
GET /payments/invoiceGET /v3/payments/invoices
POST /payments/invoice/{id}/sendPOST /v3/payments/invoices/{invoiceId}/send
POST /payments/invoice/{id}/generateGET /v3/payments/invoices/{invoiceId}/pdf (returns a 302 to a short-lived download URL)
POST /payments/invoice/{id}/refundPOST /v3/payments/invoices/{invoiceId}/refund
POST /payments/client/invoice/{id}/payPOST /v3/payments/invoices/{invoiceId}/pay
GET /payments/client/invoiceGET /v3/payments/payables (what you owe)
/payments/invoice-template/v3/payments/recurring-invoices

Users, sessions, and accounts

V1V3
POST /users/sessionPOST /v3/platform/sessions with sessionType: Person
POST, PATCH /users/session/otpPOST, 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/userGET /v3/platform/accounts/{accountId}/children
GET /users/organization/user/USERID/sessionSend 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

V1V3
POST /files/member/private/upload, /files/member/public/uploadPOST /v3/compliance/vault-files/uploads for an upload URL, then POST /v3/compliance/vault-files. See Files and documents.
GET /files/member/private/{id}/downloadGET /v3/compliance/vault-files/{fileId}/file (returns a 302 to a short-lived download URL)
POST /integrations/webhooks/preferencePOST /v3/platform/webhooks
GET /integrations/webhooks/eventnamesGET /v3/platform/webhook-event-types

Field changes that break code

AreaV1V3
Party IDs on a payablecollaboratorId, memberId, clientIdpayeeId (or payeeEngagementId). Your own Account is implied. payeeAccountId and payerAccountId are read-only on responses.
Create a draft payablestatus: "Draft" in the create bodyPayables always start Created. Don't send status.
Why a payable is pendingmetadata.pendingStatusReasonpendingStatusReason on the payable, set only while status is Pending (for example, MemberPayoutMethodNotSelected).
Create a payeeCollaborator create with an emailemail 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 payablestatus: "Open" or "Approved"POST .../open, then payerApprovalStatus: Approved through PATCH .../workflow-status. Approval is a separate field from status.
Payable statusesDraft, Open, Approved, Paid, CancelledCreated, Opened, PaymentInTransit, Paid, PartiallyPaid, PaidOffPlatform, Cancelled, Refunded, PartiallyRefunded, Returned, Pending, with approval in payerApprovalStatus
Reimbursable line itemsreimbursableExpense: truelineItemType: "Reimbursement" (or Expense, Services, Bonus, Commission, Custom)
Card fee splitcreditFeeHandling: { clientPays, memberPays } on payablesNot on payables. Invoices take creditFeeHandling: { payerFeePercentage, payeeFeePercentage }.
MoneyDecimal amounts such as 20.00Unchanged: decimal numbers in major units, such as 1250.00, never cents. Every document carries currency.
Enum valuesMostly TitleCaseAll TitleCase, such as Opened and PaymentInTransit. Compare exactly.
DatesISO 8601Calendar dates are YYYY-MM-DD (dueDate: "2026-10-02"). Timestamps are ISO 8601 in UTC with Z.
Custom datalabelsmetadata (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 attributionevents.*At on some resourcesevents.*At and actors.*By on every resource.
Acting on a child accountA session token for the child userX-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:

  1. Credentials. Create a V3 API key, or a service account with an API key for backend jobs. See Environments and authentication.
  2. Webhooks. Create a V3 subscription and store the event log cursor. Handle Payable.Paid and the other events you need, deduplicating on event id. See Webhooks overview.
  3. Reads. Switch payee, payable, and invoice reads to V3. Store the V3 IDs next to your own IDs as you go.
  4. Payees. Move payee creation and invites to POST /v3/payments/payees and /invite. Set externalId on every create.
  5. Requirements. Recreate collaborator-group eligibility rules as requirements on engagements.
  6. Payables. Move payable creation, open, and approval to V3. Add Idempotency-Key and If-Match handling.
  7. Paying. Replace POST /payments/pay-approved with payroll runs, or pay single payables with /pay.
  8. Everything else. Invoices, deductions, files, and child Accounts.

Then cut over each production Account with your Wingspan account team:

  1. Turn off the Account's V1 webhook preference immediately before the move. V1 calls fail afterward.
  2. Switch all of that Account's calls to V3 together.
  3. 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


Did this page help you?