Appearance
Data Model Reference
This page documents the fields available for each entity type in Custom Integrations.
For PULL hooks, these are the fields you include in the data object of each record. For PUSH hooks, these are the fields available on ctx.event.before and ctx.event.after.
Common patterns
- externalId — Required in PULL records. This is your external system's unique identifier for the record. Flowstate uses it to match records across syncs.
- Dates — Use ISO 8601 format:
YYYY-MM-DD(e.g.,2025-06-15). - Custom attributes — All entity types support a
customAttributesobject for arbitrary key-value data. See Custom attributes.
Employee
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Yes | Employee's first name |
lastName | string | Yes | Employee's last name |
email | string | Yes | Work email address |
internalEmployeeId | string | No | Your internal employee identifier (e.g., payroll number) |
startDate | string (date) | No | Employment start date |
endDate | string (date) | No | Employment end date (for terminated employees) |
jobRole | object or string | No | Role the employee holds. See Job role. |
teamAllocations | array | No | See Team allocations |
projectAllocations | array | No | See Project allocations |
salaryAdjustments | array | No | See Salary adjustments |
customAttributes | object | No | See Custom attributes |
js
{
externalId: 'emp-001',
data: {
firstName: 'Jane',
lastName: 'Smith',
email: 'jane.smith@example.com',
internalEmployeeId: 'EMP-2024-042',
startDate: '2024-03-15',
jobRole: { title: 'Senior Engineer', externalId: 'ROLE-042' },
teamAllocations: [
{ teamId: 'team-042', teamName: 'Platform', startDate: '2024-03-15', fte: 1.0 }
]
}
}Vacancy
| Field | Type | Required | Description |
|---|---|---|---|
role | string | Yes | Job title or role name |
description | string | No | Role description |
status | string | No | Vacancy status (defaults to open) |
fte | number | No | Full-time equivalent value (defaults to 1.0) |
targetStartDate | string (date) | No | Intended start date for the hire |
targetFillDate | string (date) | No | Target date to fill the vacancy |
salaryMin | number | No | Minimum salary for the role |
salaryMax | number | No | Maximum salary for the role |
currencyCode | string | No | ISO 4217 currency code for salary values (e.g., GBP, USD) |
jobRole | object or string | No | Role the vacancy is hiring for. See Job role. |
teamAllocations | array | No | See Team allocations |
projectAllocations | array | No | See Project allocations |
customAttributes | object | No | See Custom attributes |
js
{
externalId: 'vac-101',
data: {
role: 'Senior Software Engineer',
description: 'Backend systems team',
status: 'open',
fte: 1.0,
targetStartDate: '2025-09-01',
salaryMin: 85000,
salaryMax: 110000,
currencyCode: 'GBP',
jobRole: { title: 'Senior Engineer', externalId: 'ROLE-042' }
}
}Contractor
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Contractor or company name |
email | string | No | Contact email address |
contractorType | string | No | individual or company (defaults to individual) |
rateType | string | No | hourly, daily, or monthly |
rate | number | No | Compensation rate |
currencyCode | string | No | ISO 4217 currency code for rate (e.g., GBP, USD) |
startDate | string (date) | No | Contract start date |
endDate | string (date) | No | Contract end date |
teamAllocations | array | No | See Team allocations |
projectAllocations | array | No | See Project allocations |
customAttributes | object | No | See Custom attributes |
js
{
externalId: 'ctr-050',
data: {
name: 'Acme Consulting Ltd',
email: 'billing@acme-consulting.com',
contractorType: 'company',
rateType: 'daily',
rate: 750,
currencyCode: 'GBP',
startDate: '2025-01-06',
endDate: '2025-06-30'
}
}Project
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Project name |
description | string | No | Project description |
projectCode | string | No | Internal project code or reference |
startDate | string (date) | No | Project start date |
endDate | string (date) | No | Project end date |
estimatedCost | number | No | Estimated total cost |
priority | number | No | Priority ranking (defaults to 0) |
customAttributes | object | No | See Custom attributes |
js
{
externalId: 'proj-alpha',
data: {
name: 'Platform Migration',
description: 'Migrate core services to new infrastructure',
projectCode: 'PLAT-2025',
startDate: '2025-04-01',
endDate: '2025-12-31',
estimatedCost: 500000,
priority: 1
}
}Assignment
Assignments link employees to projects. Both employeeSourceId and projectSourceId reference the externalId values you used when syncing the corresponding employee and project records.
| Field | Type | Required | Description |
|---|---|---|---|
employeeSourceId | string | Yes | The externalId of the employee |
projectSourceId | string | Yes | The externalId of the project |
fte | number | No | FTE allocation (defaults to 1.0) |
startDate | string (date) | No | Assignment start date |
endDate | string (date) | No | Assignment end date |
js
{
externalId: 'assign-001',
data: {
employeeSourceId: 'emp-001',
projectSourceId: 'proj-alpha',
fte: 0.5,
startDate: '2025-04-01',
endDate: '2025-12-31'
}
}TIP
Assignments require that both the employee and the project have already been synced via PULL hooks using the same integration. The employeeSourceId and projectSourceId are matched against existing records by their externalId.
Team allocations
Nested inside an Employee, Vacancy, or Contractor record as teamAllocations. Each entry places the parent on a team over a time range. Teams are auto-created when neither teamId nor an existing name matches.
| Field | Type | Required | Description |
|---|---|---|---|
teamId | string | No* | External system's team identifier. Preferred over teamName for stability. |
teamName | string | No* | Team display name. Used to look up or auto-create the team if teamId is not provided. |
startDate | string (date) | No | Allocation start date (ISO 8601). Defaults to today. |
endDate | string (date) | No | Allocation end date. Omit or set null for open-ended. |
fte | number | No | FTE allocation (0.0–1.0). Defaults to 1.0. |
*At least one of teamId or teamName must be provided.
js
{
externalId: 'emp-001',
data: {
firstName: 'Jane',
lastName: 'Smith',
email: 'jane.smith@example.com',
teamAllocations: [
{ teamId: 'team-042', teamName: 'Platform', startDate: '2024-03-15', fte: 0.8 },
{ teamId: 'team-099', teamName: 'On-call rota', startDate: '2024-03-15', fte: 0.2 }
]
}
}Re-running a sync with the same (employee, team, startDate) is idempotent — duplicates are skipped.
Legacy aliases
teamAssignments (with externalTeamId, fromDate, toDate) is still accepted for backward compatibility with older hooks. New hooks should use the canonical names above.
Project allocations
Nested inside an Employee, Vacancy, or Contractor record as projectAllocations. Each entry places the parent on a project over a time range. Projects are auto-created when neither projectId nor an existing name matches.
| Field | Type | Required | Description |
|---|---|---|---|
projectId | string | No* | External system's project identifier. Preferred over projectName for stability. |
projectName | string | No* | Project display name. Used to look up or auto-create the project if projectId is not provided. |
startDate | string (date) | No | Allocation start date (ISO 8601). Defaults to today. |
endDate | string (date) | No | Allocation end date. Omit or set null for open-ended. |
fte | number | No | FTE allocation (0.0–1.0). Defaults to 1.0. |
*At least one of projectId or projectName must be provided.
js
{
externalId: 'emp-001',
data: {
firstName: 'Jane',
lastName: 'Smith',
email: 'jane.smith@example.com',
projectAllocations: [
{ projectId: 'proj-alpha', projectName: 'Platform Migration', startDate: '2025-04-01', endDate: '2025-12-31', fte: 0.5 }
]
}
}Legacy aliases
projectAssignments (with externalProjectId, fromDate, toDate) is still accepted for backward compatibility with older hooks. New hooks should use the canonical names above.
When to use nested allocations vs the top-level Assignment entity
Use nested teamAllocations / projectAllocations to sync an employee and their allocations in a single record. Use the top-level Assignment entity when employees and projects are synced separately and you want to link them afterwards by externalId.
Salary adjustments
Nested inside an Employee record as salaryAdjustments. Each entry is a dated salary change. Deduplicated by (employee, effectiveDate) so re-running a sync is safe.
| Field | Type | Required | Description |
|---|---|---|---|
effectiveDate | string (date) | Yes | Date the new salary takes effect (ISO 8601). |
salary | number | Yes | Annualised salary amount. |
currencyCode | string | Yes | ISO 4217 currency code (e.g., GBP, USD). |
bonus | number | No | Optional annual bonus amount. |
reason | string | No | Free-text reason for the change (e.g., "promotion"). |
Entries missing any required field are silently skipped.
Job role
Nested inside an Employee or Vacancy record as jobRole. Accepts either an object with title and/or externalId, or a bare string (shorthand for { title: "…" }). Omit the field to leave the role unchanged on updates.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No* | Role name (e.g., 'Senior Engineer'). Used for lookup by name and for auto-creation when no match exists. |
externalId | string | No* | Your external system's identifier for the role. Preferred over title for stability. |
*At least one of title or externalId must be provided.
js
{
externalId: 'emp-001',
data: {
firstName: 'Jane',
lastName: 'Smith',
email: 'jane@example.com',
jobRole: { title: 'Senior Engineer', externalId: 'ROLE-042' }
}
}Resolution order:
- Match by
externalId(scoped to your organisation). - Fall back to matching by
title(the role'sname). If the matched role has noexternalIdyet and you supplied one, Flowstate patches it so subsequent syncs deduplicate correctly. - Auto-create a new role using
titleif nothing matches. Supplying onlyexternalIdwith no match is treated as "no role" — the role is not created.
Shorthand string form (equivalent to { title: 'Senior Engineer' }):
js
{ jobRole: 'Senior Engineer' }See Configuration: Job Roles for the full role management model.
Custom attributes
All entity types (except assignments) support a customAttributes field for syncing arbitrary data to Flowstate's custom attribute system.
js
{
externalId: 'emp-001',
data: {
firstName: 'Jane',
lastName: 'Smith',
email: 'jane@example.com',
customAttributes: {
cost_centre: 'ENG-001',
security_clearance: 'SC',
contract_renewal: '2026-03-01'
}
}
}Custom attributes are matched by key to Custom Attribute Definitions configured in your Flowstate organisation (Settings → Configuration → Custom Attributes). Keys that don't match a definition are silently ignored.
Supported field types
| Definition type | Expected value format | Example |
|---|---|---|
| String | String value | 'ENG-001' |
| Number | Numeric value | 42 or 3.14 |
| Date | ISO 8601 date string | '2025-06-15' |
| Date Range | Object with start and end | { start: '2025-01-01', end: '2025-12-31' } |