Documentation Get help

Contractors

A contractor is a person or firm you engage outside payroll. Dated rate changes live in contractor rate adjustments. Team and project time lives in assignments.

  • IDs. Paths take a Flowstate ID or your externalId. A value of 24–25 lowercase letters and digits that starts with a letter is read as a Flowstate ID. Anything else is read as an externalId.
  • Dates. Send YYYY-MM-DD, which is read as 00:00 UTC, or a full ISO 8601 date-time. Dates come back as ISO 8601 date-times, e.g. 2026-07-01T00:00:00.000Z.
  • Money. Amounts come back as decimal strings, e.g. "800". Send them as numbers.
  • Scenarios. Live data only. See Scenarios.

The contractor object

FieldTypeDescription
idstringFlowstate ID. Read-only.
externalIdstring | nullYour identifier. Unique among your contractors, at most 255 characters, and can’t look like a Flowstate ID.
namestringPerson’s or firm’s name.
emailstring | nullContact email.
contractorTypestringFree text, e.g. individual, agency, consultancy.
companyIdstring | nullID of the contractor record for the firm they work through.
startDatedatetime | nullFirst day of the engagement.
endDatedatetime | nullLast day. null = open-ended.
managerIdstring | nullManaging employee’s ID.
jobRoleIdstring | nullJob role ID.
workTypeIdstring | nullResource type ID. Read-only over this API.
geographyIdstring | nullLocation ID.
rateTypestring | nullPeriod of rate: hourly, daily, monthly or annually.
ratedecimal string | nullRate on the contractor record. Dated changes are rate adjustments.
currencyCodestring | nullISO 4217 code for rate.
organizationId, sourceSystem, sourceSystemId, lastSyncedAt, metadataRecord-keeping. Read-only. sourceSystem is api for records created here.
createdAt, updatedAtdatetimeRead-only.
customAttributesarrayCustom attribute values. Returned by retrieve only.

Endpoints

MethodPathPermission
GET/api/v1/org/:orgId/contractorsView Contractors (team_contractors_view)
GET/api/v1/org/:orgId/contractors/:idView Contractors (team_contractors_view)
POST/api/v1/org/:orgId/contractorsCreate Contractors (team_contractors_create)
PATCH/api/v1/org/:orgId/contractors/:idUpdate Contractors (team_contractors_update)
DELETE/api/v1/org/:orgId/contractors/:idDelete Contractors (team_contractors_delete)

Sub-resources: rate adjustments at /contractors/:contractorId/rate-adjustments and custom attribute values at /contractors/:id/custom-attributes.


List contractors

GET /api/v1/org/:orgId/contractors

Permission: View Contractors (team_contractors_view). With include=currentRate or include=rateHistory, you also need View Detailed Financials (financials_view_detailed).

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number, from 1.
limitinteger20Page size, 1–100.
searchstringCase-insensitive match on name or email.
sortBystringnamename, email, contractorType or startDate. Any other value sorts by name.
sortDirstringascasc or desc.
includestringComma-separated. See Includes.

Includes

KeyAddsNeeds
currentRateThe rate adjustment with the latest effectiveDate, or null.financials_view_detailed
rateHistoryEvery rate adjustment, latest effectiveDate first.financials_view_detailed
assignmentsEvery team and project assignment, past and current, earliest startDate first. Each is { id, type, targetId, fte, startDate, endDate, role, createdAt, updatedAt }, where type is team or project.

Example request

curl "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors?sortBy=startDate&sortDir=desc" \
  -H "Authorization: Bearer private_..."

Example response

{
  "data": [
    {
      "id": "cmf2k8x1q0060ab2cd3ef4gh5",
      "organizationId": "cmf2k8x1q0000ab2cd3ef4gh5",
      "name": "Priya Sharma",
      "externalId": "CTR-311",
      "email": "priya@sharmaconsulting.co.uk",
      "contractorType": "individual",
      "companyId": null,
      "geographyId": "cmf2k8x1q0012ab2cd3ef4gh5",
      "rateType": "daily",
      "rate": "800",
      "currencyCode": "GBP",
      "startDate": "2025-01-15T00:00:00.000Z",
      "endDate": "2026-12-31T00:00:00.000Z",
      "managerId": "cmf2k8x1q0001ab2cd3ef4gh5",
      "jobRoleId": "cmf2k8x1q0010ab2cd3ef4gh5",
      "workTypeId": null,
      "sourceSystem": "api",
      "sourceSystemId": null,
      "lastSyncedAt": "2025-01-10T08:00:00.000Z",
      "metadata": {},
      "createdAt": "2025-01-10T08:00:00.000Z",
      "updatedAt": "2026-03-20T09:00:00.000Z"
    }
  ],
  "meta": { "page": 1, "limit": 20, "total": 1, "hasNextPage": false }
}

Errors

StatusCodeWhen
400VALIDATION_ERRORInvalid query parameter, or an include key not in the table.
403FORBIDDENA rate include without financials_view_detailed.

Retrieve a contractor

GET /api/v1/org/:orgId/contractors/:id

Permission: View Contractors (team_contractors_view). The rate includes also need financials_view_detailed.

Takes the same include parameter as List contractors. The response is the contractor object, with customAttributes and any includes.

Example request

curl "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors/CTR-311?include=assignments" \
  -H "Authorization: Bearer private_..."

Example response

{
  "data": {
    "id": "cmf2k8x1q0060ab2cd3ef4gh5",
    "name": "Priya Sharma",
    "externalId": "CTR-311",
    "…": "other contractor fields",
    "customAttributes": [],
    "assignments": [
      {
        "id": "cmf2k8x1q0052ab2cd3ef4gh5",
        "type": "team",
        "targetId": "cmf2k8x1q0020ab2cd3ef4gh5",
        "fte": 1,
        "startDate": "2025-01-15T00:00:00.000Z",
        "endDate": "2026-12-31T00:00:00.000Z",
        "role": null,
        "createdAt": "2025-01-10T08:00:00.000Z",
        "updatedAt": "2025-01-10T08:00:00.000Z"
      }
    ]
  }
}

Errors

StatusCodeWhen
400VALIDATION_ERRORAn include key not in the table.
403FORBIDDENA rate include without financials_view_detailed.
404NOT_FOUNDNo contractor with that ID or externalId.

Create a contractor

POST /api/v1/org/:orgId/contractors

Permission: Create Contractors (team_contractors_create).

The contractor is created in one transaction with any nested rateAdjustment, teamAssignment and projectAssignment you send.

Body parameters

FieldTypeRequiredDescription
namestringYes
contractorTypestringYese.g. individual.
externalIdstring | nullNoAt most 255 characters. Can’t look like a Flowstate ID.
emailstring | nullNoValid email address.
companyIdstring | nullNoFlowstate ID of the firm’s contractor record.
startDatedate | nullNo
endDatedate | nullNo
managerIdstring | nullNoEmployee ID or externalId.
jobRoleIdstring | nullNoJob role ID. Wins over jobRole.
jobRoleobjectNoFind or create a job role by externalId or title. Same shape as on employees.
geographyIdstring | nullNoLocation ID.
rateTypestring | nullNohourly, daily, monthly or annually.
ratenumber | nullNo≥ 0.
currencyCodestring | nullNoThree uppercase letters.
rateAdjustmentobjectNoFirst dated rate. See rateAdjustment.
teamAssignmentobjectNoSame shape as on employees.
projectAssignmentobjectNoSame shape as on employees.

rateAdjustment

FieldTypeRequiredDescription
effectiveDatedateYes
rateTypestringYeshourly, daily, monthly or annually.
ratenumberYes≥ 0.
currencyCodestringYesThree uppercase letters.
reasonstring | nullNo

Example request

curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Marcus Johnson",
    "email": "marcus@devshop.io",
    "contractorType": "individual",
    "externalId": "CTR-412",
    "startDate": "2026-10-01",
    "endDate": "2027-03-31",
    "managerId": "EMP-1042",
    "rateType": "daily",
    "rate": 650,
    "currencyCode": "GBP",
    "rateAdjustment": {
      "effectiveDate": "2026-10-01",
      "rateType": "daily",
      "rate": 650,
      "currencyCode": "GBP",
      "reason": "Initial rate"
    },
    "teamAssignment": {
      "teamId": "cmf2k8x1q0020ab2cd3ef4gh5",
      "fte": 1,
      "startDate": "2026-10-01",
      "endDate": "2027-03-31"
    }
  }'

Example response

201 Created. When you sent them, the created records come back under rateAdjustment, teamAssignment and projectAssignment.

{
  "data": {
    "id": "cmf2k8x1q0061ab2cd3ef4gh5",
    "organizationId": "cmf2k8x1q0000ab2cd3ef4gh5",
    "name": "Marcus Johnson",
    "externalId": "CTR-412",
    "email": "marcus@devshop.io",
    "contractorType": "individual",
    "companyId": null,
    "geographyId": null,
    "rateType": "daily",
    "rate": "650",
    "currencyCode": "GBP",
    "startDate": "2026-10-01T00:00:00.000Z",
    "endDate": "2027-03-31T00:00:00.000Z",
    "managerId": "cmf2k8x1q0001ab2cd3ef4gh5",
    "jobRoleId": null,
    "workTypeId": null,
    "sourceSystem": "api",
    "sourceSystemId": null,
    "lastSyncedAt": "2026-09-14T09:00:00.000Z",
    "metadata": {},
    "createdAt": "2026-09-14T09:00:00.000Z",
    "updatedAt": "2026-09-14T09:00:00.000Z",
    "rateAdjustment": {
      "id": "cmf2k8x1q0065ab2cd3ef4gh5",
      "liveContractorId": "cmf2k8x1q0061ab2cd3ef4gh5",
      "effectiveDate": "2026-10-01T00:00:00.000Z",
      "rateType": "daily",
      "rate": "650",
      "currencyCode": "GBP",
      "reason": "Initial rate",
      "…": "other rate adjustment fields"
    },
    "teamAssignment": {
      "id": "cmf2k8x1q0053ab2cd3ef4gh5",
      "liveContractorId": "cmf2k8x1q0061ab2cd3ef4gh5",
      "liveTeamId": "cmf2k8x1q0020ab2cd3ef4gh5",
      "fte": 1,
      "startDate": "2026-10-01T00:00:00.000Z",
      "endDate": "2027-03-31T00:00:00.000Z",
      "…": "other assignment fields"
    }
  }
}

Errors

StatusCodeWhen
400VALIDATION_ERRORThe body is invalid, teamAssignment.teamId or projectAssignment.projectId matches nothing, or externalId is already used.
404NOT_FOUNDmanagerId matches no employee, or jobRole has only an externalId that matches no role.
501NOT_IMPLEMENTEDscenarioId was sent.

Update a contractor

PATCH /api/v1/org/:orgId/contractors/:id

Permission: Update Contractors (team_contractors_update).

Takes the create body with every field optional. Send only what changes. null clears a nullable field. Nested rateAdjustment, teamAssignment and projectAssignment always add a record and never change an existing one.

Example request

curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors/CTR-311" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{ "endDate": "2027-06-30" }'

Example response

200 OK. The response is the updated contractor, plus any nested records created.

Errors

As for Create a contractor, plus 404 NOT_FOUND when no contractor has that ID or externalId.


Delete a contractor

DELETE /api/v1/org/:orgId/contractors/:id

Permission: Delete Contractors (team_contractors_delete).

Permanent. The following are deleted with the contractor:

  • rate adjustments
  • assignments
  • effort records

Where another record points to the contractor as its firm (companyId) or as a vacancy filler, that link is set to null. To end an engagement, set endDate instead.

Example request

curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors/CTR-311" \
  -H "Authorization: Bearer private_..."

Example response

{ "data": { "id": "cmf2k8x1q0060ab2cd3ef4gh5", "deleted": true } }

Errors

StatusCodeWhen
404NOT_FOUNDNo contractor with that ID or externalId.
501NOT_IMPLEMENTEDscenarioId was sent.

Tasks

End an engagement early

PATCH /contractors/CTR-311 with { "endDate": "2026-11-30" }. Assignments keep their own end dates, so end those through Assignments.

Change a rate from a date

Add a rate adjustment with the new effectiveDate. Earlier rates stay in the history.