Documentation Get help

API Changelog

Changes to the Flowstate REST API, the MCP server, outbound webhooks and custom integrations, newest first. GraphQL is the app’s own API and isn’t covered here.

Version numbers follow Semantic Versioning. Read Changed and Removed before you upgrade an integration.

For product changes, see Flowstate Updates.


v1.6.0 — 2026-09-10

Functional groups over REST, MCP, webhooks and custom integrations. Teams are archived rather than deleted, and MCP says more about projects and effort.

Added

  • Functional groups REST endpoints under /org/{orgId}/functional-groups: GET /tree (the whole visible hierarchy on a day, up to 500 groups), GET / (list, paged with offset and limit), GET /:id, GET /:id/summary, GET /:id/positions, POST / and PUT /:id (create or update a group), POST /:id/move, POST /positions and PUT /positions/:id, POST /assignments and PUT /assignments/:id, POST /structure/move, POST /access and DELETE /access/:id, and custom attribute values under /groups/:id/custom-attributes and /positions/:id/custom-attributes. Reads take asOf; reads and writes take ?scenarioId=. Every path returns 403 FEATURE_DISABLED while the module is off for the organisation. The endpoints are in the OpenAPI spec. See Functional groups.
  • Thirteen functional group MCP tools. Read: list_functional_groups, get_functional_group. Write: add_functional_group, update_functional_group, move_functional_group, add_functional_position, update_functional_position, fill_functional_position, update_functional_assignment, move_functional_position, add_functional_group_manager, remove_functional_group_manager, set_functional_group_cost_visibility. See MCP server.
  • Three webhook entity types: functional_group, functional_position and functional_assignment. These records are dated and never deleted, so retiring one arrives as an update carrying its endDate, never as a delete. See Webhooks.
  • Custom integration hooks for the functional structure. PULL and PUSH hooks can target functional_group, functional_position and functional_assignment. See Data model.
  • Custom attributes on functional groups and positions: the FUNCTIONAL_GROUP and FUNCTIONAL_POSITION entity types. See Custom attributes.
  • archivedAt and archiveAppliedAt on teams. Returned by GET /teams and GET /teams/:id. Both are read-only. See Teams.
  • isOwnershipOnly on team assignments. Read-only. true when a team is attached to a project as its owner without allocating any capacity; those assignments have an fte of 0. See Assignments.
  • Archive state over MCP. search_teams takes archived (active, archived or all), and search_teams and get_team_details return archivedAt and isArchived.
  • Find projects by PMS link over MCP. search_projects takes pmsLinked (false finds projects linked to no Jira, Linear or Azure DevOps project) and sourceSystem (how the project was created, such as manual or jira). Each result carries sourceSystem and pmsLinked. get_project_details also returns sourceSystem, pmsLinked and linkedPmsProjects.
  • Effort units over MCP. get_team_effort and get_project_effort totals carry unit: "person-days": fte figures are work-day equivalents summed over the window, not an average FTE.

Changed

  • Teams are archived in the app instead of deleted. Archiving sets the day the team goes: from that day it’s hidden in the app and its open allocations end the day before. Archiving sends a team update webhook. Over REST: GET /teams still returns archived teams, so filter on archivedAt; PATCH /teams/:id can’t set archivedAt; and DELETE /teams/:id still permanently deletes the team and its allocations. Archive in the app when you want to keep a team’s history.
  • search_teams leaves archived teams out by default. Pass archived: "all" to include them.
  • search_projects returns { totalCount, returned, projects } instead of a bare list. totalCount counts every match, not just the page returned.

v1.5.0 — 2026-08-19

Push business metric readings over REST, four Insights tools on MCP, and AI spend questions through query_analytics.

Added

  • POST /org/{orgId}/business-metrics/readings: push a batch of up to 1,000 readings for one metric from one source. Each reading upserts on metric, source and periodStart, so sending a period again corrects it. A source that doesn’t exist yet is created on the first push. Needs the business_metrics_ingest permission (Ingest Business Metric Readings). Limited to 120 requests a minute per API key; over the limit the endpoint returns 429 with a Retry-After header and { "error": "too_many_requests", "error_description": "…" }. The endpoint is in the OpenAPI spec. See Business metrics.
  • Four MCP tools, each returning one Insights screen in a single answer: get_ai_adoption_summary (AI adoption and idle seats), get_business_metrics_summary (every active metric with volume, AI share and cost per unit), get_hybrid_workforce_summary (employees, contractors and agents with cost and AI share) and get_project_value_summary (a project’s cost against the value it promised). See MCP server.
  • Top-N and team roll-ups in query_analytics. sort (one of the requested metrics), order (ASC or DESC) and limit (up to 25 series) answer “top five” questions. The DESCENDANT_OF filter operator, on TEAM only, matches a team and every team beneath it.
  • AI and code delivery in query_analytics. On ACTUAL data it accepts the metrics AI_COST, AI_SESSIONS, AI_FRUSTRATION, AI_INPUT_QUALITY_AVG, AI_TOKENS, AI_REQUESTS, PR_COUNT, COST_PER_PR and LINES_CHANGED, and the dimensions PROVIDER, MODEL, AI_USE_CATEGORY, AI_BUSINESS_FUNCTION, AI_ACTIVITY_TYPE, REPO, DELIVERY_KIND and OPERATOR. TEAM_PARENT and JOB_ROLE_FAMILY are listed too. COST, AI_COST and COST_PER_PR need financial access.

Removed

  • get_ai_usage_summary, get_ai_spend_by_team and get_ai_spend_by_person. Ask query_analytics for AI_COST by PROVIDER, TEAM or EMPLOYEE instead.

v1.4.0 — 2026-07-10

CapEx, R&D, code delivery and more budget tools on MCP, AI policy webhooks, and one salary per vacancy.

Added

  • MCP tools for effort, budgets, CapEx, R&D and code delivery.

    • Effort: get_effort_gaps, get_uncosted_effort, get_submission_leaderboard.
    • Budgets and forecast: list_forecast_budget_snapshots, get_forecast_budget_drift_summary, add_budget_proposal_comment, cancel_budget_request.
    • CapEx and R&D: get_capex_project_breakdown, get_capex_initiative_breakdown, get_capex_declared_vs_claimed, list_capex_claims, list_rd_claims, delete_capitalisation.
    • Code delivery: get_code_delivery_summary, get_code_delivery_trends, list_pull_requests.
    • Scenario allocations: allocate_contractor_to_project, allocate_vacancy_to_project, remove_allocation.
    • AI sessions: mark_my_ai_sessions_as_project.

    See MCP server.

  • More detail on AI agents over MCP. add_ai_agent and update_ai_agent take purpose, agentOrigin, providerNativeAgentId, region, modelSlug, ownerEmployeeId, ownerContractorId and aiServiceAccountId.

  • The ai_policy webhook entity type. See Webhooks.

Changed

  • Vacancies carry one salary. Send salary when you create or update a vacancy over REST or from a custom integration. salaryMin and salaryMax are still accepted but deprecated: an explicit salary wins, otherwise a range is stored as its midpoint. Vacancy responses return salary and no longer return salaryMin or salaryMax. See Vacancies.
  • add_ai_agent needs providerSlug (for example anthropic or openai) in place of provider. monthlyPerSeatPrice is optional.
  • merge_budget_proposal no longer merges. It returns an error telling you to lock the budget request, which finalises the approved budget.
  • approve_budget_proposal returns the resulting status: APPROVED, or MERGED when approval rolls the proposal into its parent.

Removed

  • set_initiative_commitment. An initiative’s resourcing is the sum of its projects’ allocations.
  • costCentreId from get_initiative_details. A project’s cost centre lives on the project.
  • get_ai_wastage_summary.

v1.3.0 — 2026-06-11

Initiatives, objectives and the budget workflow on MCP, custom attributes in MCP searches, project delivery status, and team and initiative hooks.

Added

  • Initiative, objective and key result MCP tools. Read: search_initiatives, get_initiative_details, list_portfolio, list_objectives. Write: add_initiative, update_initiative, delete_initiative, adopt_project_to_initiative, detach_project_from_initiative, set_initiative_commitment, create_capex_claim, add_objective, update_objective, delete_objective, add_key_result, update_key_result, delete_key_result. These writes change live data; no scenario is needed. See MCP server.
  • Budget workflow MCP tools. Read: list_budget_requests, get_budget_request, get_budget_proposal, get_budget_envelope. Write, on live data: create_budget_request, assign_budget_proposal, submit_budget_proposal, approve_budget_proposal, reject_budget_proposal, merge_budget_proposal.
  • Custom attributes over MCP. list_custom_attributes lists the keys. search_employees, search_teams, search_projects, search_contractors and search_vacancies take customAttributeFilters and return each record’s customAttributes, as do get_employee_details, get_team_details and get_project_details.
  • get_ai_security_metrics and get_ai_wastage_summary MCP tools.
  • deliveryStatus filter on search_projects: BACKLOG, TODO, IN_PROGRESS, IN_REVIEW, DONE or CANCELLED. search_projects and get_project_details return the project’s status.
  • Vacancy end dates. Vacancies take targetEndDate for a fixed-term role over REST and from custom integrations; GET /vacancies can sort by it. An end date before targetStartDate returns 400. Over MCP, add_vacancy takes targetStartDate and targetEndDate, and update_vacancy takes targetEndDate. See Vacancies.
  • Job roles on contractors. POST and PATCH /contractors take jobRoleId, or jobRole with a title or externalId; contractor records from custom integrations take jobRole too.
  • The agent_policy and initiative webhook entity types. See Webhooks.
  • team and initiative hook entities. PULL and PUSH hooks can target teams, including the parent team and team manager, and initiatives. See Data model.

Changed

  • Salary and rate data needs financials_view_detailed. Reading salary adjustments or rate adjustments, and include=currentSalary, salaryHistory, currentRate or rateHistory, return 403 FORBIDDEN for an API key without it. Reading a record’s custom attribute values needs settings_entity_config_view. See Authentication.
  • Changing a vacancy’s dates moves its assignments. When PATCH /vacancies/:id changes targetStartDate or targetEndDate, the vacancy’s team and project assignments move to the new dates. See Vacancies.
  • A project’s status is its delivery status, such as BACKLOG or IN_PROGRESS. It’s read-only over REST. See Projects.
  • MCP tools check permissions. Each write tool needs the matching Flowstate permission, and scenario writes need a scenario still in draft; otherwise the tool returns an error. Employee salary, bonus, currentSalary and salaryHistory, and contractor rate, rateType and currency, come back null for people without financials_view_detailed.
  • get_budget_envelope reports amounts in the envelope’s currency. It returns currencyCode, totalCost and totalFte, and divergence reports envelopeCost, currentCost, deltaCost and the matching FTE figures in that currency.
  • MCP client registration has limits. Redirect URIs must be https, or http on localhost, 127.0.0.1 or [::1]; anything else returns 400 invalid_redirect_uri. A client can register at most 5 redirect URIs and a client_name of at most 200 characters; beyond that the response is 400 invalid_client_metadata. Each IP address can register at most 20 clients an hour. See MCP server.
  • Custom integration records need an externalId. A record without a non-empty externalId is rejected on its own with an error; the rest of the run carries on.

Removed

  • lifecycleStageId on projects over REST, and on add_project and update_project over MCP. The lifecycleStage filter and field on search_projects and get_project_details are gone too; use deliveryStatus and status.
  • targetFillDate on vacancies, from REST requests, responses and sortBy, and from custom integration vacancy records. It’s ignored if sent. Use targetStartDate and targetEndDate.
  • The lifecycle_stage webhook entity type.
  • envelopeAmountUSD, totalCostUSD and divergence.divergenceCostUSD from get_budget_envelope.

v1.2.0 — 2026-05-12

Your own IDs everywhere, custom integrations, contractor-filled vacancies, and slicing and filtering in query_analytics.

Added

  • Custom integrations. Write PULL hooks that run on a schedule or on demand, and PUSH hooks that run when a record is created, updated or deleted, for employee, vacancy, contractor, project and assignment. Hooks get ctx.secrets, ctx.http, ctx.kv, ctx.log and ctx.org, PUSH hooks get ctx.event, and a PULL hook can page through a source by returning more and meta. See Custom integrations.
  • externalId on employees, contractors, vacancies, teams and projects. Set it on create or update and it comes back in responses. Any path :id, and reference fields such as teamId, projectId, employeeId, managerId and hiringManagerId, accept either the Flowstate ID or your externalId. An externalId shaped like a Flowstate ID returns 400.
  • attributeKey on custom attribute definitions. A stable key of lowercase letters, digits and underscores, generated from the name if you don’t send one. It can’t be changed after creation. Custom integrations key customAttributes by it.
  • SELECT and CURRENCY custom attribute types. Definitions take selectOptions; values take selectValue, or currencyCode with currencyAmount. See Custom attributes.
  • Job roles by title or your own ID. Employee and vacancy create and update, the vacancy fill endpoint, and custom integration employee and vacancy records take jobRole: { title, externalId }. A matching role is used, otherwise one is created. jobRoleId takes precedence.
  • Fill a vacancy with a contractor. POST /vacancies/:id/fill takes fillerType: employee (the default) or contractor. The contractor branch needs name, rate, rateType and currencyCode, and creates the contractor with its first rate adjustment. Sending employee fields with fillerType: "contractor" returns 400. The response has employee and contractor, and the one not used is null. Filling a vacancy that’s already filled returns 409. See Vacancies.
  • filledByLiveContractorId on vacancy reads, alongside filledByLiveEmployeeId. Both are set by the fill endpoint, not by PATCH.
  • Filled-by references in custom integrations. Vacancy records take filledBy: { externalId } or the shorthand filledByExternalId, matched against employees and contractors, or filledByEmployeeId or filledByContractorId. filledBy: null clears the fill. See Syncing positions as vacancies.
  • Allocations and deletions in custom integrations. Records take teamAllocations and projectAllocations with teamId, projectId, startDate and endDate; the earlier field names still work. deletedAt on a record, or on a nested allocation, salary or rate entry, deletes it, and an allocation the integration created that’s missing from a sent list is removed. Nested salary, rate and project allocation entries take externalId. See Data model.
  • Filters and effort submissions in query_analytics. filters takes a JSON array of { dimension, operator, values } with EQ, IN or NOT_IN. costSection narrows actuals to UNLINKED_PMS_WORK, HOLIDAY, NON_PROJECT or UNTRACKED. dataSource: "SUBMISSION" with the SUBMISSION_COUNT metric answers questions about effort submissions, and PMS_PROJECT is a dimension on actuals.

Changed

  • Contractor rateType is one of hourly, daily, monthly or annually. Any other value is rejected on REST contractor writes, nested rateAdjustment and rate adjustments, on add_contractor and update_contractor over MCP, and on custom integration contractor records. annually is newly accepted.
  • One failing custom integration record no longer fails the rest of its page. Every PULL processes every record it returns.

v1.1.0 — 2026-04-08

The MCP server, outbound webhook events, salary and rate adjustments, custom attributes, nested writes and vacancy fill.

Added

  • The MCP server. People connect Claude, ChatGPT and other assistants by signing in with their own Flowstate account over OAuth; MCP doesn’t use API keys. An admin switches it on for the organisation. The first release carried the read tools for people, teams, projects, contractors, vacancies, analytics and AI spend, and scenario write tools. Limited to 100 requests a minute per person, with X-RateLimit-Limit and X-RateLimit-Remaining on every response and 429 over the limit. See MCP server and Rate limiting.
  • get_project_effort, get_team_effort and get_unattributed_effort MCP tools.
  • Outbound webhook events for employees, teams, contractors, projects, vacancies, employee, contractor and vacancy allocations to teams and projects, team allocations to projects, salary adjustments, bonuses, contractor rates and AI agents. Each delivery is a create, update or delete with before and after, signed in X-Flowstate-Signature, with X-Flowstate-Event-Id for de-duplication. Merging a scenario sends one event per change with initiator.type merge. See Webhooks.
  • Salary adjustments and contractor rate adjustments. List, create, update and delete them under /employees/:id/salary-adjustments and /contractors/:id/rate-adjustments. See Salary adjustments and Contractor rate adjustments.
  • Custom attributes. Define STRING, NUMBER, DATE and DATE_RANGE fields for employees, contractors, vacancies, teams and projects with GET, POST, PATCH and DELETE /custom-attributes. Read a record’s values with GET /{resource}/:id/custom-attributes, and set or remove one with PUT or DELETE /{resource}/:id/custom-attributes/:definitionId. GET for a single record returns customAttributes. See Custom attributes.
  • Nested writes. POST and PATCH /employees take salary, teamAssignment and projectAssignment; POST and PATCH /contractors take rateAdjustment, teamAssignment and projectAssignment. On PATCH they add a new record rather than replacing one.
  • Vacancy fill. POST /vacancies/:id/fill turns a vacancy into an employee and moves the vacancy’s allocations across.
  • ?include= embeds related records. Employees: currentSalary, salaryHistory, assignments. Contractors: currentRate, rateHistory, assignments. Vacancies (single record): assignments, filledByEmployee. An unknown value returns 400.

v1.0.0 — 2026-03-11

The Flowstate REST API.

Added

  • Employees: create, read, update and delete employee records.
  • Contractors: contractors with rate, rateType, currencyCode and contract dates.
  • Vacancies: open roles with a salary range and a status.
  • Teams: teams with a parent team.
  • Projects: projects with dates and an estimated cost.
  • Assignments: allocate employees, contractors and vacancies to a team or a project under /assignments/employees, /assignments/contractors and /assignments/vacancies, and teams to projects under /assignments/teams.
  • Pagination: page, limit (up to 100), search, sortBy and sortDir on every list. See Pagination.
  • Authentication: API keys sent as Authorization: Bearer private_…. Each key carries its own permissions, never more than its creator’s, and lasts at most 90 days. A missing permission returns 403 FORBIDDEN. See Authentication.
  • Errors: { "error": { "code", "message", "details", "errorId" } }. See Errors.
  • OpenAPI spec and Swagger UI describing the API.