Appearance
Vacancies
Manage open positions and the hiring pipeline. Vacancies represent roles that need to be filled, with target dates, salary ranges, and team assignments. A vacancy can be filled by either a new employee or a new contractor — the Fill endpoint creates the filler atomically and transfers the vacancy's team/project assignments.
A vacancy is considered "filled" while its filler is currently active (i.e. their endDate is either unset or in the future). Once the filler's endDate passes, the vacancy is treated as open again and resumes contributing to the forecast.
New to Flowstate?
Read the Domain Model to understand the vacancy-to-employee/contractor fill workflow and how vacancies feed into headcount forecasting.
Vacancy Object
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (CUID format). System-generated. Read-only. |
externalId | string | null | Your external identifier for this vacancy. Unique within your organization. Use for lookups via path parameters. |
role | string | Vacancy title / role name (e.g. "Senior Backend Engineer"). |
description | string | null | Detailed description of the role and responsibilities. |
status | string | Vacancy status: "open", "filled", "cancelled", or "on_hold". |
fte | number | Full-Time Equivalent for this vacancy. 1.0 = full-time, 0.5 = half-time. Range: 0--1. |
targetStartDate | date (YYYY-MM-DD) | null | Desired start date for the new hire. Used in headcount forecasting. |
targetFillDate | date (YYYY-MM-DD) | null | Target date to fill this vacancy by. Used for recruitment planning. |
jobRoleId | string | null | Job role classification. Use GET /job-roles for valid IDs. |
workTypeId | string | null | Work arrangement type. References a Work Type. |
geographyId | string | null | Target hire location/region. References a Location. Affects salary range expectations. |
salaryMin | number | null | Minimum annual salary for the role. Decimal with 2 decimal places. |
salaryMax | number | null | Maximum annual salary for the role. Decimal with 2 decimal places. |
currencyCode | string | null | ISO 4217 currency code for the salary range (e.g. "GBP"). |
filledByLiveEmployeeId | string | null | ID of the employee who filled this vacancy. Set by the Fill endpoint (employee branch) or manually via PATCH. References an Employee. Mutually exclusive with filledByLiveContractorId. |
filledByLiveContractorId | string | null | ID of the contractor who filled this vacancy. Set by the Fill endpoint (contractor branch) or manually via PATCH. References a Contractor. Mutually exclusive with filledByLiveEmployeeId. |
isFilled | boolean | Derived. true when the vacancy is currently filled — i.e. one of the filledBy* FKs is set and the filler's endDate is unset or in the future. Once the filler's endDate passes, isFilled flips back to false and the vacancy resumes contributing to forecast cost. |
hiringManagerId | string | null | ID of the employee responsible for hiring this role. References an Employee. |
createdAt | datetime (ISO 8601) | When the record was created. Read-only. |
updatedAt | datetime (ISO 8601) | When the record was last modified. Read-only. |
customAttributes | array | Custom attribute values for this vacancy. Returned on GET-by-ID. See Custom Attributes. |
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /org/:orgId/vacancies | List vacancies |
GET | /org/:orgId/vacancies/:id | Get vacancy |
POST | /org/:orgId/vacancies | Create vacancy |
PATCH | /org/:orgId/vacancies/:id | Update vacancy |
DELETE | /org/:orgId/vacancies/:id | Delete vacancy |
POST | /org/:orgId/vacancies/:id/fill | Fill a vacancy (convert to employee) |
External ID Support
The :id path parameter accepts either a Flowstate CUID (e.g. clx1a2b3c4d5e6f7g8h9) or your externalId. The format is auto-detected.
List Vacancies
GET /org/:orgId/vacanciesReturns a paginated list of vacancies in the organization.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (1-based). |
limit | integer | 20 | Records per page. Min 1, max 100. |
search | string | -- | Free-text search by role or description. |
sortBy | string | role | Field to sort by. |
sortDir | string | asc | Sort direction: asc or desc. |
scenarioId | string | -- | Scenario ID for what-if queries. |
Example Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies?sortBy=targetStartDate&sortDir=asc" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": [
{
"id": "clx5v6w7x8y9z0a1b2c3",
"externalId": null,
"role": "Senior Backend Engineer",
"description": "Backend engineer for the payments team, focusing on billing infrastructure.",
"status": "open",
"fte": 1.0,
"targetStartDate": "2026-06-01",
"targetFillDate": "2026-05-15",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"salaryMin": 120000,
"salaryMax": 160000,
"currencyCode": "USD",
"filledByLiveEmployeeId": null,
"hiringManagerId": "clx9m4n5o6p7q8r9",
"createdAt": "2026-01-15T10:00:00Z",
"updatedAt": "2026-03-01T16:45:00Z"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 23,
"hasNextPage": true
}
}Get Vacancy
GET /org/:orgId/vacancies/:idReturns a single vacancy by ID.
Including Related Data
| Include Key | Description |
|---|---|
assignments | All team and project assignments for this vacancy. |
filledByEmployee | The employee record that filled this vacancy (if status is "filled"). |
GET /org/:orgId/vacancies/:id?include=assignments,filledByEmployeeExample Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3?include=assignments" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": {
"id": "clx5v6w7x8y9z0a1b2c3",
"role": "Senior Backend Engineer",
"description": "Backend engineer for the payments team, focusing on billing infrastructure.",
"status": "open",
"fte": 1.0,
"targetStartDate": "2026-06-01",
"targetFillDate": "2026-05-15",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"salaryMin": 120000,
"salaryMax": 160000,
"currencyCode": "USD",
"filledByLiveEmployeeId": null,
"hiringManagerId": "clx9m4n5o6p7q8r9",
"createdAt": "2026-01-15T10:00:00Z",
"updatedAt": "2026-03-01T16:45:00Z",
"customAttributes": [
{
"id": "clx0a1b2c3d4e5f6g7h8",
"definitionId": "clx2d3e4f5g6h7i8j9k0",
"entityType": "VACANCY",
"entityId": "clx5v6w7x8y9z0a1b2c3",
"stringValue": "ENG-001",
"numberValue": null,
"dateValue": null,
"dateRangeStart": null,
"dateRangeEnd": null,
"definition": {
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING"
}
}
],
"assignments": [
{
"id": "clx8a9b0c1d2e3f4",
"type": "team",
"targetId": "clx6t7u8v9w0x1y2",
"fte": 1.0,
"startDate": "2026-06-01",
"endDate": null,
"createdAt": "2026-01-15T10:00:00Z",
"updatedAt": "2026-01-15T10:00:00Z"
}
]
}
}Create Vacancy
POST /org/:orgId/vacanciesCreates a new vacancy record.
Create Request Body
| Field | Type | Required | Description |
|---|---|---|---|
role | string | Yes | Vacancy title or role name. |
externalId | string | null | No | Your external identifier. Must be unique within the organization. Cannot resemble a CUID format. Max 255 characters. |
description | string | null | No | Detailed role description. |
status | string | No | Vacancy status. Default: "open". |
fte | number | No | FTE value (0--1). Default: 1.0. |
targetStartDate | date (YYYY-MM-DD) | null | No | Desired start date for the new hire. |
targetFillDate | date (YYYY-MM-DD) | null | No | Target date to fill the vacancy. |
jobRoleId | string | null | No | Job role ID. Takes precedence over jobRole. |
jobRole | object | No | Resolve-or-create a job role by externalId or title. Ignored when jobRoleId is supplied. See Employees → Nested jobRole Object for the shape and semantics — identical here. |
workTypeId | string | null | No | Work Type ID. |
geographyId | string | null | No | Location ID. |
salaryMin | number | null | No | Minimum annual salary. Must be >= 0. |
salaryMax | number | null | No | Maximum annual salary. Must be >= 0. |
currencyCode | string | null | No | ISO 4217 currency code (3 uppercase letters). |
hiringManagerId | string | null | No | ID of the hiring manager (an employee). |
Example Request
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"role": "DevOps Engineer",
"description": "Cloud infrastructure engineer to support the platform team.",
"fte": 1.0,
"targetStartDate": "2026-09-01",
"targetFillDate": "2026-08-15",
"salaryMin": 110000,
"salaryMax": 145000,
"currencyCode": "USD",
"hiringManagerId": "clx9m4n5o6p7q8r9"
}'Example Response
json
{
"data": {
"id": "clx2b3c4d5e6f7g8h9i0",
"externalId": null,
"role": "DevOps Engineer",
"description": "Cloud infrastructure engineer to support the platform team.",
"status": "open",
"fte": 1.0,
"targetStartDate": "2026-09-01",
"targetFillDate": "2026-08-15",
"jobRoleId": null,
"workTypeId": null,
"geographyId": null,
"salaryMin": 110000,
"salaryMax": 145000,
"currencyCode": "USD",
"filledByLiveEmployeeId": null,
"hiringManagerId": "clx9m4n5o6p7q8r9",
"createdAt": "2026-04-08T09:30:00Z",
"updatedAt": "2026-04-08T09:30:00Z"
}
}Status: 201 Created
Update Vacancy
PATCH /org/:orgId/vacancies/:idUpdates one or more fields on an existing vacancy. Only include the fields you want to change. You can set or update the externalId field.
Update Request Body
All fields from the Create Request Body are accepted, and all are optional.
Example Request
bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"status": "on_hold",
"targetFillDate": "2026-07-01"
}'Delete Vacancy
DELETE /org/:orgId/vacancies/:idDeletes a vacancy record.
Example Request
bash
curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3" \
-H "Authorization: Bearer private_..."Status: 204 No Content
Fill a Vacancy
POST /org/:orgId/vacancies/:id/fillConverts a vacancy into a new employee or contractor. This is an atomic operation that:
- Creates a new employee (or contractor) with the provided details.
- Creates an initial salary adjustment (or contractor rate adjustment) effective from
startDate. - Transfers all team/project assignments from the vacancy to the new filler.
- Sets the vacancy
statusto"filled"and links it viafilledByLiveEmployeeId(employee branch) orfilledByLiveContractorId(contractor branch).
Fields not provided in the fill request fall back to the vacancy's values (e.g. jobRoleId, geographyId, workTypeId, hiringManagerId).
Choosing the branch
Set fillerType to choose the branch (defaults to "employee" for backwards compatibility):
fillerType | Required fields |
|---|---|
"employee" (default) | firstName, lastName, salary, currencyCode, startDate |
"contractor" | name, rate, rateType, currencyCode, startDate |
The "wrong" branch's fields must not be sent — e.g. name with fillerType: "employee" is rejected with a 400. The endpoint also rejects fills against vacancies whose isFilled is currently true.
Fill Request Body
| Field | Type | Required | Description |
|---|---|---|---|
fillerType | "employee" | "contractor" | No | Discriminator. Defaults to "employee". |
startDate | date (YYYY-MM-DD) | Yes | Filler's start date. Vacancy assignments transfer from this date. |
currencyCode | string | Yes | ISO 4217 currency code for the salary or rate (e.g. "GBP"). |
email | string | null | No | Work email. If omitted on the employee branch, a placeholder is generated. |
managerId | string | null | No | Employee ID of the new hire's manager. Falls back to the vacancy's hiringManagerId. |
jobRoleId | string | null | No | Job role ID. Falls back to the vacancy's jobRoleId. Takes precedence over jobRole. (Employee branch only.) |
jobRole | object | No | Resolve-or-create a job role by externalId or title (same shape as on employees). Ignored when jobRoleId is supplied. (Employee branch only.) |
workTypeId | string | null | No | Work type ID. Falls back to the vacancy's workTypeId. |
geographyId | string | null | No | Geography ID. Falls back to the vacancy's geographyId. |
| Employee branch | |||
firstName | string | Yes (employee) | New employee's first name. |
lastName | string | Yes (employee) | New employee's last name. |
salary | number | Yes (employee) | Annual base salary. Must be >= 0. |
| Contractor branch | |||
name | string | Yes (contractor) | Full name for the new contractor. |
rate | number | Yes (contractor) | Rate per period (see rateType). Must be >= 0. |
rateType | "hourly" | "daily" | "monthly" | "annually" | Yes (contractor) | Billing period for rate. |
contractorType | string | No | Defaults to "individual". |
Example Request — Employee Branch
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3/fill" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"fillerType": "employee",
"firstName": "Sarah",
"lastName": "Okonkwo",
"email": "sarah.okonkwo@example.com",
"startDate": "2026-06-01",
"salary": 140000,
"currencyCode": "USD"
}'Example Request — Contractor Branch
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3/fill" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"fillerType": "contractor",
"name": "Marco Bianchi",
"startDate": "2026-06-01",
"rate": 800,
"rateType": "daily",
"currencyCode": "EUR"
}'Example Response
The response wraps both possibilities — exactly one of employee / contractor is non-null depending on the branch taken:
json
{
"data": {
"employee": {
"id": "clx7n8m9k0j1h2g3f4e5",
"firstName": "Sarah",
"lastName": "Okonkwo",
"email": "sarah.okonkwo@example.com",
"internalEmployeeId": null,
"startDate": "2026-06-01",
"endDate": null,
"managerId": "clx9m4n5o6p7q8r9",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"defaultCurrencyCode": "USD",
"createdAt": "2026-04-08T10:00:00Z",
"updatedAt": "2026-04-08T10:00:00Z"
},
"contractor": null,
"vacancyId": "clx5v6w7x8y9z0a1b2c3",
"teamAllocationsTransferred": 1,
"projectAllocationsTransferred": 0
}
}Status: 200 OK
What happens behind the scenes
After a successful fill (employee branch):
- The vacancy's
statusbecomes"filled"andfilledByLiveEmployeeIdpoints to the new employee. - A salary adjustment is created with the provided
salary,currencyCode, and the employee'sstartDateas theeffectiveDate. - All vacancy team/project assignments are end-dated the day before
startDateand re-created against the new employee.
After a successful fill (contractor branch):
- The vacancy's
statusbecomes"filled"andfilledByLiveContractorIdpoints to the new contractor. - An initial contractor rate adjustment is created with the provided
rate,rateType,currencyCode, andeffectiveDate = startDate. - All vacancy team/project assignments are end-dated and re-created against the new contractor.
In both cases the managerId defaults to the vacancy's hiringManagerId, and jobRoleId / workTypeId / geographyId default to the vacancy's values when not provided.
External IDs
You can assign your own externalId during creation or update. This allows you to reference vacancies by your system's identifier instead of the Flowstate CUID.
- Setting: Pass
externalIdin the POST or PATCH request body. - Lookups: Use the external ID directly in path parameters (e.g.
GET /vacancies/MY-EXT-ID). - Uniqueness: External IDs must be unique per entity type within your organization.
- Format restriction: External IDs cannot match the CUID format (25 lowercase alphanumeric characters starting with a letter).
Error Responses
Validation Error (400)
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": [
{ "field": "role", "message": "role is required" }
],
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Not Found (404)
json
{
"error": {
"code": "NOT_FOUND",
"message": "Vacancy not found.",
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Related Endpoints
- Employees -- Employee records created when vacancies are filled.
- Assignments -- Manage team and project allocations for vacancies.
- Teams -- Teams that vacancies can be assigned to.
- Projects -- Projects that vacancies can be allocated to.
- Recipes: Fill a Vacancy -- Step-by-step guide for the fill workflow.
- Custom Attributes -- Manage custom fields on vacancies.