Skip to content

Recipes

Task-oriented guides for common integration workflows. Each recipe shows the full sequence of API calls, request bodies, and responses.

Prerequisites

All recipes assume you have an API key and know your organization ID. See the API Overview for setup instructions. All URLs use the base https://{tenant}.flowstate.inc/api/v1.


Onboard a New Employee

Create an employee with their starting salary and team assignment in a single request using nested create.

Request

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Kai",
    "lastName": "Nakamura",
    "email": "kai.nakamura@example.com",
    "internalEmployeeId": "EMP-3042",
    "startDate": "2026-05-01",
    "geographyId": "clx3g2h1j0k9l8m7",
    "workTypeId": "clx2w3x4y5z6a7b8",
    "jobRoleId": "clx9r8q7w6e5t4r3",
    "managerId": "clx9m4n5o6p7q8r9",
    "salary": {
      "effectiveDate": "2026-05-01",
      "salary": 92000,
      "currencyCode": "GBP",
      "bonus": 8000,
      "reason": "Starting salary"
    },
    "teamAssignment": {
      "teamId": "clx6t7u8v9w0x1y2z3a4",
      "fte": 1.0,
      "startDate": "2026-05-01"
    }
  }'

Response (201 Created)

json
{
  "data": {
    "id": "clx7n8m9k0j1h2g3f4e5",
    "firstName": "Kai",
    "lastName": "Nakamura",
    "email": "kai.nakamura@example.com",
    "internalEmployeeId": "EMP-3042",
    "startDate": "2026-05-01",
    "endDate": null,
    "noticeDate": null,
    "managerId": "clx9m4n5o6p7q8r9",
    "jobRoleId": "clx9r8q7w6e5t4r3",
    "workTypeId": "clx2w3x4y5z6a7b8",
    "geographyId": "clx3g2h1j0k9l8m7",
    "defaultCurrencyCode": null,
    "createdAt": "2026-04-08T09:00:00Z",
    "updatedAt": "2026-04-08T09:00:00Z"
  }
}

What happened

  • The employee record was created.
  • A salary adjustment of 92,000 GBP with an 8,000 GBP bonus was created with effective date 2026-05-01.
  • A team assignment at 1.0 FTE was created linking the employee to the Platform Engineering team from their start date.
  • All three records were created atomically -- if any part fails, nothing is created.

Fill a Vacancy

Convert an open vacancy into a hired employee. This transfers all vacancy assignments to the new employee.

Step 1: Fill the vacancy

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 '{
    "firstName": "Sarah",
    "lastName": "Okonkwo",
    "email": "sarah.okonkwo@example.com",
    "startDate": "2026-06-01",
    "salary": 140000,
    "currencyCode": "USD"
  }'

Response (201 Created)

json
{
  "data": {
    "id": "clx7n8m9k0j1h2g3f4e5",
    "firstName": "Sarah",
    "lastName": "Okonkwo",
    "email": "sarah.okonkwo@example.com",
    "internalEmployeeId": null,
    "startDate": "2026-06-01",
    "endDate": null,
    "noticeDate": null,
    "managerId": "clx9m4n5o6p7q8r9",
    "jobRoleId": "clx9r8q7w6e5t4r3",
    "workTypeId": "clx2w3x4y5z6a7b8",
    "geographyId": "clx3g2h1j0k9l8m7",
    "defaultCurrencyCode": null,
    "createdAt": "2026-04-08T10:00:00Z",
    "updatedAt": "2026-04-08T10:00:00Z"
  }
}

Step 2 (optional): Verify the vacancy is filled

bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/vacancies/clx5v6w7x8y9z0a1b2c3" \
  -H "Authorization: Bearer private_..."

The vacancy now shows "status": "filled" and "filledByLiveEmployeeId": "clx7n8m9k0j1h2g3f4e5".

What happened

  • A new employee was created with the provided name, email, and start date.
  • The vacancy's jobRoleId, workTypeId, geographyId, and hiringManagerId were inherited by the new employee (since they were not provided in the fill request).
  • A salary adjustment of 140,000 USD was created effective 2026-06-01.
  • All team and project assignments that were on the vacancy were duplicated as employee assignments.
  • The vacancy's status changed to "filled".

Sync Employees from HRIS

A typical HRIS sync pattern: list existing employees, compare with your source system, then create or update as needed.

Step 1: List all employees with their internal IDs

Page through all employees to build a lookup map:

bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees?limit=100&page=1" \
  -H "Authorization: Bearer private_..."

Continue incrementing page until hasNextPage is false. Use internalEmployeeId to match records against your HRIS.

Step 2: Create new employees

For each employee in your HRIS that has no match in Flowstate:

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Aisha",
    "lastName": "Patel",
    "email": "aisha.patel@example.com",
    "internalEmployeeId": "HR-5089",
    "startDate": "2026-03-01",
    "geographyId": "clx3g2h1j0k9l8m7",
    "workTypeId": "clx2w3x4y5z6a7b8"
  }'

Step 3: Update changed employees

For each employee that exists in both systems but has changed fields:

bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "aisha.patel-new@example.com",
    "endDate": "2026-12-31"
  }'

Best practices

  • Use internalEmployeeId as the stable join key between your HRIS and Flowstate.
  • Page through results with limit=100 (the maximum) to minimize API calls.
  • Use PATCH for updates -- it only modifies the fields you send.
  • To handle salary changes from your HRIS, use the nested salary object on the PATCH request or create salary adjustments directly.

Sync Employees Using External IDs

The recommended sync pattern for automated integrations. Use externalId to correlate records between your system and Flowstate -- no need to maintain a separate ID mapping table.

Step 1: Create employees with your external ID

Set externalId when creating each employee. This becomes the stable correlation key.

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "firstName": "Aisha",
    "lastName": "Patel",
    "email": "aisha.patel@example.com",
    "externalId": "HR-5089",
    "startDate": "2026-03-01",
    "geographyId": "clx3g2h1j0k9l8m7"
  }'

Step 2: Update using the external ID directly in the path

No need to look up the Flowstate CUID -- use the external ID as the path parameter:

bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/HR-5089" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "aisha.patel-new@example.com",
    "endDate": "2026-12-31"
  }'

The :id path parameter auto-detects the format -- CUIDs (25-character lowercase alphanumeric) are looked up by id; everything else is treated as an externalId lookup.

Step 3: Fetch by external ID

bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/HR-5089" \
  -H "Authorization: Bearer private_..."

Best practices

  • Set externalId on every record during initial sync. This avoids the need to page through all records for matching.
  • Use externalId in path parameters for GET, PATCH, and DELETE -- it works everywhere the :id parameter is accepted.
  • Foreign key fields like managerId also accept external IDs (e.g., "managerId": "HR-4001").
  • externalId must be unique within your organization. It cannot resemble a Flowstate CUID (25-character lowercase alphanumeric).
  • This pattern works for all core entities: employees, contractors, vacancies, teams, and projects.

Record a Salary Change

Record a salary increase for an employee. This creates a new salary adjustment without affecting existing history.

Request

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9/salary-adjustments" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "effectiveDate": "2026-07-01",
    "salary": 110000,
    "currencyCode": "GBP",
    "bonus": 15000,
    "reason": "Annual performance review - exceeds expectations"
  }'

Response (201 Created)

json
{
  "data": {
    "id": "clx9k0l1m2n3o4p5q6r7",
    "employeeId": "clx1a2b3c4d5e6f7g8h9",
    "effectiveDate": "2026-07-01",
    "salary": 110000,
    "bonus": 15000,
    "currencyCode": "GBP",
    "reason": "Annual performance review - exceeds expectations",
    "createdAt": "2026-04-08T09:30:00Z",
    "updatedAt": "2026-04-08T09:30:00Z"
  }
}

What happened

  • A new salary adjustment was added to the employee's history.
  • The employee's current salary is now 110,000 GBP effective 2026-07-01.
  • The previous salary adjustment(s) remain unchanged in the history.
  • Flowstate will use the old salary for cost calculations before 2026-07-01 and the new salary after.

Verify the salary history

bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/employees/clx1a2b3c4d5e6f7g8h9?include=salaryHistory" \
  -H "Authorization: Bearer private_..."

Move an Employee Between Teams

To move an employee from one team to another, end the existing assignment and create a new one.

Step 1: Find the current team assignment

bash
curl -X GET "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/assignments/employees?employeeId=clx1a2b3c4d5e6f7g8h9&type=team" \
  -H "Authorization: Bearer private_..."

Response

json
{
  "data": [
    {
      "id": "clx8a9b0c1d2e3f4g5h6",
      "type": "team",
      "targetId": "clx6t7u8v9w0x1y2z3a4",
      "fte": 1.0,
      "startDate": "2025-01-01",
      "endDate": null,
      "createdAt": "2024-12-15T10:00:00Z",
      "updatedAt": "2025-06-01T14:00:00Z"
    }
  ],
  "meta": { "page": 1, "limit": 20, "total": 1, "hasNextPage": false }
}

Step 2: End the current assignment

Set the endDate to the last day on the old team:

bash
curl -X PATCH "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/assignments/employees/clx8a9b0c1d2e3f4g5h6" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "endDate": "2026-06-30"
  }'

Step 3: Create the new team assignment

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/assignments/employees" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "employeeId": "clx1a2b3c4d5e6f7g8h9",
    "teamId": "clx3d4e5f6g7h8i9j0k1",
    "fte": 1.0,
    "startDate": "2026-07-01"
  }'

What happened

  • The old team assignment was end-dated to 2026-06-30.
  • A new team assignment was created starting 2026-07-01 on the new team.
  • Headcount reports will show the employee on the old team through June and on the new team from July.
  • Cost attribution automatically follows the assignment dates.

Add a Contractor Engagement

Onboard a new contractor with their initial rate and team assignment.

Request

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/contractors" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Elena Vasquez",
    "email": "elena@design-studio.io",
    "contractorType": "individual",
    "startDate": "2026-05-01",
    "endDate": "2026-10-31",
    "geographyId": "clx1e2n3g4r5o0t6u7v8",
    "managerId": "clx9m4n5o6p7q8r9",
    "rateAdjustment": {
      "effectiveDate": "2026-05-01",
      "rateType": "daily",
      "rate": 950,
      "currencyCode": "EUR",
      "reason": "Initial engagement rate"
    },
    "teamAssignment": {
      "teamId": "clx6t7u8v9w0x1y2z3a4",
      "fte": 0.8,
      "startDate": "2026-05-01",
      "endDate": "2026-10-31"
    }
  }'

Response (201 Created)

json
{
  "data": {
    "id": "clx8p9q0r1s2t3u4v5w6",
    "name": "Elena Vasquez",
    "email": "elena@design-studio.io",
    "contractorType": "individual",
    "companyId": null,
    "startDate": "2026-05-01",
    "endDate": "2026-10-31",
    "managerId": "clx9m4n5o6p7q8r9",
    "geographyId": "clx1e2n3g4r5o0t6u7v8",
    "rateType": "daily",
    "rate": 950.00,
    "currencyCode": "EUR",
    "createdAt": "2026-04-08T09:00:00Z",
    "updatedAt": "2026-04-08T09:00:00Z"
  }
}

What happened

  • The contractor record was created with a 6-month engagement window.
  • A rate adjustment of 950 EUR/day was created effective 2026-05-01.
  • A team assignment at 0.8 FTE was created matching the engagement dates.
  • All three records were created atomically.

Set Up a Project with Team Allocations

Create a project and allocate teams to it.

Step 1: Create the project

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/projects" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Payment Gateway Migration",
    "projectCode": "PAY-MIG",
    "description": "Migrate from legacy payment processor to Stripe Connect.",
    "startDate": "2026-05-01",
    "endDate": "2026-11-30",
    "lifecycleStageId": "clx5a6b7c8d9e0f1g2h3",
    "estimatedCost": 380000,
    "priority": 1
  }'

Response (201 Created)

json
{
  "data": {
    "id": "clx4m5n6o7p8q9r0s1t2",
    "name": "Payment Gateway Migration",
    "projectCode": "PAY-MIG",
    "description": "Migrate from legacy payment processor to Stripe Connect.",
    "startDate": "2026-05-01",
    "endDate": "2026-11-30",
    "ownerUserId": null,
    "lifecycleStageId": "clx5a6b7c8d9e0f1g2h3",
    "priority": 1,
    "estimatedCost": 380000.00,
    "createdAt": "2026-04-08T10:00:00Z",
    "updatedAt": "2026-04-08T10:00:00Z"
  }
}

Step 2: Allocate the Payments team (primary)

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/assignments/teams" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "teamId": "clx7p8r9o0d1e2f3g4h5",
    "projectId": "clx4m5n6o7p8q9r0s1t2",
    "fte": 3.0,
    "startDate": "2026-05-01",
    "endDate": "2026-11-30",
    "role": "Primary",
    "costCategory": "CapEx"
  }'

Step 3: Allocate the Platform team (support)

bash
curl -X POST "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/assignments/teams" \
  -H "Authorization: Bearer private_..." \
  -H "Content-Type: application/json" \
  -d '{
    "teamId": "clx6t7u8v9w0x1y2z3a4",
    "projectId": "clx4m5n6o7p8q9r0s1t2",
    "fte": 1.0,
    "startDate": "2026-05-01",
    "endDate": "2026-08-31",
    "role": "Support",
    "costCategory": "CapEx"
  }'

What happened

  • A project was created with a 7-month timeline, estimated cost, and lifecycle stage.
  • The Payments team was allocated at 3.0 FTE for the full project duration as the primary team. This means 3 full-time equivalent people from that team are dedicated to the project.
  • The Platform team was allocated at 1.0 FTE for the first 4 months as support.
  • Both allocations are categorised as CapEx for financial reporting.
  • Flowstate will use these allocations to calculate project cost attribution based on the team members' salaries.

Flowstate Documentation