Employees
An employee is a person on your payroll. Salary history lives in salary 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 anexternalId. - 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.
"85000". Send them as numbers. - Scenarios. Live data only. See Scenarios.
The employee object
| Field | Type | Description |
|---|---|---|
id | string | Flowstate ID. Read-only. |
externalId | string | null | Your identifier. Unique among your employees, at most 255 characters, and can’t look like a Flowstate ID. |
firstName | string | Given name. |
lastName | string | Family name. |
name | string | null | Display name, when set. Read-only. |
email | string | null | Work email. Unique among your employees. |
internalEmployeeId | string | null | Your HR system’s employee number. |
startDate | datetime | First day of employment. |
endDate | datetime | null | Last day of employment. null = no leave date. |
noticeDate | datetime | null | Day notice was given. |
managerId | string | null | Line manager’s employee ID. |
jobRoleId | string | null | Job role ID. |
workTypeId | string | null | Resource type ID. |
geographyId | string | null | Location ID. |
defaultCurrencyCode | string | null | ISO 4217 code. |
organizationId, correlationEmployeeId, sourceSystem, sourceSystemId, lastSyncedAt, metadata | Record-keeping. Read-only. sourceSystem is api for records created here. | |
createdAt, updatedAt | datetime | Read-only. |
customAttributes | array | Custom attribute values. Returned by retrieve only. |
Endpoints
| Method | Path | Permission |
|---|---|---|
GET | /api/v1/org/:orgId/employees | View Employees (team_employees_view) |
GET | /api/v1/org/:orgId/employees/:id | View Employees (team_employees_view) |
POST | /api/v1/org/:orgId/employees | Create Employees (team_employees_create) |
PATCH | /api/v1/org/:orgId/employees/:id | Update Employees (team_employees_update) |
DELETE | /api/v1/org/:orgId/employees/:id | Delete Employees (team_employees_delete) |
Sub-resources: salary adjustments at /employees/:employeeId/salary-adjustments and custom attribute values at /employees/:id/custom-attributes.
List employees
GET /api/v1/org/:orgId/employees
Permission: View Employees (team_employees_view). With include=currentSalary or include=salaryHistory, you also need View Detailed Financials (financials_view_detailed).
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number, from 1. |
limit | integer | 20 | Page size, 1–100. |
search | string | Case-insensitive match on firstName, lastName or email. | |
sortBy | string | lastName | lastName, firstName, email or startDate. Any other value sorts by lastName. |
sortDir | string | asc | asc or desc. |
include | string | Comma-separated. See Includes. |
Includes
| Key | Adds | Needs |
|---|---|---|
currentSalary | The salary adjustment with the latest effectiveDate, or null. | financials_view_detailed |
salaryHistory | Every salary adjustment, latest effectiveDate first. | financials_view_detailed |
assignments | Every team and project assignment, past and current, latest startDate first. Each has type (team or project), targetId, employeeId, fte, startDate, endDate and role, along with the assignment’s own fields. |
Example request
curl "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees?search=chen&limit=10" \
-H "Authorization: Bearer private_..."
Example response
{
"data": [
{
"id": "cmf2k8x1q0001ab2cd3ef4gh5",
"organizationId": "cmf2k8x1q0000ab2cd3ef4gh5",
"correlationEmployeeId": null,
"firstName": "Jane",
"lastName": "Chen",
"name": "Jane Chen",
"email": "jane.chen@example.com",
"internalEmployeeId": "HR-1042",
"externalId": "EMP-1042",
"startDate": "2024-03-15T00:00:00.000Z",
"endDate": null,
"managerId": "cmf2k8x1q0002ab2cd3ef4gh5",
"jobRoleId": "cmf2k8x1q0010ab2cd3ef4gh5",
"workTypeId": "cmf2k8x1q0011ab2cd3ef4gh5",
"geographyId": "cmf2k8x1q0012ab2cd3ef4gh5",
"defaultCurrencyCode": "GBP",
"sourceSystem": "api",
"sourceSystemId": null,
"lastSyncedAt": "2024-03-15T10:30:00.000Z",
"metadata": {},
"createdAt": "2024-03-15T10:30:00.000Z",
"updatedAt": "2026-06-01T14:22:00.000Z",
"noticeDate": null
}
],
"meta": { "page": 1, "limit": 10, "total": 1, "hasNextPage": false }
}
Errors
| Status | Code | When |
|---|---|---|
400 | VALIDATION_ERROR | Invalid query parameter, or an include key not in the table. |
403 | FORBIDDEN | A salary include without financials_view_detailed. |
Retrieve an employee
GET /api/v1/org/:orgId/employees/:id
Permission: View Employees (team_employees_view). The salary includes also need financials_view_detailed.
Takes the same include parameter as List employees. The response is the employee object, with customAttributes and any includes.
Example request
curl "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/EMP-1042?include=currentSalary" \
-H "Authorization: Bearer private_..."
Example response
{
"data": {
"id": "cmf2k8x1q0001ab2cd3ef4gh5",
"firstName": "Jane",
"lastName": "Chen",
"externalId": "EMP-1042",
"startDate": "2024-03-15T00:00:00.000Z",
"endDate": null,
"defaultCurrencyCode": "GBP",
"…": "other employee fields",
"customAttributes": [],
"currentSalary": {
"id": "cmf2k8x1q0040ab2cd3ef4gh5",
"organizationId": "cmf2k8x1q0000ab2cd3ef4gh5",
"liveEmployeeId": "cmf2k8x1q0001ab2cd3ef4gh5",
"effectiveDate": "2026-01-01T00:00:00.000Z",
"salary": "95000",
"bonus": "10000",
"currencyCode": "GBP",
"reason": "Annual review",
"sourceSystem": "api",
"sourceSystemId": null,
"lastSyncedAt": "2025-12-15T10:30:00.000Z",
"metadata": {},
"createdAt": "2025-12-15T10:30:00.000Z",
"updatedAt": "2025-12-15T10:30:00.000Z"
}
}
}
Errors
| Status | Code | When |
|---|---|---|
400 | VALIDATION_ERROR | An include key not in the table. |
403 | FORBIDDEN | A salary include without financials_view_detailed. |
404 | NOT_FOUND | No employee with that ID or externalId. |
Create an employee
POST /api/v1/org/:orgId/employees
Permission: Create Employees (team_employees_create).
The employee is created in one transaction with any nested salary, teamAssignment and projectAssignment you send.
Body parameters
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | |
lastName | string | Yes | |
email | string | Yes | Valid email address, unique among your employees. |
startDate | date | Yes | |
externalId | string | null | No | At most 255 characters. Can’t look like a Flowstate ID. |
internalEmployeeId | string | null | No | |
endDate | date | null | No | |
noticeDate | date | null | No | |
managerId | string | null | No | Employee ID or externalId. |
jobRoleId | string | null | No | Job role ID. Wins over jobRole. |
jobRole | object | No | Find or create a job role. See jobRole. |
workTypeId | string | null | No | Resource type ID. |
geographyId | string | null | No | Location ID. |
defaultCurrencyCode | string | null | No | Three uppercase letters. |
salary | object | No | First salary adjustment. See salary. |
teamAssignment | object | No | See teamAssignment and projectAssignment. |
projectAssignment | object | No | See teamAssignment and projectAssignment. |
jobRole
Matched by externalId first, then by title. With no match, a role is created from title. Send at least one of the two.
| Field | Type | Description |
|---|---|---|
title | string | 1–255 characters. Needed to create a role. |
externalId | string | 1–255 characters. A role matched by title that has no externalId takes this one. |
salary
| Field | Type | Required | Description |
|---|---|---|---|
effectiveDate | date | Yes | |
salary | number | Yes | Annual base salary, ≥ 0. |
currencyCode | string | Yes | Three uppercase letters. |
bonus | number | null | No | Annual bonus, ≥ 0. |
reason | string | null | No |
teamAssignment and projectAssignment
| Field | Type | Required | Description |
|---|---|---|---|
teamId / projectId | string | Yes | Team or project ID, or its externalId. |
fte | number | Yes | 0–10. 1 = full time. |
startDate | date | Yes | |
endDate | date | null | No | null = open-ended. |
role | string | null | No |
Example request
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-10-01",
"managerId": "EMP-1042",
"jobRole": { "title": "Senior Engineer", "externalId": "ROLE-042" },
"geographyId": "cmf2k8x1q0012ab2cd3ef4gh5",
"defaultCurrencyCode": "GBP",
"salary": {
"effectiveDate": "2026-10-01",
"salary": 85000,
"currencyCode": "GBP",
"reason": "Starting salary"
},
"teamAssignment": {
"teamId": "cmf2k8x1q0020ab2cd3ef4gh5",
"fte": 1,
"startDate": "2026-10-01"
}
}'
Example response
201 Created. When you sent them, the created records come back under salary, teamAssignment and projectAssignment.
{
"data": {
"id": "cmf2k8x1q0003ab2cd3ef4gh5",
"organizationId": "cmf2k8x1q0000ab2cd3ef4gh5",
"correlationEmployeeId": null,
"firstName": "Alex",
"lastName": "Rivera",
"name": null,
"email": "alex.rivera@example.com",
"internalEmployeeId": null,
"externalId": "EMP-2001",
"startDate": "2026-10-01T00:00:00.000Z",
"endDate": null,
"managerId": "cmf2k8x1q0001ab2cd3ef4gh5",
"jobRoleId": "cmf2k8x1q0010ab2cd3ef4gh5",
"workTypeId": null,
"geographyId": "cmf2k8x1q0012ab2cd3ef4gh5",
"defaultCurrencyCode": "GBP",
"sourceSystem": "api",
"sourceSystemId": null,
"lastSyncedAt": "2026-09-14T09:15:00.000Z",
"metadata": {},
"createdAt": "2026-09-14T09:15:00.000Z",
"updatedAt": "2026-09-14T09:15:00.000Z",
"noticeDate": null,
"salary": {
"id": "cmf2k8x1q0041ab2cd3ef4gh5",
"liveEmployeeId": "cmf2k8x1q0003ab2cd3ef4gh5",
"effectiveDate": "2026-10-01T00:00:00.000Z",
"salary": "85000",
"bonus": null,
"currencyCode": "GBP",
"reason": "Starting salary",
"…": "other salary adjustment fields"
},
"teamAssignment": {
"id": "cmf2k8x1q0050ab2cd3ef4gh5",
"liveEmployeeId": "cmf2k8x1q0003ab2cd3ef4gh5",
"liveTeamId": "cmf2k8x1q0020ab2cd3ef4gh5",
"fte": 1,
"startDate": "2026-10-01T00:00:00.000Z",
"endDate": null,
"role": null,
"…": "other assignment fields"
}
}
}
Errors
| Status | Code | When |
|---|---|---|
400 | VALIDATION_ERROR | The body is invalid, teamAssignment.teamId or projectAssignment.projectId matches nothing, or email or externalId is already used. |
404 | NOT_FOUND | managerId matches no employee, or jobRole has only an externalId that matches no role. |
501 | NOT_IMPLEMENTED | scenarioId was sent. |
Update an employee
PATCH /api/v1/org/:orgId/employees/:id
Permission: Update Employees (team_employees_update).
Takes the create body with every field optional. Send only what changes. null clears a nullable field. email can’t be cleared.
Nested salary, teamAssignment and projectAssignment always add a record. They never change or end an existing one. To change those, use salary adjustments and assignments.
Example request
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/EMP-1042" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{ "lastName": "Chen-Rivera" }'
Example response
200 OK. The response is the updated employee, plus any nested records created.
Errors
As for Create an employee, plus 404 NOT_FOUND when no employee has that ID or externalId.
Delete an employee
DELETE /api/v1/org/:orgId/employees/:id
Permission: Delete Employees (team_employees_delete).
Permanent. The following are deleted with the employee:
- salary adjustments
- assignments
- leave
- effort records
Where another record points to the employee as its manager, hiring manager or vacancy filler, that link is set to null. To record someone leaving, set endDate instead. See Record a leaver.
Example request
curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/EMP-1042" \
-H "Authorization: Bearer private_..."
Example response
{ "data": { "id": "cmf2k8x1q0001ab2cd3ef4gh5", "deleted": true } }
Errors
| Status | Code | When |
|---|---|---|
404 | NOT_FOUND | No employee with that ID or externalId. |
501 | NOT_IMPLEMENTED | scenarioId was sent. |
Tasks
Record a leaver
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/EMP-1042" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{ "noticeDate": "2026-09-30", "endDate": "2026-12-31" }'
The employee’s assignments keep their own end dates. End any that should stop on the leaving date with PATCH /assignments/employees/:id. To withdraw a resignation, send "endDate": null, "noticeDate": null.
Sync from an HR system by your own ID
Create with externalId, then use it in paths (/employees/EMP-1042) and in managerId. Before creating, GET /employees/EMP-1042: a 404 means the employee doesn’t exist yet. The full walkthrough is in Recipes.
Related
- Salary adjustments · Assignments · Custom attributes · Errors
- Recipes: Sync people from an HR system · Move an employee between teams
- Webhooks: writes send
employee,salary_adjustment,employee_team_allocationandemployee_project_allocationevents. See Webhooks.