Custom fields
Define your own typed fields on Wingspan payees, line items, and engagements, then set, read, and search their values through the V3 API.
This page shows you how to store your own data on Wingspan records, such as an internal contractor ID on a payee or a cost center on a payable's line items. You define each custom field once for your Account, then set values on the records it applies to.
Custom fields are typed and validated. If you only need free-form tags, use metadata instead. Custom fields are for data you want checked on write, required on import, and searchable.
Define a field
Create a custom field with a display name, a machine key, a data type, and the kind of record it applies to:
curl -X POST "https://api.wingspan.app/v3/platform/custom-fields" \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "Contractor ID",
"key": "contractorId",
"dataType": "String",
"resourceType": "Payee",
"isRequired": false
}'// (trimmed)
{
"id": "Xa2Lk8Pq5Rt9Vm3Wz7NcYd",
"name": "Contractor ID",
"key": "contractorId",
"dataType": "String",
"resourceType": "Payee",
"isRequired": false
}| Field | Values |
|---|---|
name | The label people see. |
key | The machine name you use in API calls. For payee fields, keys may use letters, digits, _, and - only. |
dataType | String, Number, Boolean, Datetime, or ValueSet (a list of strings). |
resourceType | Payee, LineItem, or Engagement. |
isRequired | When true, imports must include a value for this field. Defaults to false. |
Custom field definitions belong to the Account. To define fields for a child Account, send X-Wingspan-Account. See Acting on behalf of Accounts.
Set and read values on a payee
Each payee has a map of custom field values keyed by the field's key.
Update a payee's custom fields with the keys you want to change:
curl -X PATCH "https://api.wingspan.app/v3/payments/payees/Rw5Nc2Hk8LqZ4tVp9XmBsa/custom-fields" \
-H "Authorization: Bearer $WINGSPAN_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"contractorId": "RN-04821",
"startedAt": "2026-01-15",
"badgeNumber": null
}'- A key with a value sets it. The value is checked against the field's
dataType. - A key set to
nullclears that value. - Keys you leave out keep their values.
- An empty object (
{}) is rejected. ValueSetvalues can be read here but can't be written through this endpoint.- A key with no custom field definition for payees is rejected with
422 ValidationError;errors[].fieldisvalues.{key}anderrors[].codeisUnsupportedValue. This applies to clearing a key withnulltoo. Nothing in the request is saved.
Get a payee's custom fields returns the same map. A payee with no values returns {}.
Payee custom fields are the payer's own data about the relationship.
Set values on line items
Set line item custom field values when you import payables with a PayableImport batch. Each item's data.customFields takes values keyed by the LineItem field's key, and every required LineItem field must be present. See Batches and bulk operations.
A PayeeImport batch sets payee custom field values the same way, through each item's data.customFields.
Setting values on engagement custom fields isn't available in the V3 API yet. Contact support if you need it.
Search by custom field
The payee search rows can filter on payee custom fields with filter[customFields.{key}][eq]=..., as well as ne, anyOf, noneOf, and contains:
curl -g "https://api.wingspan.app/v3/search/payee-rows?filter[customFields.contractorId][eq]=RN-04821" \
-H "Authorization: Bearer $WINGSPAN_TOKEN"Matching on custom fields in search is by analyzed terms, not exact string equality, and it's case-insensitive. A key that isn't defined matches no rows. See Search.
Manage definitions
| Task | Endpoint |
|---|---|
List definitions, optionally by filter[resourceType][eq] or filter[resourceType][anyOf][] | GET /v3/platform/custom-fields |
| Read one definition | GET /v3/platform/custom-fields/{fieldId} |
Change a definition's name, key, isRequired, or other fields | PATCH /v3/platform/custom-fields/{fieldId} |
| Delete a definition | DELETE /v3/platform/custom-fields/{fieldId} |
Important: Deleting a definition also deletes every value stored for it, on every record. Export the values first if you need them.
Renaming a key changes the name you use in API calls and imports. Update your integration at the same time.
From V1
| V1 | V3 |
|---|---|
| Custom collaborator fields | Custom fields with resourceType: Payee |
| Custom line item fields | Custom fields with resourceType: LineItem |
| Columns named after the field in the bulk payable upload | data.customFields on PayableImport batch items |
| Editing fields on a collaborator or through the collaborator bulk upload | PATCH /v3/payments/payees/{payeeId}/custom-fields, or data.customFields on PayeeImport batch items |
payerOwnedData.customFields.{key} search filter | filter[customFields.{key}][eq] on /v3/search/payee-rows |
What can go wrong
| Response | Cause | Fix |
|---|---|---|
422 ValidationError on a value | The value doesn't match the field's dataType | Check the definition with GET /v3/platform/custom-fields |
422 ValidationError on a key | A payee field key uses characters other than letters, digits, _, and - | Rename the key |
422 ValidationError on an empty body | PATCH with {} | Send at least one key |
| Batch item rejected | A required custom field is missing from data.customFields | Add the value, or make the field optional |
Related pages
Updated 10 days ago