Appearance
Employees
Manage employee records in your organization. Employees represent full-time and part-time staff with salary information, team memberships, and project assignments.
New to Flowstate?
Read the Domain Model first to understand how employees relate to teams, projects, salary adjustments, and assignments.
Employee Object
The employee object represents a single person employed by your organization.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the employee (CUID format, e.g. clx1a2b3c4d5e6f7g8h9). System-generated. Read-only. |
externalId | string | null | Your external identifier for this employee. Unique within your organization. Use for lookups via path parameters. |
firstName | string | Employee's first (given) name. |
lastName | string | Employee's last (family) name. |
email | string | Work email address. Must be unique within the organization. |
internalEmployeeId | string | null | Your organization's internal employee/HR identifier (e.g. from BambooHR or Workday). |
startDate | date (YYYY-MM-DD) | Employment start date. Used for headcount forecasting and cost calculations. |
endDate | date (YYYY-MM-DD) | null | Employment end date. null means the employee is currently active. |
noticeDate | date (YYYY-MM-DD) | null | Date the employee gave notice. Distinct from endDate -- notice triggers forecast adjustments before the actual departure. |
managerId | string | null | ID of the employee's direct manager. References another employee in this organization. |
jobRoleId | string | null | ID of the employee's job role (e.g. "Senior Engineer"). Use GET /job-roles to list valid values. |
workTypeId | string | null | Work arrangement classification (e.g. full-time, part-time). References a Work Type. |
geographyId | string | null | Employee's work location/region. References a Location. Affects currency defaults and cost multipliers. |
defaultCurrencyCode | string | null | ISO 4217 currency code (e.g. GBP, USD). If not set, defaults to the geography's currency. |
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 employee. Returned on GET-by-ID. See Custom Attributes. |
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /org/:orgId/employees | List employees |
GET | /org/:orgId/employees/:id | Get employee |
POST | /org/:orgId/employees | Create employee |
PATCH | /org/:orgId/employees/:id | Update employee |
DELETE | /org/:orgId/employees/:id | Delete employee |
External ID Support
The :id path parameter accepts either a Flowstate CUID (e.g. clx1a2b3c4d5e6f7g8h9) or your externalId. The format is auto-detected — CUIDs are 25-character lowercase alphanumeric strings starting with a letter; anything else is treated as an external ID lookup.
List Employees
GET /org/:orgId/employeesReturns a paginated list of employees 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 across first name, last name, and email. |
sortBy | string | lastName | Field to sort by (e.g. lastName, startDate, email). |
sortDir | string | asc | Sort direction: asc or desc. |
scenarioId | string | -- | Scenario ID for what-if queries. Omit for live data. |
Example Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees?sortBy=lastName&limit=10" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": [
{
"id": "clx1a2b3c4d5e6f7g8h9",
"externalId": "EMP-1042",
"firstName": "Jane",
"lastName": "Chen",
"email": "jane.chen@example.com",
"internalEmployeeId": "EMP-1042",
"startDate": "2024-03-15",
"endDate": null,
"noticeDate": null,
"managerId": "clx9m4n5o6p7q8r9",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"defaultCurrencyCode": "USD",
"createdAt": "2024-03-15T10:30:00Z",
"updatedAt": "2024-06-01T14:22:00Z"
}
],
"meta": {
"page": 1,
"limit": 10,
"total": 142,
"hasNextPage": true
}
}Get Employee
GET /org/:orgId/employees/:idReturns a single employee by ID.
Including Related Data
Use the include query parameter to embed related data in the response. Pass a comma-separated list of include keys:
| Include Key | Description |
|---|---|
currentSalary | The employee's current salary adjustment (most recent by effectiveDate). |
salaryHistory | All salary adjustments for the employee, ordered by effectiveDate descending. |
assignments | All active team and project assignments for the employee. |
GET /org/:orgId/employees/:id?include=currentSalary,assignmentsExample Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9?include=currentSalary,assignments" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": {
"id": "clx1a2b3c4d5e6f7g8h9",
"externalId": "EMP-1042",
"firstName": "Jane",
"lastName": "Chen",
"email": "jane.chen@example.com",
"internalEmployeeId": "EMP-1042",
"startDate": "2024-03-15",
"endDate": null,
"noticeDate": null,
"managerId": "clx9m4n5o6p7q8r9",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"defaultCurrencyCode": "USD",
"createdAt": "2024-03-15T10:30:00Z",
"updatedAt": "2024-06-01T14:22:00Z",
"customAttributes": [
{
"id": "clx8v9w0x1y2z3a4b5c6",
"definitionId": "clx2d3e4f5g6h7i8j9k0",
"entityType": "EMPLOYEE",
"entityId": "clx1a2b3c4d5e6f7g8h9",
"stringValue": "ENG-001",
"numberValue": null,
"dateValue": null,
"dateRangeStart": null,
"dateRangeEnd": null,
"definition": {
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING"
}
}
],
"currentSalary": {
"id": "clx5s6a7l8r9y0j1",
"employeeId": "clx1a2b3c4d5e6f7g8h9",
"effectiveDate": "2026-01-01",
"salary": 95000,
"bonus": 10000,
"currencyCode": "USD",
"reason": "Annual review",
"createdAt": "2025-12-15T10:30:00Z",
"updatedAt": "2025-12-15T10:30:00Z"
},
"assignments": [
{
"id": "clx8a9b0c1d2e3f4",
"type": "team",
"targetId": "clx6t7u8v9w0x1y2",
"fte": 0.6,
"startDate": "2025-01-01",
"endDate": null,
"createdAt": "2024-12-15T10:00:00Z",
"updatedAt": "2025-06-01T14:00:00Z"
},
{
"id": "clx9b0c1d2e3f4g5",
"type": "project",
"targetId": "clx7p8r9q0s1t2u3",
"fte": 0.4,
"startDate": "2025-03-01",
"endDate": "2025-12-31",
"createdAt": "2025-02-20T09:00:00Z",
"updatedAt": "2025-02-20T09:00:00Z"
}
]
}
}About included assignments
Each assignment in the assignments array contains type ("team" or "project") and targetId (the ID of the team or project). See Assignments for full details.
Create Employee
POST /org/:orgId/employeesCreates a new employee record. You can optionally include nested objects to atomically create a salary adjustment, team assignment, and/or project assignment in the same request.
Create Request Body
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | Employee's first (given) name. |
lastName | string | Yes | Employee's last (family) name. |
email | string | Yes | Work email address. Must be a valid email and unique within the organization. |
internalEmployeeId | string | null | No | Your internal HR identifier (e.g. "EMP-2001"). |
externalId | string | null | No | Your external identifier for this employee. Must be unique within the organization. Cannot resemble a CUID format. Max 255 characters. |
startDate | date (YYYY-MM-DD) | Yes | Employment start date. |
endDate | date (YYYY-MM-DD) | null | No | Employment end date. null or omit for currently active. |
noticeDate | date (YYYY-MM-DD) | null | No | Date notice was given. |
managerId | string | null | No | ID of an existing employee who is the direct manager. |
jobRoleId | string | null | No | Job role ID. Use GET /job-roles for valid values. Takes precedence over jobRole. |
jobRole | object | No | Resolve-or-create a job role by externalId or title. Ignored when jobRoleId is supplied. See below. |
workTypeId | string | null | No | Work Type ID. |
geographyId | string | null | No | Location ID. |
defaultCurrencyCode | string | null | No | ISO 4217 currency code (3 uppercase letters, e.g. "GBP"). |
salary | object | No | Nested salary adjustment to create atomically. See below. |
teamAssignment | object | No | Nested team assignment to create atomically. See below. |
projectAssignment | object | No | Nested project assignment to create atomically. See below. |
Nested jobRole Object
Resolves a job role by external reference, creating it on the fly when the name is new. Match order: externalId → title → create. Supply at least one of the two fields.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Conditional | Human-readable role name (e.g. "Senior Engineer"). Required when the externalId doesn't match an existing row — we auto-create by name. |
externalId | string | Conditional | Your external system's identifier for the role. Preferred match key when supplied. An existing row matched by title will be stamped with this externalId on first contact. |
INFO
Sending jobRole: { "externalId": "R-042" } alone with no existing match returns 404 NOT_FOUND — auto-create needs a human-readable title so Flowstate never invents a role name.
Nested salary Object
Creates an initial salary adjustment record for the employee.
| Field | Type | Required | Description |
|---|---|---|---|
effectiveDate | date (YYYY-MM-DD) | Yes | Date this salary takes effect. Usually matches the employee's startDate. |
salary | number | Yes | Annual base salary. Must be >= 0. |
currencyCode | string | Yes | ISO 4217 currency code (e.g. "GBP"). |
bonus | number | null | No | Annual bonus amount. Must be >= 0. null = no bonus. |
reason | string | null | No | Reason (e.g. "Starting salary"). |
Nested teamAssignment Object
Assigns the employee to a team.
| Field | Type | Required | Description |
|---|---|---|---|
teamId | string | Yes | ID of the team to assign the employee to. |
fte | number | Yes | FTE allocation (0.0--10.0). 1.0 = full-time on this team. |
startDate | date (YYYY-MM-DD) | Yes | Assignment start date. |
endDate | date (YYYY-MM-DD) | null | No | Assignment end date. null = ongoing. |
role | string | null | No | Role within this assignment (e.g. "Tech Lead"). |
Nested projectAssignment Object
Assigns the employee to a project.
| Field | Type | Required | Description |
|---|---|---|---|
projectId | string | Yes | ID of the project to assign the employee to. |
fte | number | Yes | FTE allocation (0.0--10.0). |
startDate | date (YYYY-MM-DD) | Yes | Assignment start date. |
endDate | date (YYYY-MM-DD) | null | No | Assignment end date. null = ongoing. |
role | string | null | No | Role within this assignment. |
Example: Simple Create
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"firstName": "Alex",
"lastName": "Rivera",
"email": "alex.rivera@example.com",
"startDate": "2026-07-01",
"defaultCurrencyCode": "GBP"
}'Example: Create with Auto-Created Job Role
Supply jobRole to look up or create the role by external reference, without needing to pre-provision it:
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"firstName": "Alex",
"lastName": "Rivera",
"email": "alex.rivera@example.com",
"startDate": "2026-07-01",
"jobRole": {
"title": "Senior Engineer",
"externalId": "ROLE-042"
}
}'If a job role named "Senior Engineer" already exists in the organization, it is reused and stamped with externalId: "ROLE-042" on first contact. Otherwise a new role is created with both fields populated.
Example: Nested Create (Employee + Salary + Team)
This creates the employee, their starting salary, and a team assignment in a single atomic operation:
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"firstName": "Alex",
"lastName": "Rivera",
"email": "alex.rivera@example.com",
"externalId": "EMP-2001",
"startDate": "2026-07-01",
"geographyId": "clx3g2h1j0k9l8m7",
"workTypeId": "clx2w3x4y5z6a7b8",
"jobRoleId": "clx9r8q7w6e5t4r3",
"managerId": "clx9m4n5o6p7q8r9",
"salary": {
"effectiveDate": "2026-07-01",
"salary": 85000,
"currencyCode": "GBP",
"reason": "Starting salary"
},
"teamAssignment": {
"teamId": "clx6t7u8v9w0x1y2",
"fte": 1.0,
"startDate": "2026-07-01"
}
}'Example Response
json
{
"data": {
"id": "clx7n8m9k0j1h2g3f4e5",
"externalId": "EMP-2001",
"firstName": "Alex",
"lastName": "Rivera",
"email": "alex.rivera@example.com",
"internalEmployeeId": null,
"startDate": "2026-07-01",
"endDate": null,
"noticeDate": null,
"managerId": "clx9m4n5o6p7q8r9",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"defaultCurrencyCode": null,
"createdAt": "2026-04-08T09:15:00Z",
"updatedAt": "2026-04-08T09:15:00Z"
}
}Status: 201 Created
TIP
When you provide a geographyId, the employee inherits the location's default currency. You only need to set defaultCurrencyCode explicitly if you want to override the geography's default.
Update Employee
PATCH /org/:orgId/employees/:idUpdates one or more fields on an existing employee. Only include the fields you want to change.
You can also pass salary, teamAssignment, or projectAssignment nested objects on update. These are additive -- they create new records and never overwrite or end existing ones.
Update Request Body
All fields from the Create Request Body are accepted, and all are optional. Additionally:
- Set
endDatetonullto clear a previously set departure date. - Set
noticeDatetonullto clear a previously set notice. - Set
externalIdto assign or change the employee's external identifier, ornullto clear it. - Pass
salaryto add a new salary adjustment (does not modify existing adjustments). - Pass
teamAssignmentto add a new team assignment (does not end existing assignments).
Example Request
bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"lastName": "Chen-Rivera",
"endDate": "2026-12-31",
"noticeDate": "2026-09-30"
}'Example Response
json
{
"data": {
"id": "clx1a2b3c4d5e6f7g8h9",
"externalId": "EMP-1042",
"firstName": "Jane",
"lastName": "Chen-Rivera",
"email": "jane.chen@example.com",
"internalEmployeeId": "EMP-1042",
"startDate": "2024-03-15",
"endDate": "2026-12-31",
"noticeDate": "2026-09-30",
"managerId": "clx9m4n5o6p7q8r9",
"jobRoleId": "clx9r8q7w6e5t4r3",
"workTypeId": "clx2w3x4y5z6a7b8",
"geographyId": "clx3g2h1j0k9l8m7",
"defaultCurrencyCode": "USD",
"createdAt": "2024-03-15T10:30:00Z",
"updatedAt": "2026-04-08T09:20:00Z"
}
}Delete Employee
DELETE /org/:orgId/employees/:idDeletes an employee record. This operation is permanent for live data. In scenario mode, it creates a soft-delete tombstone.
Example Request
bash
curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9" \
-H "Authorization: Bearer private_..."Status: 204 No Content
External IDs
You can assign your own externalId to employees during creation or update. This allows you to reference employees 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 /employees/EMP-1042). - 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).
- FK references: Fields like
managerIdalso accept external IDs.
Error Responses
Validation Error (400)
Returned when the request body fails validation:
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": [
{ "field": "email", "message": "email must be a valid email address" },
{ "field": "startDate", "message": "startDate is required" }
],
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Not Found (404)
Returned when the employee ID does not exist:
json
{
"error": {
"code": "NOT_FOUND",
"message": "Employee not found.",
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Related Endpoints
- Salary Adjustments -- Manage compensation history for an employee.
- Assignments -- Manage team and project allocations.
- Teams -- The teams employees can be assigned to.
- Projects -- The projects employees can be allocated to.
- Locations -- Geographic locations referenced by
geographyId. - Work Types -- Work arrangement types referenced by
workTypeId. - Custom Attributes -- Manage custom fields on employees.