Worksites
Record the physical locations where your employees work, mark a primary worksite, and assign worksites to Employee engagements in the V3 API.
A worksite is a physical work location your Account owns, such as an office, a clinic, or a warehouse. This page shows you how to create worksites, choose a primary one, and assign them to Employee engagements.
What a worksite is for
Worksites apply to Employee work. When an Employee engagement names a worksite, Wingspan uses that worksite for state income tax assignment on the employee's pay statements. An Employee engagement with no worksite is treated as remote work.
Contractor engagements don't need a worksite.
| Where you set it | Field | What it means |
|---|---|---|
| An Engagement template | worksiteId or isRemote | The default location for Employee work on that template. |
| A PayeeEngagement | worksiteId | Where this employee works. Leave it out, or set it to null, for remote work. |
Create a worksite
POST /v3/payments/worksites creates one. name and address are required, and address.countryCode is required inside the address.
curl -X POST https://api.wingspan.app/v3/payments/worksites \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Northwind Sacramento clinic",
"externalId": "site-sac-01",
"address": {
"line1": "200 Example Way",
"city": "Sacramento",
"subdivision": "US-CA",
"postalCode": "95814",
"countryCode": "US"
},
"isPrimary": true
}'// 201 Created (trimmed)
{
"id": "Wk4sT9pLq2Xn7Rb3Vd8HcA",
"accountId": "z9uv0jPAxTqSLs5UKwv1DE",
"externalId": "site-sac-01",
"name": "Northwind Sacramento clinic",
"address": {
"line1": "200 Example Way",
"city": "Sacramento",
"subdivision": "US-CA",
"postalCode": "95814",
"countryCode": "US"
},
"isPrimary": true,
"events": { "createdAt": "2026-09-25T15:00:00Z" }
}| Field | What it's for |
|---|---|
name | Required. |
address | Required. line1, line2, city, subdivision (an ISO 3166-2 code such as US-CA), postalCode, and countryCode (ISO 3166-1 alpha-2, required). The address can't be changed later. |
isPrimary | Makes this your Account's primary worksite. Defaults to false. |
externalId | Your own ID for the location. A duplicate returns 409 ResourceConflict. Create is never an upsert. |
metadata | Up to 50 keys of your own data. |
The worksite belongs to the Account you're acting as. To create one for a child Account, send X-Wingspan-Account. See Acting on behalf of Accounts.
List and read worksites
GET /v3/payments/worksiteslists your worksites, paginated. Filter withfilter[isPrimary][eq]=trueorfilter[externalId][eq]=site-sac-01.GET /v3/payments/worksites/{worksiteId}returns one.
curl -G https://api.wingspan.app/v3/payments/worksites \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
--data-urlencode "filter[externalId][eq]=site-sac-01"Update a worksite
PATCH /v3/payments/worksites/{worksiteId} changes name, isPrimary, externalId, and metadata. Fields you leave out stay the same.
curl -X PATCH https://api.wingspan.app/v3/payments/worksites/Wk4sT9pLq2Xn7Rb3Vd8HcA \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "name": "Northwind Sacramento clinic (Midtown)" }'- Address. You can't change it. If a location moves, create a new worksite and point engagements at it.
- Primary. Send
"isPrimary": trueto make a worksite primary. The previous primary is demoted. You can't sendfalse; to move the primary flag, promote another worksite instead. externalId. Send an empty string to clear it.metadata. Sending it replaces the stored metadata. You can't clear metadata through this endpoint.
Delete a worksite
DELETE /v3/payments/worksites/{worksiteId} returns 204. It returns 409 if the worksite is your primary worksite or if any engagement still references it. Move those engagements to another worksite, or promote another worksite to primary, then delete.
Assign a worksite to an employee
Set worksiteId when you create the PayeeEngagement, or change it later with PATCH:
curl -X PATCH https://api.wingspan.app/v3/payments/payee-engagements/ru9zEafz6i4WRlIOA10wAk \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "worksiteId": "Wk4sT9pLq2Xn7Rb3Vd8HcA" }'Send "worksiteId": null to switch the engagement to remote work. See Engagements and Payroll runs.
What can go wrong
Status and code | Cause | Fix |
|---|---|---|
409 ResourceConflict on create | The externalId is already used by another worksite. | Look it up with filter[externalId][eq]. |
409 on delete | The worksite is primary, or an engagement still uses it. | Promote another worksite or reassign the engagements first. |
422 ValidationError on create | A required field is missing, such as address.countryCode, or you sent a field the endpoint doesn't accept. | Read errors[]. |
422 ValidationError on update | You sent isPrimary: false or an address. | Promote another worksite, or create a new one for a new address. |
Webhooks
Worksites don't emit webhook events. Read the worksite after each change.
Related
Updated 10 days ago