Invite a payee
Create a Payee, send the Wingspan invitation email, confirm it went out, and invite many Payees at once with the V3 API.
This page shows you how to invite a contractor or vendor to Wingspan so they can link their own Account, add their tax information, and choose a payout method. In the V1 API this was one call to POST /payments/collaborator that created the collaborator, invited them, and optionally added them to a collaborator group. In V3 these are separate steps, so each one can be retried and audited on its own.
Before you start
- An API token for your payer Account. See Environments and authentication.
- If you're acting for a child Account, add
X-Wingspan-Account: <accountId>to every request. See Acting on behalf of Accounts. - The contractor's email address.
1. Create the payee
curl -X POST https://api.wingspan.app/v3/payments/payees \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"email": "[email protected]",
"externalId": "ATS-04821",
"context": "Contractor",
"profile": { "displayName": "Priya Shah" }
}'// 201 Created (trimmed)
{
"id": "M9ISYbzJElXs4zIHv76rjT",
"email": "[email protected]",
"externalId": "ATS-04821",
"status": "Activated"
}Save the id. Creating a Payee doesn't send anything to the contractor. If the same externalId already exists you get 409 ResourceConflict; a matching email may instead return the existing Payee with 201. See Payees for how to handle both.
2. Send the invitation
POST /v3/payments/payees/{payeeId}/invite emails the contractor a one-time sign-in link and creates a pending link request.
curl -X POST https://api.wingspan.app/v3/payments/payees/M9ISYbzJElXs4zIHv76rjT/invite \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"customMessage": "Welcome to Northwind Staffing. Please finish setup before your first shift."
}'All body fields are optional:
| Field | What it does |
|---|---|
email | Send the invitation to this address instead of the Payee's stored email. |
customMessage | Plain text (up to 2,000 characters) added to the invitation email. |
override | Set to true to proceed when the invitation is held back by a warning that the invited person already has an existing relationship. It doesn't merge the relationships. |
// 200 OK (trimmed)
{
"id": "M9ISYbzJElXs4zIHv76rjT",
"status": "Activated",
"linkRequestStatus": "Pending"
}Wingspan finds or creates the right Person for that email and binds the link to them. You never supply a personId, and the invitation link is never returned to you.
3. Confirm the invitation went out
linkRequestStatus: Pending on the Payee confirms it. For the details, list the Payee's link requests with GET /v3/payments/payees/{payeeId}/link-requests:
curl https://api.wingspan.app/v3/payments/payees/M9ISYbzJElXs4zIHv76rjT/link-requests \
-H "Authorization: Bearer $WINGSPAN_TOKEN"// 200 OK (trimmed)
{
"data": [
{
"id": "0bmLRtEASNfDLcLxpUNkyw",
"subject": { "type": "Payee", "id": "M9ISYbzJElXs4zIHv76rjT" },
"status": "Pending",
"target": { "type": "Email", "email": "[email protected]" },
"events": { "createdAt": "2026-09-24T15:03:40Z", "pendingAt": "2026-09-24T15:03:40Z" }
}
],
"pagination": { "nextPageToken": "" }
}When the contractor accepts, the link request becomes Linked and the Payee gains a payeeAccountId. See Payee account linking.
You don't have to wait for the contractor to accept before you place them on an engagement, set up payer-managed payout routing, or create payables.
Resend or redirect an invitation
- To resend to the same address, call
POST .../inviteagain. - To send it to a different address and cancel the old link, use
PATCH /v3/payments/link-requests/{linkRequestId}with{"targetEmail": "[email protected]"}. This only works while the link request isPending. - To withdraw a pending invitation, use
DELETE /v3/payments/link-requests/{linkRequestId}. - If the contractor rejected the invitation, create a new link request with
POST /v3/payments/payees/{payeeId}/link-requests. A rejected request can't be reopened.
Invite many payees
You have two options.
Invite Payees that already exist. POST /v3/payments/payees/bulk-invite sends invitations to 1 to 100 Payees in one request. Each Payee is handled independently, and duplicate IDs are processed once.
curl -X POST https://api.wingspan.app/v3/payments/payees/bulk-invite \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "payeeIds": ["M9ISYbzJElXs4zIHv76rjT", "JDft9g7rxT3I8yuWXpMBg6"] }'// 202 Accepted
{
"accepted": 1,
"rejected": [
{ "payeeId": "JDft9g7rxT3I8yuWXpMBg6", "reason": "..." }
]
}Check rejected and fix or retry those Payees one at a time. A 503 means the service isn't available right now; retry later.
Create and invite from a roster. Use a PayeeImport batch: create the batch with POST /v3/platform/batches, add one item per contractor, then run it with POST /v3/platform/batches/{batchId}/process. configuration.context (Contractor or Employee) is required: it's the context every new Payee in the batch is created with. A newly created Payee in the batch is invited at the item's email. You can also set configuration.engagementId to place every Payee on an engagement as its item completes. That engagement's type must fit context, or the create returns 422. An item that matches an existing Payee keeps that Payee's own context. See Batches and bulk operations.
Add the payee to a group
V1 let you pass collaboratorGroupId on the invite call. In V3, add the Payee to a group with a separate call to POST /v3/payments/groups/{groupId}/members. See Groups.
If you used collaborator groups in V1 to send eligibility requirements, attach those requirements to an Engagement instead. Groups in V3 organize Payees; engagements carry requirements.
Track progress
Webhook events for invitations and link requests (Payee.Invited, PayerPayeeLinkRequest.Linked, and others) aren't available for subscription yet. Payee.Activated is subscribable, but it isn't an invitation-accepted signal. See Payees. To see who accepted, poll:
curl -G https://api.wingspan.app/v3/payments/payees \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
--data-urlencode "filter[linkRequestStatus][eq]=Linked"Poll at a modest interval and respect the RateLimit headers. See Rate limiting.
What can go wrong
| Status | code | What it means | What to do |
|---|---|---|---|
404 | ResourceNotFound | The Payee doesn't exist or isn't visible to your Account. | Check the ID and the X-Wingspan-Account header. |
409 | ResourceConflict | On create, the externalId or email is already in use. On invite, the invitation conflicts with the Payee's current state or an existing relationship. | Read detail. For an existing-relationship warning you've reviewed, resend with "override": true. |
422 | ValidationError | A field is invalid, for example a malformed email or a customMessage over 2,000 characters. | Fix the fields listed in errors[]. |
429 | RateLimitExceeded | You sent too many requests. | Wait for Retry-After seconds. |
Branch on code, not detailCode. See Errors.
Next steps
- Payee account linking: what happens on the contractor's side.
- Engagements: assign the Payee to work.
- Requirements and eligibility: make sure they can be paid.
Updated 10 days ago