Gig marketplaces

How delivery, mobility, and on-demand marketplaces onboard and pay large contractor pools with the Wingspan V3 API. Account model, daily pay cycle, and pitfalls.

This guide is for marketplaces that pay many contractors often: delivery, ride-hailing and mobility, home services, and freelance platforms. It covers how to structure markets as Accounts, how to onboard a large pool (including migrating an existing one), how to run a daily pay cycle, and what to listen for.

Typical requirements

  • Thousands of contractors, high turnover, and seasonal surges in onboarding.
  • Pay that's calculated daily or per job, often from a dispatch or trip system.
  • Several operating companies (one per city or region), each with its own tax ID, so payments and 1099s must be attributed to the right entity.
  • Migrating an existing contractor base without asking everyone to start over.
  • Handling returned payments without manual spreadsheets.

Structure markets as Accounts

If each market pays contractors under its own tax ID, give each one a child Account under a parent:

flowchart TD
  P[Parent Account: Northwind Mobility] --> M1[Market Account: Indianapolis]
  P --> M2[Market Account: Albuquerque]
  M1 --> D1[Payee: driver]
  M2 --> D2[Payee: driver]

Your backend uses one ServiceAccount key on the parent and names the market on each call with X-Wingspan-Account. Payables, funding, and year-end forms then belong to that market. A driver who works in two markets has a Payee in each and one Account of their own. See Parent and child Accounts and Embed Wingspan in your app.

A single Account is fine if all contractors are paid by one legal entity.

Map your objects

Marketplace objectWingspan objectNotes
Market or operating companyChild AccountexternalId = your market code
Driver, courier, or providerPayee on a Contractor engagementexternalId = your worker ID
Trip, delivery, or jobLine item on a payableUse line-item custom fields for booking IDs
Daily or weekly earningsPayableexternalId = your earnings record ID, so a retry can't pay twice
Amount the contractor owes youDeductionTaken from their pay
Payout sentPayout, listed under the payableRead with GET /v3/payments/payouts

Onboard the contractor pool

New contractors. Create each Payee, place it on an engagement, and invite it. The contractor completes onboarding in Wingspan's hosted flow. See Invite a payee.

A surge or a migration. Use a PayeeImport batch. Each item matches an existing payee by payeeId, then externalId, then email, and updates it; otherwise it creates the payee and sends the invite. Set configuration.context to Contractor (it's required), and optionally configuration.engagementId to place every payee in the batch on one engagement. See Batches and bulk operations.

Contractors whose Account you already manage. If your platform controls the contractor's Wingspan Account (for example, from an earlier integration, with the contractor's agreement), a person with access to both Accounts can bind the Payee to it with POST /v3/payments/payees/{payeeId}/associate-account, recording the authority as MigrationImported or PlatformAsserted. The contractor doesn't have to accept a new invite. Talk to us before a large migration so we can check which existing verifications carry over.

Run a daily pay cycle

sequenceDiagram
  participant D as Dispatch system
  participant WS as Wingspan API
  D->>WS: POST /v3/platform/batches (type PayableImport)
  D->>WS: POST /batches/{id}/items, one per driver-day
  D->>WS: POST /batches/{id}/process
  D->>WS: GET /batches/{id}/summary until Completed
  WS-->>D: Payable.Paid, Payable.Returned webhooks
  1. At the end of the day, create a PayableImport batch on the market's Account with a processingStrategy.
  2. Add one item per payable. Give each item a uniqueReferenceKey (for example, the driver ID plus the date), the payeeEngagementId, a description, and a totalCost. A negative amount creates a deduction against the payee instead of a payable.
  3. Process the batch with POST /v3/platform/batches/{batchId}/process and follow it with GET /v3/platform/batches/{batchId}/summary. Items that fail carry field-level errors; fix and resubmit them in a new batch.
  4. Pay the resulting payables, or set payableStatus: Opened and payerApprovalStatus: Approved on the batch so they're ready for a contractor payroll run. See Bulk payables and Payroll runs.

For one-off payments (a bonus or an adjustment), create a single payable instead. See Create a payable.

Listen for results

EventWhat to do
Payable.PaidMark the earnings record paid in your system. This is Wingspan's internal state, not confirmation that the driver's bank received the money.
Payable.ReturnedThe payment came back. Returned is terminal: create a new payable after the driver fixes their payout method.
FundsMovement.Failed, FundsMovement.ReturnedMoney-movement detail for reconciliation.

Dedupe on the event id, and recover anything you missed from GET /v3/platform/events. Whether a driver can be paid (paymentsEligibility on the payee engagement) has no webhook yet; poll it before you create earnings for a new driver. See Paid vs DepositConfirmed and Recover missed events.

Contractor financial features

Contractors choose how they get paid, including faster payout methods where offered, in their own onboarding and settings. Debit cards and a tax withholding account are available on the contractor's own Account through /v3/finance/cards and /v3/finance/tax-withholdings. See Payout methods. Insurance and benefits products aren't available in the V3 API yet.

Pitfalls

  • Paying the same day twice. Use a stable externalId or uniqueReferenceKey per earnings record, and an Idempotency-Key per request.
  • One market's payments on another's Account. Always send the market's X-Wingspan-Account. Year-end forms follow the Account that paid.
  • Creating earnings for drivers who can't be paid yet. Check paymentsEligibility first, or expect those payables to wait.
  • Hitting rate limits during the daily run. Adding batch items is still one write call per item. Watch the RateLimit header, pace your calls, and back off on 429 using Retry-After. See Rate limiting.

Related pages


Did this page help you?