Authentication
Every REST request carries an API key as a bearer token. A key belongs to one organisation, holds an explicit list of permissions and expires after at most 90 days.
curl "https://{tenant}.flowstate.inc/api/v1/org/{orgId}/teams" \
-H "Authorization: Bearer $FLOWSTATE_API_KEY"
The Authorization header is the only place a key is read from.
The MCP server doesn’t use API keys; each person signs in with OAuth.
Key format
private_<identifier>_<secret>
identifier is 32 lowercase hexadecimal characters and secret is 64. The full key is shown once, when it’s created.
Create a key
You need the Create API Keys permission.
- Go to Settings → Users & Access → API Keys and select Create API Key.
- Name — the integration that will use it.
- Expiration — 7 days, 30 days (default), 60 days or 90 days.
- Permissions — tick what the integration calls; see Permissions by endpoint. You can only grant permissions your own role has; anything else fails with “Cannot grant permissions you do not have”.
- Select Create API Key and copy the key.
Admin-facing detail: Create and manage API keys.
Rotate and revoke
- Rotate before the expiry date: create a key with the same permissions, deploy it, then revoke the old one.
- Revoke from the key’s row (needs Revoke API Keys). The next request with that key returns
401.
Permissions by endpoint
A key’s permissions are fixed when it’s created. The checkbox label is in brackets.
| Endpoints | Permissions |
|---|---|
/employees · /assignments/employees · /employees/{id}/salary-adjustments | team_employees_view · team_employees_create · team_employees_update · team_employees_delete (View / Create / Update / Delete Employees) |
POST /vacancies/{id}/fill | team_employees_create (Create Employees) |
/contractors · /assignments/contractors · /contractors/{id}/rate-adjustments | team_contractors_view · _create · _update · _delete (… Contractors) |
/vacancies · /assignments/vacancies | team_vacancies_view · _create · _update · _delete (… Vacancies) |
/teams · /assignments/teams | team_teams_view · _create · _update · _delete (… Teams) |
/projects | roadmap_projects_view · _create · _update · _delete (… Projects) |
/custom-attributes | settings_entity_config_view (read) · settings_entity_config_update (create, update) · settings_entity_config_delete (… Entity Configuration) |
/{resource}/{id}/custom-attributes | settings_entity_config_view (read) · settings_entity_config_update (set, clear) |
POST /business-metrics/readings | business_metrics_ingest (Ingest Business Metric Readings) |
/functional-groups | See Functional groups. |
Pay data also needs financials_view_detailed (View Detailed Financials):
GET /employees/{id}/salary-adjustmentsandGET /contractors/{id}/rate-adjustmentsinclude=currentSalaryorinclude=salaryHistoryon employeesinclude=currentRateorinclude=rateHistoryon contractors
Functional group requests are also limited to the areas and scenarios the key’s creator can currently access.
Errors
Authentication errors have code and message and no errorId.
{
"error": {
"code": "UNAUTHORIZED",
"message": "API key has expired"
}
}
| Status | message | Cause |
|---|---|---|
401 | Missing or invalid Authorization header. Expected: Bearer private_... | No header, or it doesn’t start Bearer private_. |
401 | Invalid API key format | Not private_ + 32 hex + _ + 64 hex. |
401 | Invalid API key | No such key. |
401 | API key has been revoked | |
401 | API key has expired | |
403 | API key does not have access to this organization | orgId in the path isn’t the key’s organisation. |
403 | API key lacks required permission: <permission> | Add the permission by creating a new key. |
403 bodies carry an errorId. All codes: Errors.
Handling keys
- One key per integration, so one can be revoked alone.
- Keep keys in a secrets manager or environment variable, never in source control or client-side code.
- Alert on expiry: a key stops working on its expiry date with
401 API key has expired.