Appearance
Custom Attributes
Define and manage custom fields on employees, contractors, vacancies, teams, and projects. Custom attributes let you extend Flowstate's data model with organization-specific fields -- cost centre codes, department tags, compliance flags, or anything else your workflows need.
Two-part model
Custom attributes have two parts: definitions (the field schema, configured once per organization) and values (the data set on individual entities). Create definitions first, then set values on entities.
Custom Attribute Definition Object
A definition describes one custom field -- its name, data type, and which entity types it applies to.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (CUID format). System-generated. Read-only. |
name | string | Display name (e.g. "Cost Centre Code"). Unique within your organization. |
attributeKey | string | Immutable machine key for API and integration usage (e.g. "cost_centre_code"). Lowercase alphanumeric with underscores, starting with a letter. Max 100 characters. Auto-generated from name if omitted on creation. |
fieldType | string | Data type: STRING, NUMBER, DATE, or DATE_RANGE. |
entityTypes | string[] | Entity types this attribute applies to. One or more of: EMPLOYEE, TEAM, PROJECT, VACANCY, CONTRACTOR. |
description | string | null | Optional description of what this attribute tracks. |
isRequired | boolean | Whether this attribute is required when editing an entity. Default: false. |
isActive | boolean | Whether this attribute is active and visible. Default: true. |
sortOrder | integer | Display sort order. Lower numbers appear first. Default: 0. |
createdAt | datetime (ISO 8601) | When the definition was created. Read-only. |
updatedAt | datetime (ISO 8601) | When the definition was last modified. Read-only. |
attributeKey is immutable
The attributeKey is set once during creation and cannot be changed. If you need a different key, delete the definition and create a new one. Any attributeKey passed in a PATCH request is silently ignored.
Custom Attribute Value Object
A value stores the data for one custom attribute on one entity.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier (CUID format). System-generated. Read-only. |
definitionId | string | ID of the custom attribute definition. |
entityType | string | Entity type (EMPLOYEE, TEAM, PROJECT, VACANCY, or CONTRACTOR). |
entityId | string | ID of the entity this value belongs to. |
stringValue | string | null | Value for STRING field type. Max 255 characters. |
numberValue | number | null | Value for NUMBER field type. |
dateValue | datetime (ISO 8601) | null | Value for DATE field type. |
dateRangeStart | datetime (ISO 8601) | null | Start date for DATE_RANGE field type. |
dateRangeEnd | datetime (ISO 8601) | null | End date for DATE_RANGE field type. |
sourceSystem | string | System that set this value (e.g. "api", "manual", "integration"). Read-only. |
createdAt | datetime (ISO 8601) | When the value was created. Read-only. |
updatedAt | datetime (ISO 8601) | When the value was last modified. Read-only. |
definition | object | The full definition object. Included in responses. |
Values on GET-by-ID
When you fetch an entity by ID (e.g. GET /employees/:id), the response automatically includes a customAttributes array containing all custom attribute values for that entity. You do not need to make a separate request.
Definition Endpoints
| Method | Path | Description |
|---|---|---|
GET | /org/:orgId/custom-attributes | List definitions |
GET | /org/:orgId/custom-attributes/:definitionId | Get definition |
POST | /org/:orgId/custom-attributes | Create definition |
PATCH | /org/:orgId/custom-attributes/:definitionId | Update definition |
DELETE | /org/:orgId/custom-attributes/:definitionId | Delete definition |
Permissions
| Action | Required Permission |
|---|---|
| List / Get definitions | settings_entity_config_view |
| Create / Update definitions | settings_entity_config_update |
| Delete definitions | settings_entity_config_delete |
List Definitions
GET /org/:orgId/custom-attributesReturns a paginated list of custom attribute definitions.
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 name and description. |
sortBy | string | sortOrder | Field to sort by: name, createdAt, fieldType, or sortOrder. |
sortDir | string | asc | Sort direction: asc or desc. |
entityType | string | -- | Filter to definitions that apply to this entity type (e.g. EMPLOYEE). |
Example Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/custom-attributes?entityType=EMPLOYEE&limit=50" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": [
{
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING",
"entityTypes": ["EMPLOYEE", "CONTRACTOR"],
"description": "Finance cost centre for budget allocation.",
"isRequired": false,
"isActive": true,
"sortOrder": 0,
"createdAt": "2026-03-01T10:00:00Z",
"updatedAt": "2026-03-01T10:00:00Z"
},
{
"id": "clx3e4f5g6h7i8j9k0l1",
"name": "Compliance Expiry",
"attributeKey": "compliance_expiry",
"fieldType": "DATE",
"entityTypes": ["EMPLOYEE"],
"description": "Date when the employee's compliance certification expires.",
"isRequired": true,
"isActive": true,
"sortOrder": 1,
"createdAt": "2026-03-05T14:00:00Z",
"updatedAt": "2026-03-05T14:00:00Z"
}
],
"meta": {
"page": 1,
"limit": 50,
"total": 2,
"hasNextPage": false
}
}Get Definition
GET /org/:orgId/custom-attributes/:definitionIdReturns a single custom attribute definition by ID.
Example Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/custom-attributes/clx2d3e4f5g6h7i8j9k0" \
-H "Authorization: Bearer private_..."Create Definition
POST /org/:orgId/custom-attributesCreates a new custom attribute definition.
Create Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name. Must be unique within the organization. |
attributeKey | string | No | Machine key. Must match ^[a-z][a-z0-9_]*$, max 100 characters. Auto-generated from name if omitted. |
fieldType | string | Yes | STRING, NUMBER, DATE, or DATE_RANGE. |
entityTypes | string[] | Yes | At least one of: EMPLOYEE, TEAM, PROJECT, VACANCY, CONTRACTOR. |
description | string | null | No | Description of the attribute. |
isRequired | boolean | No | Default: false. |
isActive | boolean | No | Default: true. |
sortOrder | integer | No | Default: 0. |
Example Request
bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/custom-attributes" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Cost Centre Code",
"fieldType": "STRING",
"entityTypes": ["EMPLOYEE", "CONTRACTOR"],
"description": "Finance cost centre for budget allocation."
}'Example Response
json
{
"data": {
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING",
"entityTypes": ["EMPLOYEE", "CONTRACTOR"],
"description": "Finance cost centre for budget allocation.",
"isRequired": false,
"isActive": true,
"sortOrder": 0,
"createdAt": "2026-04-08T09:00:00Z",
"updatedAt": "2026-04-08T09:00:00Z"
}
}Status: 201 Created
attributeKey auto-generation
If you omit attributeKey, it is generated from the name -- for example, "Cost Centre Code" becomes "cost_centre_code". Uniqueness within the organization is guaranteed automatically.
Update Definition
PATCH /org/:orgId/custom-attributes/:definitionIdUpdates one or more fields on an existing definition. Only include the fields you want to change.
Update Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | Updated display name. |
fieldType | string | No | Updated data type. |
entityTypes | string[] | No | Updated entity types (min 1). |
description | string | null | No | Updated description. |
isRequired | boolean | No | Updated required flag. |
isActive | boolean | No | Updated active flag. |
sortOrder | integer | No | Updated sort order. |
Example Request
bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/custom-attributes/clx2d3e4f5g6h7i8j9k0" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"description": "Finance cost centre code — required for all permanent staff.",
"isRequired": true,
"entityTypes": ["EMPLOYEE", "CONTRACTOR", "VACANCY"]
}'Delete Definition
DELETE /org/:orgId/custom-attributes/:definitionIdDeletes a custom attribute definition and all associated values across all entities. This cannot be undone.
Example Request
bash
curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/custom-attributes/clx2d3e4f5g6h7i8j9k0" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": {
"id": "clx2d3e4f5g6h7i8j9k0",
"deleted": true
}
}Status: 200 OK
Cascading delete
Deleting a definition permanently removes every value that references it, across all entity types. This cannot be undone.
Value Endpoints
Custom attribute values are managed as a sub-resource on each entity type. The same endpoint pattern applies to all five entity types:
| Method | Path | Description |
|---|---|---|
GET | /org/:orgId/employees/:id/custom-attributes | List values for an employee |
PUT | /org/:orgId/employees/:id/custom-attributes/:definitionId | Set value on an employee |
DELETE | /org/:orgId/employees/:id/custom-attributes/:definitionId | Remove value from an employee |
Replace employees/:id with any of:
contractors/:idvacancies/:idteams/:idprojects/:id
Permissions
| Action | Required Permission |
|---|---|
| List values | Inherits from parent entity access |
| Set / Remove values | settings_entity_config_update |
List Values for Entity
GET /org/:orgId/employees/:id/custom-attributesReturns all custom attribute values for the specified entity, ordered by definition sort order. The response is a flat array (not paginated).
Example Request
bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9/custom-attributes" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": [
{
"id": "clx8v9w0x1y2z3a4b5c6",
"definitionId": "clx2d3e4f5g6h7i8j9k0",
"entityType": "EMPLOYEE",
"entityId": "clx1a2b3c4d5e6f7g8h9",
"stringValue": "ENG-001",
"numberValue": null,
"dateValue": null,
"dateRangeStart": null,
"dateRangeEnd": null,
"sourceSystem": "api",
"createdAt": "2026-04-08T10:00:00Z",
"updatedAt": "2026-04-08T10:00:00Z",
"definition": {
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING",
"entityTypes": ["EMPLOYEE", "CONTRACTOR"],
"description": "Finance cost centre for budget allocation.",
"isRequired": false,
"isActive": true,
"sortOrder": 0
}
}
]
}Set Value (Upsert)
PUT /org/:orgId/employees/:id/custom-attributes/:definitionIdSets a custom attribute value on an entity. If a value already exists for this definition and entity, it is updated. If not, a new value is created.
Request Body
Provide the value field that matches the definition's fieldType. All value fields are nullable -- set to null to clear a value while keeping the record.
| Field | Type | Description |
|---|---|---|
stringValue | string | null | Value for STRING definitions. |
numberValue | number | null | Value for NUMBER definitions. |
dateValue | datetime (ISO 8601) | null | Value for DATE definitions. |
dateRangeStart | datetime (ISO 8601) | null | Start date for DATE_RANGE definitions. |
dateRangeEnd | datetime (ISO 8601) | null | End date for DATE_RANGE definitions. |
Example: Set a STRING value
bash
curl -X PUT "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9/custom-attributes/clx2d3e4f5g6h7i8j9k0" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"stringValue": "ENG-001"
}'Example: Set a NUMBER value
bash
curl -X PUT "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/projects/clx7p8r9q0s1t2u3v4w5/custom-attributes/clx4f5g6h7i8j9k0l1m2" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"numberValue": 42.5
}'Example: Set a DATE_RANGE value
bash
curl -X PUT "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9/custom-attributes/clx5g6h7i8j9k0l1m2n3" \
-H "Authorization: Bearer private_..." \
-H "Content-Type: application/json" \
-d '{
"dateRangeStart": "2026-01-01T00:00:00Z",
"dateRangeEnd": "2026-12-31T23:59:59Z"
}'Example Response
json
{
"data": {
"id": "clx8v9w0x1y2z3a4b5c6",
"definitionId": "clx2d3e4f5g6h7i8j9k0",
"entityType": "EMPLOYEE",
"entityId": "clx1a2b3c4d5e6f7g8h9",
"stringValue": "ENG-001",
"numberValue": null,
"dateValue": null,
"dateRangeStart": null,
"dateRangeEnd": null,
"sourceSystem": "api",
"createdAt": "2026-04-08T10:00:00Z",
"updatedAt": "2026-04-08T10:00:00Z",
"definition": {
"id": "clx2d3e4f5g6h7i8j9k0",
"name": "Cost Centre Code",
"attributeKey": "cost_centre_code",
"fieldType": "STRING"
}
}
}Entity type validation
The definition's entityTypes must include the entity type you're setting the value on. For example, if a definition has entityTypes: ["EMPLOYEE", "CONTRACTOR"], you cannot set it on a project. The API returns 400 with a descriptive error.
Remove Value
DELETE /org/:orgId/employees/:id/custom-attributes/:definitionIdRemoves a custom attribute value from an entity.
Example Request
bash
curl -X DELETE "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9/custom-attributes/clx2d3e4f5g6h7i8j9k0" \
-H "Authorization: Bearer private_..."Example Response
json
{
"data": {
"definitionId": "clx2d3e4f5g6h7i8j9k0",
"entityId": "clx1a2b3c4d5e6f7g8h9",
"deleted": true
}
}Status: 200 OK
Error Responses
Validation Error (400)
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed.",
"details": [
{ "field": "entityTypes", "message": "At least one entity type is required" }
],
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Entity Type Mismatch (400)
Returned when setting a value on an entity type the definition does not support:
json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Custom attribute \"Cost Centre Code\" does not apply to entity type PROJECT. Allowed: EMPLOYEE, CONTRACTOR",
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Not Found (404)
json
{
"error": {
"code": "NOT_FOUND",
"message": "Custom attribute definition not found: clx2d3e4f5g6h7i8j9k0",
"errorId": "err_clx9a8b7c6d5e4f3"
}
}Related Endpoints
- Employees -- Employee records that can have custom attributes.
- Contractors -- Contractor records that can have custom attributes.
- Vacancies -- Vacancy records that can have custom attributes.
- Teams -- Team records that can have custom attributes.
- Projects -- Project records that can have custom attributes.
- Custom Integrations Data Model -- Syncing custom attributes via integration hooks.