openapi: 3.0.3 info: title: 'Orgonaut API Reference' description: '' version: 1.0.0 servers: - url: 'https://app.orgonaut.co' tags: - name: 'AI Tooling Usage' description: '' - name: Actors description: '' - name: Aggregates description: '' - name: Astro description: '' - name: Auth/Tokens description: '' - name: Batch description: '' - name: 'Capability Studio' description: '' - name: Costs description: '' - name: 'Department Types' description: '' - name: Departments description: '' - name: Initiatives description: '' - name: Integrations description: '' - name: Lineage description: '' - name: 'Org Placements' description: '' - name: 'Org Units' description: '' - name: 'Org Units — Metrics' description: '' - name: 'Org Units — Structure' description: '' - name: 'Org Units — Velocity' description: '' - name: 'Organisation Accounts' description: "\nAPI descriptions intentionally use “Organisation Account” wording for user-facing clarity." - name: Positions description: 'Manage effective-dated team positions, linking actors, teams, and compensation within each scenario.' - name: 'Role Types' description: '' - name: 'Scenario Tasks' description: '' - name: Scenarios description: '' - name: Snapshots description: '' - name: 'Team Types' description: '' - name: Teams description: '' - name: 'Teams — Velocity' description: '' - name: 'Tenant Invitations' description: '' - name: 'Tenant Members' description: '' - name: 'Work Catalog' description: '' components: securitySchemes: default: type: http scheme: bearer description: 'You can retrieve your token by visiting your dashboard and clicking Generate API token.' bearerAuth: type: http scheme: bearer parameters: X-Tenant: name: X-Tenant in: header required: true schema: type: string description: 'Tenant slug; required on the root domain and optional when using a tenant subdomain.' security: - default: [] bearerAuth: [] paths: /api/v1/ai-tooling/usage: get: summary: 'AI Tooling — List daily usage records' operationId: aIToolingListDailyUsageRecords description: "Returns a paginated list of daily usage records across all matched and unmatched actors.\nRecords are ordered by `usage_date` descending (most recent first). Each record represents\none actor's usage of one AI tooling provider on a single day.\n\nSupports filtering by actor, provider, and date range. Unmatched records (where the\nsource email could not be resolved to an Orgonaut actor) have `actor_id: null`.\n\nRequires `ai-tooling.manage` ability." parameters: - in: query name: actor_id description: 'Filter by actor ID. Only returns records matched to this actor.' example: 42 required: false schema: type: integer description: 'Filter by actor ID. Only returns records matched to this actor.' example: 42 - in: query name: provider description: 'Filter by provider slug (e.g. "claude").' example: claude required: false schema: type: string description: 'Filter by provider slug (e.g. "claude").' example: claude - in: query name: from description: 'date Start of date range (inclusive, YYYY-MM-DD).' example: '2026-03-01' required: false schema: type: string description: 'date Start of date range (inclusive, YYYY-MM-DD).' example: '2026-03-01' - in: query name: to description: 'date End of date range (inclusive, YYYY-MM-DD). Must be on or after `from`.' example: '2026-03-25' required: false schema: type: string description: 'date End of date range (inclusive, YYYY-MM-DD). Must be on or after `from`.' example: '2026-03-25' - in: query name: per_page description: 'Results per page. Min 1, max 100. Defaults to 25.' example: 25 required: false schema: type: integer description: 'Results per page. Min 1, max 100. Defaults to 25.' example: 25 - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 128 actor_id: 42 provider: claude source_email: aoife.ryan@example.com usage_date: '2026-03-24' sessions_count: 5 lines_added: 320 lines_removed: 85 commits_count: 4 pull_requests_count: 1 total_input_tokens: 125000 total_output_tokens: 48000 total_cache_read_tokens: 32000 total_cache_creation_tokens: 8000 total_cost_cents: 245 currency: USD tool_actions: edit_tool: accepted: 18 rejected: 2 bash_tool: accepted: 12 rejected: 0 model_breakdown: - model: claude-opus-4-6 input_tokens: 80000 output_tokens: 30000 - model: claude-sonnet-4-6 input_tokens: 45000 output_tokens: 18000 matched_at: '2026-03-24T03:15:42+00:00' actor: id: 42 name: 'Aoife Ryan' email: aoife.ryan@example.com links: first: '/?page=1' last: '/?page=3' prev: null next: '/?page=2' meta: current_page: 1 last_page: 3 per_page: 25 total: 72 properties: data: type: array example: - id: 128 actor_id: 42 provider: claude source_email: aoife.ryan@example.com usage_date: '2026-03-24' sessions_count: 5 lines_added: 320 lines_removed: 85 commits_count: 4 pull_requests_count: 1 total_input_tokens: 125000 total_output_tokens: 48000 total_cache_read_tokens: 32000 total_cache_creation_tokens: 8000 total_cost_cents: 245 currency: USD tool_actions: edit_tool: accepted: 18 rejected: 2 bash_tool: accepted: 12 rejected: 0 model_breakdown: - model: claude-opus-4-6 input_tokens: 80000 output_tokens: 30000 - model: claude-sonnet-4-6 input_tokens: 45000 output_tokens: 18000 matched_at: '2026-03-24T03:15:42+00:00' actor: id: 42 name: 'Aoife Ryan' email: aoife.ryan@example.com items: type: object properties: id: type: integer example: 128 actor_id: type: integer example: 42 provider: type: string example: claude source_email: type: string example: aoife.ryan@example.com usage_date: type: string example: '2026-03-24' sessions_count: type: integer example: 5 lines_added: type: integer example: 320 lines_removed: type: integer example: 85 commits_count: type: integer example: 4 pull_requests_count: type: integer example: 1 total_input_tokens: type: integer example: 125000 total_output_tokens: type: integer example: 48000 total_cache_read_tokens: type: integer example: 32000 total_cache_creation_tokens: type: integer example: 8000 total_cost_cents: type: integer example: 245 currency: type: string example: USD tool_actions: type: object properties: edit_tool: type: object properties: accepted: type: integer example: 18 rejected: type: integer example: 2 bash_tool: type: object properties: accepted: type: integer example: 12 rejected: type: integer example: 0 model_breakdown: type: array example: - model: claude-opus-4-6 input_tokens: 80000 output_tokens: 30000 - model: claude-sonnet-4-6 input_tokens: 45000 output_tokens: 18000 items: type: object properties: model: type: string example: claude-opus-4-6 input_tokens: type: integer example: 80000 output_tokens: type: integer example: 30000 matched_at: type: string example: '2026-03-24T03:15:42+00:00' actor: type: object properties: id: type: integer example: 42 name: type: string example: 'Aoife Ryan' email: type: string example: aoife.ryan@example.com links: type: object properties: first: type: string example: '/?page=1' last: type: string example: '/?page=3' prev: type: string example: null nullable: true next: type: string example: '/?page=2' meta: type: object properties: current_page: type: integer example: 1 last_page: type: integer example: 3 per_page: type: integer example: 25 total: type: integer example: 72 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: to: - 'The to field must be a date after or equal to from.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: to: type: array example: - 'The to field must be a date after or equal to from.' items: type: string tags: - 'AI Tooling Usage' requestBody: required: false content: application/json: schema: type: object properties: actor_id: type: integer description: '' example: 11 provider: type: string description: 'Must not be greater than 50 characters.' example: pzdszigdqrrkthqp from: type: string description: 'Must be a valid date.' example: '2026-10-11T21:57:38' to: type: string description: 'Must be a valid date. Must be a date after or equal to from.' example: '2069-11-01' per_page: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 4 '/api/v1/ai-tooling/actors/{actor_id}/summary': get: summary: 'AI Tooling — Actor summary' operationId: aIToolingActorSummary description: "Returns aggregated AI tooling cost and usage totals for a single actor. Includes the\nlast full calendar month total, month-to-date total, and a per-provider breakdown.\n\nUseful for actor profile cards and dashboards.\n\nRequires `ai-tooling.manage` ability." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: actor_id: 42 has_data: true monthly_cost_cents: 850 mtd_cost_cents: 320 sessions: 15 commits: 8 prs: 2 lines_added: 450 lines_removed: 120 by_provider: claude: cost_cents: 850 mtd_cost_cents: 320 sessions: 15 properties: data: type: object properties: actor_id: type: integer example: 42 has_data: type: boolean example: true monthly_cost_cents: type: integer example: 850 mtd_cost_cents: type: integer example: 320 sessions: type: integer example: 15 commits: type: integer example: 8 prs: type: integer example: 2 lines_added: type: integer example: 450 lines_removed: type: integer example: 120 by_provider: type: object properties: claude: type: object properties: cost_cents: type: integer example: 850 mtd_cost_cents: type: integer example: 320 sessions: type: integer example: 15 tags: - 'AI Tooling Usage' parameters: - in: path name: actor_id description: 'The ID of the actor.' example: 11 required: true schema: type: integer - in: path name: actor description: 'The actor ID.' example: 42 required: true schema: type: integer '/api/v1/ai-tooling/actors/{actor_id}/series': get: summary: 'AI Tooling — Actor daily series' operationId: aIToolingActorDailySeries description: "Returns a daily time series of AI tooling usage for a single actor. Each data point\ncontains cost, token consumption, session count, and code-change metrics for one day.\nDesigned for rendering cost trend and activity charts on actor profile pages.\n\nDefaults to the last 6 months when `from`/`to` are omitted.\n\nRequires `ai-tooling.manage` ability." parameters: - in: query name: from description: 'date Start of date range (YYYY-MM-DD). Defaults to 6 months ago (start of month).' example: '2025-10-01' required: false schema: type: string description: 'date Start of date range (YYYY-MM-DD). Defaults to 6 months ago (start of month).' example: '2025-10-01' - in: query name: to description: 'date End of date range (YYYY-MM-DD). Must be on or after `from`. Defaults to today.' example: '2026-03-25' required: false schema: type: string description: 'date End of date range (YYYY-MM-DD). Must be on or after `from`. Defaults to today.' example: '2026-03-25' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - date: '2026-03-23' cost_cents: 120 sessions: 3 commits: 2 prs: 0 lines_added: 180 lines_removed: 45 input_tokens: 52000 output_tokens: 19000 - date: '2026-03-24' cost_cents: 245 sessions: 5 commits: 4 prs: 1 lines_added: 320 lines_removed: 85 input_tokens: 125000 output_tokens: 48000 properties: data: type: array example: - date: '2026-03-23' cost_cents: 120 sessions: 3 commits: 2 prs: 0 lines_added: 180 lines_removed: 45 input_tokens: 52000 output_tokens: 19000 - date: '2026-03-24' cost_cents: 245 sessions: 5 commits: 4 prs: 1 lines_added: 320 lines_removed: 85 input_tokens: 125000 output_tokens: 48000 description: 'Array of daily data points, ordered by date ascending.' items: type: object properties: date: type: string example: '2026-03-23' cost_cents: type: integer example: 120 sessions: type: integer example: 3 commits: type: integer example: 2 prs: type: integer example: 0 lines_added: type: integer example: 180 lines_removed: type: integer example: 45 input_tokens: type: integer example: 52000 output_tokens: type: integer example: 19000 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: to: - 'The to field must be a date after or equal to from.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: to: type: array example: - 'The to field must be a date after or equal to from.' items: type: string tags: - 'AI Tooling Usage' requestBody: required: false content: application/json: schema: type: object properties: from: type: string description: 'Must be a valid date.' example: '2026-10-11T21:57:38' to: type: string description: 'Must be a valid date. Must be a date after or equal to from.' example: '2069-11-01' parameters: - in: path name: actor_id description: 'The ID of the actor.' example: 11 required: true schema: type: integer - in: path name: actor description: 'The actor ID.' example: 42 required: true schema: type: integer '/api/v1/ai-tooling/teams/{team_id}/summary': get: summary: 'AI Tooling — Team summary' operationId: aIToolingTeamSummary description: "Returns aggregated AI tooling cost and usage totals for all actors placed in the team's\norg unit. Covers the last full calendar month and month-to-date period.\n\nIf the team has no associated org unit, zeroed metrics are returned.\n\nRequires `ai-tooling.manage` ability." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: team_id: 7 monthly_cost_cents: 2500 mtd_cost_cents: 1200 sessions: 45 commits: 22 prs: 6 lines_added: 1800 lines_removed: 400 properties: data: type: object properties: team_id: type: integer example: 7 monthly_cost_cents: type: integer example: 2500 mtd_cost_cents: type: integer example: 1200 sessions: type: integer example: 45 commits: type: integer example: 22 prs: type: integer example: 6 lines_added: type: integer example: 1800 lines_removed: type: integer example: 400 tags: - 'AI Tooling Usage' parameters: - in: path name: team_id description: 'The ID of the team.' example: 11 required: true schema: type: integer - in: path name: team description: 'The team ID.' example: 7 required: true schema: type: integer '/api/v1/ai-tooling/departments/{department_id}/summary': get: summary: 'AI Tooling — Department summary' operationId: aIToolingDepartmentSummary description: "Returns aggregated AI tooling cost and usage totals for a department. By default,\naggregates across the entire department subtree (all descendant org units). Pass\n`scope=direct` to limit to actors placed directly in the department's own org unit.\n\nIf the department has no associated org unit, zeroed metrics are returned.\n\nRequires `ai-tooling.manage` ability." parameters: - in: query name: scope description: 'Aggregation scope: "direct" (department org unit only) or "subtree" (all descendants). Defaults to "subtree".' example: subtree required: false schema: type: string description: 'Aggregation scope: "direct" (department org unit only) or "subtree" (all descendants). Defaults to "subtree".' example: subtree - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: department_id: 3 scope: subtree monthly_cost_cents: 8500 mtd_cost_cents: 3200 sessions: 120 commits: 55 prs: 12 lines_added: 5000 lines_removed: 1200 properties: data: type: object properties: department_id: type: integer example: 3 scope: type: string example: subtree monthly_cost_cents: type: integer example: 8500 mtd_cost_cents: type: integer example: 3200 sessions: type: integer example: 120 commits: type: integer example: 55 prs: type: integer example: 12 lines_added: type: integer example: 5000 lines_removed: type: integer example: 1200 tags: - 'AI Tooling Usage' parameters: - in: path name: department_id description: 'The ID of the department.' example: 11 required: true schema: type: integer - in: path name: department description: 'The department ID.' example: 3 required: true schema: type: integer /api/v1/actors: get: summary: 'Actors — List' operationId: actorsList description: "Returns actors with their primary team resolved from primary org-unit placements (primary=true) inside\nthe active scenario context.\n\nRequires `actors.read` ability." parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: scenario description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan required: false schema: type: string description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan - in: query name: 'filter[scenario_id]' description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' required: false schema: type: string description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' - in: query name: 'filter[scenario]' description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan required: false schema: type: string description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' - in: query name: 'filter[status]' description: 'Filter by status.' example: active required: false schema: type: string description: 'Filter by status.' example: active - in: query name: 'filter[department_id]' description: 'Filter by department id. Uses the department org unit mapping derived from `org_unit_actor`.' example: 5 required: false schema: type: integer description: 'Filter by department id. Uses the department org unit mapping derived from `org_unit_actor`.' example: 5 - in: query name: 'filter[org_unit_id]' description: 'Filter by org unit id (department or team subtree).' example: 10 required: false schema: type: integer description: 'Filter by org unit id (department or team subtree).' example: 10 - in: query name: 'fields[actors]' description: 'Sparse fields for actors.' example: 'id,first_name,last_name' required: false schema: type: string description: 'Sparse fields for actors.' example: 'id,first_name,last_name' - in: query name: include description: 'Comma-separated expansions. Allowed: manager,org_units,team,department. The `team`/`department` flags surface computed placement data.' example: 'manager,team' required: false schema: type: string description: 'Comma-separated expansions. Allowed: manager,org_units,team,department. The `team`/`department` flags surface computed placement data.' example: 'manager,team' - in: query name: as_of description: 'The date/datetime used for time-window checks. Defaults to now.' example: '2025-09-01' required: false schema: type: string description: 'The date/datetime used for time-window checks. Defaults to now.' example: '2025-09-01' - in: query name: scenario_id description: 'The scenario ID to scope results. Omit or pass `scenario_id=null` for baseline data.' example: 4 required: false schema: type: integer description: 'The scenario ID to scope results. Omit or pass `scenario_id=null` for baseline data.' example: 4 - in: query name: with_counts description: 'Comma-separated relationship counts. Allowed: reports.' example: reports required: false schema: type: string description: 'Comma-separated relationship counts. Allowed: reports.' example: reports - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 44 first_name: Aoife last_name: Ryan name: 'Aoife Ryan' email: aoife.ryan@example.com fte: 1.0 manager_id: 2 team: id: 15 name: 'Team CúChulainn' team_type: id: 3 name: 'Product Squad' slug: product-squad department: id: 3 name: Engineering properties: data: type: array example: - id: 44 first_name: Aoife last_name: Ryan name: 'Aoife Ryan' email: aoife.ryan@example.com fte: 1 manager_id: 2 team: id: 15 name: 'Team CúChulainn' team_type: id: 3 name: 'Product Squad' slug: product-squad department: id: 3 name: Engineering items: type: object properties: id: type: integer example: 44 first_name: type: string example: Aoife last_name: type: string example: Ryan name: type: string example: 'Aoife Ryan' email: type: string example: aoife.ryan@example.com fte: type: number example: 1.0 manager_id: type: integer example: 2 team: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' team_type: type: object properties: id: type: integer example: 3 name: type: string example: 'Product Squad' slug: type: string example: product-squad department: type: object properties: id: type: integer example: 3 name: type: string example: Engineering 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - name - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - name - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - Actors post: summary: 'Store a newly created actor.' operationId: storeANewlyCreatedActor description: "Requires `actors.create` ability.\nManager must belong to the same tenant and cannot reference the actor themselves." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 44 first_name: Aoife last_name: Ryan name: 'Aoife Ryan' email: aoife.ryan@example.com kind: human status: active contract_type: perm fte: 1.0 manager_id: 2 start_date: '2025-09-01' end_date: '2025-09-30' created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 44 first_name: type: string example: Aoife last_name: type: string example: Ryan name: type: string example: 'Aoife Ryan' email: type: string example: aoife.ryan@example.com kind: type: string example: human status: type: string example: active contract_type: type: string example: perm fte: type: number example: 1.0 manager_id: type: integer example: 2 start_date: type: string example: '2025-09-01' end_date: type: string example: '2025-09-30' created_at: type: string example: '2025-01-10T09:00:00Z' 429: description: '' content: application/json: schema: type: object example: code: limit.actors message: 'Actor plan limit reached (250/250). Upgrade your plan to add more people.' limit: 250 current: 250 properties: code: type: string example: limit.actors message: type: string example: 'Actor plan limit reached (250/250). Upgrade your plan to add more people.' limit: type: integer example: 250 current: type: integer example: 250 tags: - Actors requestBody: required: true content: application/json: schema: type: object properties: first_name: type: string description: "The actor's first name." example: Aoife last_name: type: string description: "The actor's last name." example: Ryan email: type: string description: 'Optional contact email for the actor. Human actors can be created without email.' example: aoife.ryan@example.com nullable: true external_uid: type: string description: 'Durable Orgonaut actor identifier. If omitted, API generates a ULID.' example: 01J9ZQ4J6W2Q0WJ9V6YV5NPQ4W nullable: true source_hris: type: string description: '' example: null source_hris_id: type: string description: '' example: null kind: type: string description: 'The actor kind (human, agent, robot). Defaults to human when omitted.' example: human status: type: string description: 'Employment status.' example: active enum: - active - inactive nullable: true fte: type: number description: 'Contractual FTE (0.01–3.00). 1.00 = full-time, 0.50 = half-time.' example: 1.0 position_id: type: integer description: 'ID of an existing position to assign. Must exist in positions table for this tenant.' example: 42 nullable: true create_placement: type: boolean description: 'Whether to automatically create an org unit placement.' example: true nullable: true location: type: string description: "The actor's location." example: 'Dublin, IE' nullable: true start_date: type: string description: 'The start date. Format: YYYY-MM-DD.' example: '2025-09-01' nullable: true end_date: type: string description: 'The end date. Format: YYYY-MM-DD.' example: '2025-09-30' nullable: true manager_id: type: integer description: 'nullable The ID of the manager within this tenant.' example: 2 nullable: true agent_profile: type: object description: 'Agent profile payload (required when kind=agent).' example: provider: openai properties: provider: type: string description: 'Agent provider (openai, anthropic, azure_openai, custom).' example: openai nullable: true orchestrator: type: string description: 'Optional orchestrator identifier.' example: langgraph nullable: true meta: type: object description: 'Optional structured metadata payload.' example: capabilities: - search properties: { } nullable: true endpoint_base_url: type: string description: 'Optional base URL for provider API.' example: 'https://api.openai.com' model_default: type: string description: 'Default model identifier.' example: gpt-4.1 nullable: true required: - first_name - last_name - fte '/api/v1/actors/{id}': get: summary: 'Actors — Show' operationId: actorsShow description: "Returns an actor with their primary team resolved from primary org-unit placement (primary=true) within\nthe active scenario context.\n\nRequires a Sanctum token with the `actors.read` ability and an `X-Tenant` header." parameters: - in: query name: include description: 'Comma-separated expansions. Allowed: manager,org_units,team,department. The `team`/`department` flags surface computed placement data.' example: 'manager,team' required: false schema: type: string description: 'Comma-separated expansions. Allowed: manager,org_units,team,department. The `team`/`department` flags surface computed placement data.' example: 'manager,team' - in: query name: as_of description: 'The date/datetime used for time-window checks. Defaults to now.' example: '2025-09-01' required: false schema: type: string description: 'The date/datetime used for time-window checks. Defaults to now.' example: '2025-09-01' - in: query name: scenario_id description: 'The scenario context to resolve against. Defaults to current (baseline if none).' example: null required: false schema: type: string description: 'The scenario context to resolve against. Defaults to current (baseline if none).' example: null - in: query name: with_counts description: 'Comma-separated relationship counts. Allowed: reports.' example: reports required: false schema: type: string description: 'Comma-separated relationship counts. Allowed: reports.' example: reports - in: header name: X-Tenant description: '' example: 'string required Tenant slug' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 44 first_name: Aoife last_name: Ryan name: 'Aoife Ryan' email: aoife.ryan@example.com kind: human status: active contract_type: perm fte: 1.0 manager_id: 2 manager: id: 2 first_name: Brian last_name: "O'Neil" name: "Brian O'Neil" reports_count: 3 team: id: 15 name: 'Team CúChulainn' team_type: id: 3 name: 'Product Squad' slug: product-squad department: id: 3 name: Engineering created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 44 first_name: type: string example: Aoife last_name: type: string example: Ryan name: type: string example: 'Aoife Ryan' email: type: string example: aoife.ryan@example.com kind: type: string example: human status: type: string example: active contract_type: type: string example: perm fte: type: number example: 1.0 manager_id: type: integer example: 2 manager: type: object properties: id: type: integer example: 2 first_name: type: string example: Brian last_name: type: string example: "O'Neil" name: type: string example: "Brian O'Neil" reports_count: type: integer example: 3 team: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' team_type: type: object properties: id: type: integer example: 3 name: type: string example: 'Product Squad' slug: type: string example: product-squad department: type: object properties: id: type: integer example: 3 name: type: string example: Engineering created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Actors put: summary: 'Update the specified actor.' operationId: updateTheSpecifiedActor description: "Requires `actors.update` ability and an `X-Tenant` header.\nManager must belong to the same tenant and cannot reference the actor themselves." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 44 first_name: Aoife last_name: Ryan name: 'Aoife Ryan' email: aoife.ryan@example.com kind: human status: active fte: 1.0 manager_id: 2 team: id: 15 name: 'Team CúChulainn' department: id: 3 name: Engineering created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 44 first_name: type: string example: Aoife last_name: type: string example: Ryan name: type: string example: 'Aoife Ryan' email: type: string example: aoife.ryan@example.com kind: type: string example: human status: type: string example: active fte: type: number example: 1.0 manager_id: type: integer example: 2 team: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' department: type: object properties: id: type: integer example: 3 name: type: string example: Engineering created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Actors requestBody: required: false content: application/json: schema: type: object properties: first_name: type: string description: "The actor's first name. Required for PUT; optional for PATCH (omitted fields keep their current value)." example: Aoife last_name: type: string description: "The actor's last name. Required for PUT; optional for PATCH." example: Ryan email: type: string description: 'Optional contact email for the actor. Human actors can remain blank.' example: aoife.ryan@example.com nullable: true external_uid: type: string description: 'Durable Orgonaut actor identifier.' example: 01J9ZQ4J6W2Q0WJ9V6YV5NPQ4W nullable: true source_hris: type: string description: '' example: null source_hris_id: type: string description: '' example: null kind: type: string description: 'The actor kind (immutable once set; updates rejected).' example: human status: type: string description: 'Employment status.' example: active enum: - active - inactive nullable: true fte: type: number description: 'Contractual FTE (0.01–3.00). 1.00 = full-time, 0.50 = half-time. Required for PUT; optional for PATCH.' example: 1.0 position_id: type: integer description: 'ID of an existing position to assign. Must exist in positions table for this tenant.' example: 42 nullable: true create_placement: type: boolean description: 'Whether to automatically create an org unit placement.' example: true nullable: true location: type: string description: "The actor's location." example: 'Dublin, IE' nullable: true start_date: type: string description: 'The start date. Format: YYYY-MM-DD.' example: '2025-09-01' nullable: true end_date: type: string description: 'The end date. Format: YYYY-MM-DD.' example: '2025-09-30' nullable: true manager_id: type: integer description: 'nullable The ID of the manager within this tenant.' example: 2 nullable: true agent_profile: type: object description: 'Agent profile payload.' example: provider: openai properties: provider: type: string description: 'Agent provider (openai, anthropic, azure_openai, custom).' example: openai enum: - openai - anthropic - azure_openai - custom nullable: true orchestrator: type: string description: 'Optional orchestrator identifier. Must not be greater than 255 characters.' example: langgraph nullable: true meta: type: object description: 'Optional structured metadata payload.' example: capabilities: - search properties: { } nullable: true nullable: true delete: summary: 'Remove the specified actor.' operationId: removeTheSpecifiedActor description: 'Requires `actors.delete` ability and an `X-Tenant` header.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Actors parameters: - in: path name: id description: 'The ID of the actor.' example: 44 required: true schema: type: integer '/api/v1/actors/{actor_id}/compensations': get: summary: 'Actor compensations — List' operationId: actorCompensationsList description: "Lists compensation slices for the given actor. When a scenario context is supplied,\nscenario slices are returned alongside baseline fallbacks, ordered with scenario data first.\n\nRequires `actors.read` and `costs.read` abilities." parameters: - in: query name: scenario_id description: 'Scenario id to scope results; omit or pass null for baseline only.' example: 4 required: false schema: type: integer description: 'Scenario id to scope results; omit or pass null for baseline only.' example: 4 - in: query name: as_of description: 'Date used to flag current slices.' example: '2025-03-01' required: false schema: type: string description: 'Date used to flag current slices.' example: '2025-03-01' - in: query name: include description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario required: false schema: type: string description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario - in: query name: 'fields[actor_compensations]' description: 'Sparse fieldset.' example: 'id,currency,base_salary_cents' required: false schema: type: string description: 'Sparse fieldset.' example: 'id,currency,base_salary_cents' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 currency: USD base_salary_cents: 12000000 properties: data: type: array example: - id: 1 currency: USD base_salary_cents: 12000000 items: type: object properties: id: type: integer example: 1 currency: type: string example: USD base_salary_cents: type: integer example: 12000000 tags: - Actors post: summary: 'Actor compensations — Create' operationId: actorCompensationsCreate description: "Stores a new compensation slice for the actor.\n\nRequires `actors.update` ability." parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario required: false schema: type: string description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 9 currency: EUR daily_rate_cents: 55000 properties: data: type: object properties: id: type: integer example: 9 currency: type: string example: EUR daily_rate_cents: type: integer example: 55000 tags: - Actors requestBody: required: true content: application/json: schema: type: object properties: scenario_id: type: integer description: 'Scenario context; omit or null for baseline actuals. The id of an existing record in the scenarios table.' example: 3 nullable: true employment_type: type: string description: 'Employment type matching actor records.' example: Permanent enum: - Permanent - Contractor currency: type: string description: 'Three-letter currency code defined in Settings → Currencies. Must contain only letters. The code of an existing record in the currencies table. Must be 3 characters.' example: USD base_salary_cents: type: integer description: 'Annual base salary in cents (for FTE). This field is required when daily_rate_cents is not present. Must be at least 0.' example: 12000000 nullable: true daily_rate_cents: type: integer description: 'Daily contractor rate in cents (for contractors). This field is required when base_salary_cents is not present. Must be at least 0.' example: 45000 nullable: true valid_from: type: string description: 'Start date for the compensation window (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-01-01' valid_to: type: string description: 'Optional end date for the compensation window (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to valid_from.' example: '2025-12-31' nullable: true meta: type: object description: 'Arbitrary metadata to persist alongside the record.' example: note: 'Includes sign-on bonus' properties: { } nullable: true required: - employment_type - currency - valid_from parameters: - in: path name: actor_id description: 'The ID of the actor.' example: 11 required: true schema: type: integer - in: path name: actor description: 'The actor identifier.' example: 11 required: true schema: type: integer '/api/v1/actors/{actor_id}/compensations/{id}': patch: summary: 'Actor compensations — Update' operationId: actorCompensationsUpdate description: "Updates an existing compensation slice for the actor.\n\nRequires `actors.update` ability." parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario required: false schema: type: string description: 'Comma-separated relationships. Allowed: scenario,actor.' example: scenario - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 9 currency: USD base_salary_cents: 9900000 properties: data: type: object properties: id: type: integer example: 9 currency: type: string example: USD base_salary_cents: type: integer example: 9900000 tags: - Actors requestBody: required: true content: application/json: schema: type: object properties: scenario_id: type: integer description: 'Scenario context; omit or null for baseline actuals. The id of an existing record in the scenarios table.' example: 3 nullable: true employment_type: type: string description: 'Employment type matching actor records.' example: Contractor enum: - Permanent - Contractor currency: type: string description: 'Three-letter currency code defined in Settings → Currencies. Must contain only letters. The code of an existing record in the currencies table. Must be 3 characters.' example: GBP base_salary_cents: type: integer description: 'Annual base salary in cents (for FTE). This field is required when daily_rate_cents is not present. Must be at least 0.' example: 9800000 nullable: true daily_rate_cents: type: integer description: 'Daily contractor rate in cents (for contractors). This field is required when base_salary_cents is not present. Must be at least 0.' example: 60000 nullable: true valid_from: type: string description: 'Start date for the compensation window (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-02-01' valid_to: type: string description: 'Optional end date for the compensation window (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to valid_from.' example: '2025-09-30' nullable: true meta: type: object description: 'Arbitrary metadata to persist alongside the record.' example: note: 'Updated after promotion' properties: { } nullable: true required: - employment_type - currency - valid_from delete: summary: 'Actor compensations — Delete' operationId: actorCompensationsDelete description: "Soft deletes a compensation slice.\n\nRequires `actors.delete` ability." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Actors parameters: - in: path name: actor_id description: 'The ID of the actor.' example: 11 required: true schema: type: integer - in: path name: id description: 'The ID of the compensation.' example: 11 required: true schema: type: integer - in: path name: actor description: 'The actor identifier.' example: 11 required: true schema: type: integer - in: path name: compensation description: 'The compensation identifier.' example: 11 required: true schema: type: integer /api/v1/aggregates/recalculate: post: summary: 'Queue recalculation of cached aggregates.' operationId: queueRecalculationOfCachedAggregates description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: status: queued properties: status: type: string example: queued tags: - Aggregates /api/v1/astro/capabilities: get: summary: 'List Astro capabilities for the current tenant, user, and context.' operationId: listAstroCapabilitiesForTheCurrentTenantUserAndContext description: '' parameters: - in: query name: category description: 'Filter by category key.' example: work_with_scenarios required: false schema: type: string description: 'Filter by category key.' example: work_with_scenarios - in: query name: kind description: 'Filter by capability kind.' example: read required: false schema: type: string description: 'Filter by capability kind.' example: read - in: query name: availability description: 'Filter by computed availability.' example: available required: false schema: type: string description: 'Filter by computed availability.' example: available - in: query name: include_unavailable description: 'Include capabilities that are unavailable in the current context.' example: false required: false schema: type: boolean description: 'Include capabilities that are unavailable in the current context.' example: false - in: query name: view_mode description: 'Override the current Astro view mode (live, scenario, snapshot).' example: scenario required: false schema: type: string description: 'Override the current Astro view mode (live, scenario, snapshot).' example: scenario - in: query name: scenario_id description: 'Optional scenario id used for scenario- or snapshot-scoped discovery.' example: 7 required: false schema: type: integer description: 'Optional scenario id used for scenario- or snapshot-scoped discovery.' example: 7 - in: query name: focused_entity_type description: 'Optional focused entity type (actor, ai_solution, business_capability, department, org_unit, scenario, team, transformation_case, work_activity).' example: team required: false schema: type: string description: 'Optional focused entity type (actor, ai_solution, business_capability, department, org_unit, scenario, team, transformation_case, work_activity).' example: team - in: query name: focused_entity_id description: 'Optional focused entity id.' example: 12 required: false schema: type: integer description: 'Optional focused entity id.' example: 12 - in: query name: as_of description: 'date Optional as-of date for scoped discovery.' example: '2026-03-01' required: false schema: type: string description: 'date Optional as-of date for scoped discovery.' example: '2026-03-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - key: explore_organisation title: 'Explore organisation' description: 'Browse the current organisation structure, departments, teams, and root summaries.' capabilities: - key: explore.organisation title: 'Explore organisation structure' kind: read availability_status: available meta: context: view_mode: live scope_key: baseline counts: available: 5 limited: 2 unavailable: 0 coming_soon: 3 properties: data: type: array example: - key: explore_organisation title: 'Explore organisation' description: 'Browse the current organisation structure, departments, teams, and root summaries.' capabilities: - key: explore.organisation title: 'Explore organisation structure' kind: read availability_status: available items: type: object properties: key: type: string example: explore_organisation title: type: string example: 'Explore organisation' description: type: string example: 'Browse the current organisation structure, departments, teams, and root summaries.' capabilities: type: array example: - key: explore.organisation title: 'Explore organisation structure' kind: read availability_status: available items: type: object properties: key: type: string example: explore.organisation title: type: string example: 'Explore organisation structure' kind: type: string example: read availability_status: type: string example: available meta: type: object properties: context: type: object properties: view_mode: type: string example: live scope_key: type: string example: baseline counts: type: object properties: available: type: integer example: 5 limited: type: integer example: 2 unavailable: type: integer example: 0 coming_soon: type: integer example: 3 tags: - Astro '/api/v1/astro/capabilities/{key}': get: summary: 'Show one Astro capability for the current tenant, user, and context.' operationId: showOneAstroCapabilityForTheCurrentTenantUserAndContext description: '' parameters: - in: query name: view_mode description: 'Override the current Astro view mode (live, scenario, snapshot).' example: scenario required: false schema: type: string description: 'Override the current Astro view mode (live, scenario, snapshot).' example: scenario - in: query name: scenario_id description: 'Optional scenario id used for scenario- or snapshot-scoped discovery.' example: 7 required: false schema: type: integer description: 'Optional scenario id used for scenario- or snapshot-scoped discovery.' example: 7 - in: query name: focused_entity_type description: 'Optional focused entity type (actor, ai_solution, business_capability, department, org_unit, scenario, team, transformation_case, work_activity).' example: team required: false schema: type: string description: 'Optional focused entity type (actor, ai_solution, business_capability, department, org_unit, scenario, team, transformation_case, work_activity).' example: team - in: query name: focused_entity_id description: 'Optional focused entity id.' example: 12 required: false schema: type: integer description: 'Optional focused entity id.' example: 12 - in: query name: as_of description: 'date Optional as-of date for scoped discovery.' example: '2026-03-01' required: false schema: type: string description: 'date Optional as-of date for scoped discovery.' example: '2026-03-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: key: explore.organisation title: 'Explore organisation structure' kind: read availability_status: available examples: - prompt: 'List the departments in the current scope.' properties: data: type: object properties: key: type: string example: explore.organisation title: type: string example: 'Explore organisation structure' kind: type: string example: read availability_status: type: string example: available examples: type: array example: - prompt: 'List the departments in the current scope.' items: type: object properties: prompt: type: string example: 'List the departments in the current scope.' 404: description: '' content: application/json: schema: type: object example: message: 'Capability not found.' properties: message: type: string example: 'Capability not found.' tags: - Astro parameters: - in: path name: key description: 'Capability key.' example: explore.organisation required: true schema: type: string /api/v1/tokens: get: summary: "List the authenticated user's tokens." operationId: listTheAuthenticatedUsersTokens description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: array items: type: object properties: id: type: integer example: 12 name: type: string example: 'CI/CD Bot' abilities: type: array example: - actors.read - scenarios.update items: type: string last_used_at: type: string example: '2025-08-01T09:30:12Z' created_at: type: string example: '2025-07-15T12:00:00Z' updated_at: type: string example: '2025-08-01T09:30:12Z' tenant_id: type: integer example: 3 example: - id: 12 name: 'CI/CD Bot' abilities: - actors.read - scenarios.update last_used_at: '2025-08-01T09:30:12Z' created_at: '2025-07-15T12:00:00Z' updated_at: '2025-08-01T09:30:12Z' tenant_id: 3 tags: - Auth/Tokens post: summary: 'Issue a new actoral access token.' operationId: issueANewActoralAccessToken description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug for token scope. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: token: 1|abcdef123456 properties: token: type: string example: 1|abcdef123456 403: description: '' content: application/json: schema: type: object example: code: auth.forbidden message: Forbidden. properties: code: type: string example: auth.forbidden message: type: string example: Forbidden. 429: description: '' content: application/json: schema: type: object example: code: limit.api_tokens message: 'API token plan limit reached (1/1). Revoke an existing token or upgrade your plan.' limit: 1 current: 1 properties: code: type: string example: limit.api_tokens message: type: string example: 'API token plan limit reached (1/1). Revoke an existing token or upgrade your plan.' limit: type: integer example: 1 current: type: integer example: 1 tags: - Auth/Tokens requestBody: required: false content: application/json: schema: type: object properties: email: type: string description: 'The email address of the user to authenticate.' example: loma53@example.com password: type: string description: 'The password of the user to authenticate.' example: secret-password-123 name: type: string description: 'The human-readable label for this token (shown in your account).' example: 'CI/CD Bot' abilities: type: array description: 'Optional abilities for the token.' example: - actors.read - scenarios.update items: type: string security: [] '/api/v1/tokens/{id}': delete: summary: 'Revoke a token.' operationId: revokeAToken description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Auth/Tokens patch: summary: 'Rename a token.' operationId: renameAToken description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Auth/Tokens requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'New human‑friendly name for the token. Must not be greater than 255 characters.' example: 'CI bot token' required: - name parameters: - in: path name: id description: 'The ID of the token.' example: ab required: true schema: type: string - in: path name: token description: 'The token ID.' example: 11 required: true schema: type: integer '/api/v1/tokens/{token}/rotate': post: summary: 'Rotate a token.' operationId: rotateAToken description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: token: 1|abcdef123456 properties: token: type: string example: 1|abcdef123456 tags: - Auth/Tokens parameters: - in: path name: token description: 'The token ID.' example: 11 required: true schema: type: integer /api/v1/batch: post: summary: 'Run several actor, placement, compensation and position writes in one request.' operationId: runSeveralActorPlacementCompensationAndPositionWritesInOneRequest description: "Batchable endpoints: create/update/delete actors\n(`/api/v1/actors`, `/api/v1/actors/{id}`), create placements\n(`/api/v1/actors/{id}/placements`), update/delete placements\n(`/api/v1/placements/{id}`), create/update/delete compensations\n(`/api/v1/actors/{id}/compensations[/{id}]`), and create/update/delete\npositions (`/api/v1/positions[/{id}]`; updates must resend `title`, `fte` and\n`valid_from`). Each operation runs through\nits normal endpoint, so it needs the same token abilities, follows the\nsame validation and scenario rules, and is audited the same way. Pass the\nusual `scenario` or `scenario_id` query parameter on the batch request to\nwrite inside a scenario. A batch counts as one request toward rate limits\nand quotas.\n\nLater operations can use results of earlier ones with `{ref:index.dot.path}`,\nfor example `{ref:0.data.id}` for the id of an actor created by operation 0.\nThe path starts inside that operation's response body, so `{ref:0.data.id}`\nreads `results[0].body.data.id`; do not include `body` in the path.\n\nThe response status is 200 when every operation succeeded, 422 when an\natomic batch was rolled back, and 207 when a non-atomic batch partly failed." parameters: - in: query name: scenario description: 'Scenario slug to write inside.' example: fy26-restructure required: false schema: type: string description: 'Scenario slug to write inside.' example: fy26-restructure - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: atomic: true succeeded: 3 failed: 0 rolled_back: false results: - index: 0 method: POST path: /api/v1/actors status: 201 body: data: id: 501 first_name: Agent last_name: Triage - index: 1 method: POST path: /api/v1/actors/501/placements status: 201 body: data: id: 9001 org_unit_id: 12 allocation_pct: 100 - index: 2 method: POST path: /api/v1/positions status: 201 body: data: id: 730 title: 'AI Operations Lead' org_unit_team_id: 12 fte: 1 properties: atomic: type: boolean example: true succeeded: type: integer example: 3 failed: type: integer example: 0 rolled_back: type: boolean example: false results: type: array example: - index: 0 method: POST path: /api/v1/actors status: 201 body: data: id: 501 first_name: Agent last_name: Triage - index: 1 method: POST path: /api/v1/actors/501/placements status: 201 body: data: id: 9001 org_unit_id: 12 allocation_pct: 100 - index: 2 method: POST path: /api/v1/positions status: 201 body: data: id: 730 title: 'AI Operations Lead' org_unit_team_id: 12 fte: 1 items: type: object properties: index: type: integer example: 0 method: type: string example: POST path: type: string example: /api/v1/actors status: type: integer example: 201 body: type: object properties: data: type: object properties: id: type: integer example: 501 first_name: type: string example: Agent last_name: type: string example: Triage 422: description: '' content: application/json: schema: type: object example: atomic: true succeeded: 0 failed: 1 rolled_back: true results: - index: 0 method: PATCH path: /api/v1/actors/44 status: 422 body: message: 'The fte field must not be greater than 3.' errors: fte: - 'The fte field must not be greater than 3.' properties: atomic: type: boolean example: true succeeded: type: integer example: 0 failed: type: integer example: 1 rolled_back: type: boolean example: true results: type: array example: - index: 0 method: PATCH path: /api/v1/actors/44 status: 422 body: message: 'The fte field must not be greater than 3.' errors: fte: - 'The fte field must not be greater than 3.' items: type: object properties: index: type: integer example: 0 method: type: string example: PATCH path: type: string example: /api/v1/actors/44 status: type: integer example: 422 body: type: object properties: message: type: string example: 'The fte field must not be greater than 3.' errors: type: object properties: fte: type: array example: - 'The fte field must not be greater than 3.' items: type: string tags: - Batch requestBody: required: true content: application/json: schema: type: object properties: atomic: type: boolean description: 'When true (default), all operations succeed or none are kept: the first failure rolls back the batch.' example: true operations: type: array description: 'Up to 200 write operations, run in order. Supported targets: actors, placements, compensations and positions. Position updates (PUT or PATCH /api/v1/positions/{id}) must include title, fte and valid_from alongside the fields that change. Must have at least 1 items. Must not have more than 200 items.' example: - method: POST path: /api/v1/actors body: first_name: Triage last_name: Agent email: triage.agent@example.com fte: 1 - method: POST path: '/api/v1/actors/{ref:0.data.id}/placements' body: org_unit_id: 12 allocation_pct: 100 primary: true - method: POST path: /api/v1/positions body: title: 'AI Operations Lead' org_unit_team_id: 12 fte: 1 valid_from: '2027-01-01' - method: DELETE path: /api/v1/placements/9001 items: type: object properties: method: type: string description: '' example: POST enum: - POST - PUT - PATCH - DELETE - post - put - patch - delete path: type: string description: 'Must start with one of /api/v1/ Must not be greater than 500 characters.' example: ipzdszigdqrrkthq body: type: object description: '' example: null properties: { } nullable: true required: - method - path required: - operations /api/v1/capabilities: get: summary: 'List capabilities for Live or the `scenario` query context.' operationId: listCapabilitiesForLiveOrThescenarioQueryContext description: '' parameters: - in: query name: scenario description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model required: false schema: type: string description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model - in: query name: as_of description: 'date Effective date.' example: '2026-09-30' required: false schema: type: string description: 'date Effective date.' example: '2026-09-30' - in: query name: page description: 'Page number.' example: 1 required: false schema: type: integer description: 'Page number.' example: 1 - in: query name: per_page description: 'Items per page, maximum 100.' example: 25 required: false schema: type: integer description: 'Items per page, maximum 100.' example: 25 - in: query name: 'fields[capabilities]' description: 'Sparse top-level fields.' example: 'definition,state,delta,warnings' required: false schema: type: string description: 'Sparse top-level fields.' example: 'definition,state,delta,warnings' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'definition,state,delta' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'definition,state,delta' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' '/api/v1/capabilities/{businessCapability_slug}': get: summary: 'Show one capability with state, source delta, Work, Solutions, and evidence.' operationId: showOneCapabilityWithStateSourceDeltaWorkSolutionsAndEvidence description: '' parameters: - in: query name: scenario description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model required: false schema: type: string description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model - in: query name: as_of description: 'date Effective date.' example: '2026-09-30' required: false schema: type: string description: 'date Effective date.' example: '2026-09-30' - in: query name: 'fields[capabilities]' description: 'Sparse top-level fields.' example: 'definition,state,delta,warnings' required: false schema: type: string description: 'Sparse top-level fields.' example: 'definition,state,delta,warnings' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'definition,state,delta' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'definition,state,delta' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' parameters: - in: path name: businessCapability_slug description: 'The slug of the businessCapability.' example: ab required: true schema: type: string - in: path name: businessCapability description: 'Capability slug.' example: resolve-customer-exceptions required: true schema: type: string /api/v1/ai-solutions: get: summary: 'List AI solutions and deployments in Live or the `scenario` query context.' operationId: listAISolutionsAndDeploymentsInLiveOrThescenarioQueryContext description: 'Cost defaults and forecasts require `transformation-cases.costs.read`.' parameters: - in: query name: scenario description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model required: false schema: type: string description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model - in: query name: page description: 'Page number.' example: 1 required: false schema: type: integer description: 'Page number.' example: 1 - in: query name: per_page description: 'Items per page, maximum 100.' example: 25 required: false schema: type: integer description: 'Items per page, maximum 100.' example: 25 - in: query name: 'fields[solutions]' description: 'Sparse top-level fields.' example: 'id,name,lifecycle_status,deployments' required: false schema: type: string description: 'Sparse top-level fields.' example: 'id,name,lifecycle_status,deployments' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'name,versions,deployments' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'name,versions,deployments' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' '/api/v1/ai-solutions/{aiSolution_slug}': get: summary: 'Show one AI solution with governed versions and contextual deployments.' operationId: showOneAISolutionWithGovernedVersionsAndContextualDeployments description: '' parameters: - in: query name: scenario description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model required: false schema: type: string description: 'Scenario slug; omit for Live.' example: ai-enabled-operating-model - in: query name: 'fields[solutions]' description: 'Sparse top-level fields.' example: 'id,name,versions,deployments' required: false schema: type: string description: 'Sparse top-level fields.' example: 'id,name,versions,deployments' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'name,versions,deployments' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'name,versions,deployments' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' parameters: - in: path name: aiSolution_slug description: 'The slug of the aiSolution.' example: 11 required: true schema: type: integer - in: path name: aiSolution description: 'AI solution slug.' example: customer-exception-copilot required: true schema: type: string /api/v1/transformation-cases: get: summary: 'List tenant-level Transformation Cases.' operationId: listTenantLevelTransformationCases description: 'Cost and workforce facts are independently permission-filtered.' parameters: - in: query name: page description: 'Page number.' example: 1 required: false schema: type: integer description: 'Page number.' example: 1 - in: query name: per_page description: 'Items per page, maximum 100.' example: 25 required: false schema: type: integer description: 'Items per page, maximum 100.' example: 25 - in: query name: 'fields[transformation-cases]' description: 'Sparse top-level fields.' example: 'id,case_key,name,status,readiness,economics' required: false schema: type: string description: 'Sparse top-level fields.' example: 'id,case_key,name,status,readiness,economics' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'name,readiness,economics' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'name,readiness,economics' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' '/api/v1/transformation-cases/{transformationCase_case_key}': get: summary: 'Show one case with readiness, readiness progress, scope, and a client-safe economics summary.' operationId: showOneCaseWithReadinessReadinessProgressScopeAndAClientSafeEconomicsSummary description: '' parameters: - in: query name: 'fields[transformation-cases]' description: 'Sparse top-level fields.' example: 'id,case_key,name,status,readiness,economics' required: false schema: type: string description: 'Sparse top-level fields.' example: 'id,case_key,name,status,readiness,economics' - in: query name: include description: 'Comma-separated top-level fields to include.' example: 'name,readiness,economics' required: false schema: type: string description: 'Comma-separated top-level fields to include.' example: 'name,readiness,economics' - in: header name: X-Tenant description: '' example: 'string required Tenant slug. Example: volkswagen' schema: type: string responses: 401: description: '' content: application/json: schema: type: object example: code: auth.unauthenticated message: Unauthenticated. properties: code: type: string example: auth.unauthenticated message: type: string example: Unauthenticated. tags: - 'Capability Studio' parameters: - in: path name: transformationCase_case_key description: '' example: ab required: true schema: type: string - in: path name: transformationCase description: 'Transformation Case key.' example: TC-2026-0001 required: true schema: type: string /api/v1/actor-cost-events: post: summary: 'Agent Cost Events — Create' operationId: agentCostEventsCreate description: "Ingest a machine cost event with idempotency protection. Duplicate idempotency keys\nreturn the existing event instead of creating a new row." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 actor_id: 44 actor_agent_profile_id: 9 org_unit_id: 12 scenario_id: null occurred_at: '2025-09-01T12:00:00Z' input_tokens: 1200000 cached_input_tokens: 250000 output_tokens: 640000 total_tokens: 2090000 amount: 18.75 currency: USD provider: openai model: gpt-4.1 meta: refund: false created_at: '2025-09-02T10:00:00Z' properties: data: type: object properties: id: type: integer example: 1 actor_id: type: integer example: 44 actor_agent_profile_id: type: integer example: 9 org_unit_id: type: integer example: 12 scenario_id: type: string example: null nullable: true occurred_at: type: string example: '2025-09-01T12:00:00Z' input_tokens: type: integer example: 1200000 cached_input_tokens: type: integer example: 250000 output_tokens: type: integer example: 640000 total_tokens: type: integer example: 2090000 amount: type: number example: 18.75 currency: type: string example: USD provider: type: string example: openai model: type: string example: gpt-4.1 meta: type: object properties: refund: type: boolean example: false created_at: type: string example: '2025-09-02T10:00:00Z' 201: description: '' content: application/json: schema: type: object example: data: id: 1 actor_id: 44 actor_agent_profile_id: 9 org_unit_id: 12 scenario_id: null occurred_at: '2025-09-01T12:00:00Z' input_tokens: 1200000 cached_input_tokens: 250000 output_tokens: 640000 total_tokens: 2090000 amount: 18.75 currency: USD provider: openai model: gpt-4.1 meta: refund: false created_at: '2025-09-02T10:00:00Z' properties: data: type: object properties: id: type: integer example: 1 actor_id: type: integer example: 44 actor_agent_profile_id: type: integer example: 9 org_unit_id: type: integer example: 12 scenario_id: type: string example: null nullable: true occurred_at: type: string example: '2025-09-01T12:00:00Z' input_tokens: type: integer example: 1200000 cached_input_tokens: type: integer example: 250000 output_tokens: type: integer example: 640000 total_tokens: type: integer example: 2090000 amount: type: number example: 18.75 currency: type: string example: USD provider: type: string example: openai model: type: string example: gpt-4.1 meta: type: object properties: refund: type: boolean example: false created_at: type: string example: '2025-09-02T10:00:00Z' tags: - Costs requestBody: required: true content: application/json: schema: type: object properties: occurred_at: type: string description: 'Event timestamp (ISO-8601). Must be a valid date.' example: '2025-09-01T12:00:00Z' amount: type: number description: 'Cost amount in the event currency. Optional when token metrics are provided and central model pricing is configured.' example: 42.55 nullable: true currency: type: string description: 'Currency code (ISO-4217). Required when amount is provided. Must be 3 characters.' example: USD nullable: true idempotency_key: type: string description: 'Unique idempotency key for this event. Must not be greater than 255 characters.' example: evt_01HX... actor_id: type: integer description: 'Agent actor ID (required). Provider is resolved from the actor profile. The id of an existing record in the actors table.' example: 44 org_unit_id: type: integer description: 'Org unit snapshot attribution (optional). The id of an existing record in the org_units table.' example: 12 nullable: true scenario_id: type: integer description: 'Optional scenario check; must match the actor profile scenario when provided. The id of an existing record in the scenarios table.' example: 3 nullable: true model: type: string description: 'Model identifier. Optional when the actor profile has a default model. Must not be greater than 255 characters.' example: gpt-4.1 nullable: true input_tokens: type: integer description: 'Input token count for the event. Must be at least 0.' example: 1200000 nullable: true cached_input_tokens: type: integer description: 'Cached input token count for the event. Must be at least 0.' example: 250000 nullable: true output_tokens: type: integer description: 'Output token count for the event. Must be at least 0.' example: 640000 nullable: true meta: type: object description: 'Arbitrary structured metadata for the event.' example: refund: false properties: { } nullable: true required: - occurred_at - idempotency_key - actor_id /api/v1/settings/ai/costs: get: summary: 'AI Model Costs — List' operationId: aIModelCostsList description: 'List tenant-global model pricing rows.' parameters: - in: query name: sort description: 'Comma-separated sort list. Allowed: id,provider,model,effective_from,effective_to,created_at. Prefix with `-` for descending order.' example: '-effective_from,provider' required: false schema: type: string description: 'Comma-separated sort list. Allowed: id,provider,model,effective_from,effective_to,created_at. Prefix with `-` for descending order.' example: '-effective_from,provider' - in: query name: 'filter[provider]' description: 'Filter by provider slug.' example: openai required: false schema: type: string description: 'Filter by provider slug.' example: openai - in: query name: 'filter[model]' description: 'Filter by model identifier.' example: gpt-4.1 required: false schema: type: string description: 'Filter by model identifier.' example: gpt-4.1 - in: query name: 'filter[currency]' description: 'Filter by currency code.' example: USD required: false schema: type: string description: 'Filter by currency code.' example: USD - in: query name: 'fields[ai_model_costs]' description: 'Sparse fieldset for AI model costs.' example: 'id,provider,model,currency,is_active' required: false schema: type: string description: 'Sparse fieldset for AI model costs.' example: 'id,provider,model,currency,is_active' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 11 provider: openai model: gpt-4.1 currency: USD input_mtok_price: 5 cached_input_mtok_price: 0.5 output_mtok_price: 15 effective_from: '2026-01-01' effective_to: null is_active: true created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' links: first: null last: null prev: null next: null meta: current_page: 1 last_page: 1 per_page: 1 total: 1 properties: data: type: array example: - id: 11 provider: openai model: gpt-4.1 currency: USD input_mtok_price: 5 cached_input_mtok_price: 0.5 output_mtok_price: 15 effective_from: '2026-01-01' effective_to: null is_active: true created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' items: type: object properties: id: type: integer example: 11 provider: type: string example: openai model: type: string example: gpt-4.1 currency: type: string example: USD input_mtok_price: type: integer example: 5 cached_input_mtok_price: type: number example: 0.5 output_mtok_price: type: integer example: 15 effective_from: type: string example: '2026-01-01' effective_to: type: string example: null nullable: true is_active: type: boolean example: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T09:00:00Z' links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: current_page: type: integer example: 1 last_page: type: integer example: 1 per_page: type: integer example: 1 total: type: integer example: 1 tags: - Costs post: summary: 'AI Model Costs — Create' operationId: aIModelCostsCreate description: 'Create a tenant-global model pricing row.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 11 provider: openai model: gpt-4.1 currency: USD input_mtok_price: 5 cached_input_mtok_price: 0.5 output_mtok_price: 15 effective_from: '2026-01-01' effective_to: null is_active: true created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' properties: data: type: object properties: id: type: integer example: 11 provider: type: string example: openai model: type: string example: gpt-4.1 currency: type: string example: USD input_mtok_price: type: integer example: 5 cached_input_mtok_price: type: number example: 0.5 output_mtok_price: type: integer example: 15 effective_from: type: string example: '2026-01-01' effective_to: type: string example: null nullable: true is_active: type: boolean example: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T09:00:00Z' tags: - Costs requestBody: required: true content: application/json: schema: type: object properties: provider: type: string description: 'Model provider identifier. Must not be greater than 64 characters.' example: openai model: type: string description: 'Provider model identifier. Must not be greater than 255 characters.' example: gpt-4.1 currency: type: string description: 'Tenant currency code. Must contain only letters. The code of an existing record in the currencies table. Must be 3 characters.' example: USD input_mtok_price: type: number description: 'Price per 1M input tokens. Must be at least 0.' example: 5.0 cached_input_mtok_price: type: number description: 'Price per 1M cached input tokens. Must be at least 0.' example: 0.5 nullable: true output_mtok_price: type: number description: 'Price per 1M output tokens. Must be at least 0.' example: 15.0 effective_from: type: string description: 'Effective start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2026-01-01' effective_to: type: string description: 'Optional effective end date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to effective_from.' example: '2026-06-30' nullable: true required: - provider - model - currency - input_mtok_price - output_mtok_price - effective_from '/api/v1/settings/ai/costs/{ai_model_cost}': get: summary: 'AI Model Costs — Show' operationId: aIModelCostsShow description: 'Return a single tenant-global model pricing row.' parameters: - in: query name: 'fields[ai_model_costs]' description: 'Sparse fieldset for AI model costs.' example: 'id,provider,model,currency,is_active' required: false schema: type: string description: 'Sparse fieldset for AI model costs.' example: 'id,provider,model,currency,is_active' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 11 provider: openai model: gpt-4.1 currency: USD input_mtok_price: 5 cached_input_mtok_price: 0.5 output_mtok_price: 15 effective_from: '2026-01-01' effective_to: null is_active: true created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' properties: data: type: object properties: id: type: integer example: 11 provider: type: string example: openai model: type: string example: gpt-4.1 currency: type: string example: USD input_mtok_price: type: integer example: 5 cached_input_mtok_price: type: number example: 0.5 output_mtok_price: type: integer example: 15 effective_from: type: string example: '2026-01-01' effective_to: type: string example: null nullable: true is_active: type: boolean example: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T09:00:00Z' tags: - Costs patch: summary: 'AI Model Costs — Update' operationId: aIModelCostsUpdate description: 'Update a tenant-global model pricing row.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 11 provider: openai model: gpt-4.1-mini currency: USD input_mtok_price: 0.8 cached_input_mtok_price: 0.2 output_mtok_price: 3.2 effective_from: '2026-03-01' effective_to: null is_active: true created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T10:15:00Z' properties: data: type: object properties: id: type: integer example: 11 provider: type: string example: openai model: type: string example: gpt-4.1-mini currency: type: string example: USD input_mtok_price: type: number example: 0.8 cached_input_mtok_price: type: number example: 0.2 output_mtok_price: type: number example: 3.2 effective_from: type: string example: '2026-03-01' effective_to: type: string example: null nullable: true is_active: type: boolean example: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T10:15:00Z' tags: - Costs requestBody: required: true content: application/json: schema: type: object properties: provider: type: string description: 'Model provider identifier. Must not be greater than 64 characters.' example: openai model: type: string description: 'Provider model identifier. Must not be greater than 255 characters.' example: gpt-4.1 currency: type: string description: 'Tenant currency code. Must contain only letters. The code of an existing record in the currencies table. Must be 3 characters.' example: USD input_mtok_price: type: number description: 'Price per 1M input tokens. Must be at least 0.' example: 5.0 cached_input_mtok_price: type: number description: 'Price per 1M cached input tokens. Must be at least 0.' example: 0.5 nullable: true output_mtok_price: type: number description: 'Price per 1M output tokens. Must be at least 0.' example: 15.0 effective_from: type: string description: 'Effective start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2026-01-01' effective_to: type: string description: 'Optional effective end date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to effective_from.' example: '2026-06-30' nullable: true required: - provider - model - currency - input_mtok_price - output_mtok_price - effective_from delete: summary: 'AI Model Costs — Delete' operationId: aIModelCostsDelete description: 'Delete a tenant-global model pricing row.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Costs parameters: - in: path name: ai_model_cost description: 'The AI model cost row id.' example: 11 required: true schema: type: integer /api/v1/department-types/search: get: summary: 'Return the top matching Department Types for the active tenant.' operationId: returnTheTopMatchingDepartmentTypesForTheActiveTenant description: 'Requires `departments.read` ability.' parameters: - in: query name: q description: 'Search term applied to name and slug.' example: ops required: false schema: type: string description: 'Search term applied to name and slug.' example: ops - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: array items: type: object properties: id: type: integer example: 1 name: type: string example: Operations slug: type: string example: operations color: type: string example: '#008080' example: - id: 1 name: Operations slug: operations color: '#008080' tags: - 'Department Types' /api/v1/department-types: post: summary: 'Create a Department Type for the active tenant.' operationId: createADepartmentTypeForTheActiveTenant description: 'Requires `departments.create` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: id: 3 name: Operations slug: operations color: '#008080' properties: id: type: integer example: 3 name: type: string example: Operations slug: type: string example: operations color: type: string example: '#008080' tags: - 'Department Types' requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Display name.' example: Operations slug: type: string description: 'Optional custom slug; defaults to a slugified name.' example: ops nullable: true color: type: string description: 'Optional hex colour (with or without #).' example: '#008080' nullable: true description: type: string description: 'Optional description of the classification.' example: 'Supports company-wide services' nullable: true required: - name '/api/v1/department-types/{departmentType_id}': get: summary: 'Show a specific Department Type.' operationId: showASpecificDepartmentType description: 'Requires `departments.read` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: Operations slug: operations color: '#008080' description: null is_active: true display_order: 0 properties: data: type: object properties: id: type: integer example: 1 name: type: string example: Operations slug: type: string example: operations color: type: string example: '#008080' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 0 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' tags: - 'Department Types' patch: summary: 'Update a Department Type.' operationId: updateADepartmentType description: 'Requires `settings.manage` ability. All fields are optional for partial updates.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: Enablement slug: enablement color: '#008080' description: null is_active: true display_order: 0 properties: data: type: object properties: id: type: integer example: 1 name: type: string example: Enablement slug: type: string example: enablement color: type: string example: '#008080' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 0 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' 422: description: '' content: application/json: schema: type: object example: message: 'The name has already been taken.' errors: name: - 'The name has already been taken.' properties: message: type: string example: 'The name has already been taken.' errors: type: object properties: name: type: array example: - 'The name has already been taken.' items: type: string tags: - 'Department Types' requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Display name. Max 120 characters. Must be unique within the tenant.' example: Enablement slug: type: string description: 'URL-safe slug. Auto-derived from name if omitted on create; not auto-updated on edit. Max 120 characters. Must be unique within the tenant.' example: enablement nullable: true color: type: string description: 'Hex colour code with or without leading `#`.' example: '#008080' nullable: true description: type: string description: 'Optional description. Max 500 characters.' example: 'Supports company-wide services' nullable: true is_active: type: boolean description: 'Whether the type is active.' example: true display_order: type: integer description: 'Sort order for selectors. Between 0 and 999.' example: 1 nullable: true delete: summary: 'Delete a Department Type.' operationId: deleteADepartmentType description: "Requires `settings.manage` ability. Associated departments will have their\n`department_type_id` set to null on delete." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' tags: - 'Department Types' parameters: - in: path name: departmentType_id description: 'The ID of the departmentType.' example: 11 required: true schema: type: integer - in: path name: departmentType description: 'The ID of the department type.' example: 1 required: true schema: type: integer /api/v1/departments: get: summary: 'Display a paginated listing of departments.' operationId: displayAPaginatedListingOfDepartments description: 'Requires `departments.read` ability.' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: scenario description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan required: false schema: type: string description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan - in: query name: 'filter[scenario_id]' description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' required: false schema: type: string description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' - in: query name: 'filter[scenario]' description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan required: false schema: type: string description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' - in: query name: 'filter[lead_id]' description: 'Filter by lead.' example: 44 required: false schema: type: integer description: 'Filter by lead.' example: 44 - in: query name: 'fields[departments]' description: 'Sparse fields for departments.' example: 'id,name,department_type_id' required: false schema: type: string description: 'Sparse fields for departments.' example: 'id,name,department_type_id' - in: query name: include description: 'Comma-separated relationships. Allowed: lead,departmentType,parentDepartment,aggregate.' example: 'lead,departmentType' required: false schema: type: string description: 'Comma-separated relationships. Allowed: lead,departmentType,parentDepartment,aggregate.' example: 'lead,departmentType' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 3 name: Engineering lead_id: 44 cost_center: ENG-001 department_type_id: 7 department_type: id: 7 name: Division slug: division color: '#0052cc' parent_org_unit_id: 202 parent_department_id: 2 parent_department: id: 2 name: Product scenario_id: 1 is_active: true description: 'Platform engineering for core services.' created_at: '2025-01-10T09:00:00Z' links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 3 name: Engineering lead_id: 44 cost_center: ENG-001 department_type_id: 7 department_type: id: 7 name: Division slug: division color: '#0052cc' parent_org_unit_id: 202 parent_department_id: 2 parent_department: id: 2 name: Product scenario_id: 1 is_active: true description: 'Platform engineering for core services.' created_at: '2025-01-10T09:00:00Z' items: type: object properties: id: type: integer example: 3 name: type: string example: Engineering lead_id: type: integer example: 44 cost_center: type: string example: ENG-001 department_type_id: type: integer example: 7 department_type: type: object properties: id: type: integer example: 7 name: type: string example: Division slug: type: string example: division color: type: string example: '#0052cc' parent_org_unit_id: type: integer example: 202 parent_department_id: type: integer example: 2 parent_department: type: object properties: id: type: integer example: 2 name: type: string example: Product scenario_id: type: integer example: 1 is_active: type: boolean example: true description: type: string example: 'Platform engineering for core services.' created_at: type: string example: '2025-01-10T09:00:00Z' links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - name - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - name - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - Departments post: summary: 'Store a newly created department.' operationId: storeANewlyCreatedDepartment description: "Requires `departments.create` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 3 name: Engineering lead_id: 44 cost_center: ENG-001 department_type_id: 7 department_type: id: 7 name: Division slug: division color: '#0052cc' parent_org_unit_id: 202 parent_department_id: null parent_department: null scenario_id: 1 is_active: true description: 'Owns platform reliability.' created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 3 name: type: string example: Engineering lead_id: type: integer example: 44 cost_center: type: string example: ENG-001 department_type_id: type: integer example: 7 department_type: type: object properties: id: type: integer example: 7 name: type: string example: Division slug: type: string example: division color: type: string example: '#0052cc' parent_org_unit_id: type: integer example: 202 parent_department_id: type: string example: null nullable: true parent_department: type: string example: null nullable: true scenario_id: type: integer example: 1 is_active: type: boolean example: true description: type: string example: 'Owns platform reliability.' created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Departments requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Department name (unique per tenant and scenario). Must not be greater than 255 characters.' example: Engineering department_type_id: type: integer description: 'Department type identifier. The id of an existing record in the department_types table.' example: 5 parent_org_unit_id: type: integer description: 'Optional parent org unit id.' example: 42 nullable: true cost_center: type: string description: 'Optional cost center code. Must not be greater than 50 characters.' example: ENG-1001 nullable: true department_lead_id: type: integer description: 'Actor id of department lead.' example: 44 nullable: true is_active: type: boolean description: 'Whether the department is active.' example: true nullable: true description: type: string description: 'Optional department description.' example: 'Supports the full product org.' nullable: true required: - name - department_type_id '/api/v1/departments/{id}': get: summary: 'Display a specific department.' operationId: displayASpecificDepartment description: 'Requires `departments.read` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 3 name: Engineering lead_id: 44 cost_center: ENG-001 department_type_id: 7 department_type: id: 7 name: Division slug: division color: '#0052cc' parent_org_unit_id: 202 parent_department_id: 2 parent_department: id: 2 name: Product scenario_id: 1 is_active: true description: 'Platform engineering for core services.' created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 3 name: type: string example: Engineering lead_id: type: integer example: 44 cost_center: type: string example: ENG-001 department_type_id: type: integer example: 7 department_type: type: object properties: id: type: integer example: 7 name: type: string example: Division slug: type: string example: division color: type: string example: '#0052cc' parent_org_unit_id: type: integer example: 202 parent_department_id: type: integer example: 2 parent_department: type: object properties: id: type: integer example: 2 name: type: string example: Product scenario_id: type: integer example: 1 is_active: type: boolean example: true description: type: string example: 'Platform engineering for core services.' created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Departments put: summary: 'Update a department.' operationId: updateADepartment description: "Requires `departments.update` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 3 name: 'Engineering & Infrastructure' lead_id: 44 cost_center: ENG-001 department_type_id: 7 department_type: id: 7 name: Division slug: division color: '#0052cc' parent_org_unit_id: 202 parent_department_id: 2 parent_department: id: 2 name: Product scenario_id: 1 is_active: true description: 'Platform engineering for core services.' created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 3 name: type: string example: 'Engineering & Infrastructure' lead_id: type: integer example: 44 cost_center: type: string example: ENG-001 department_type_id: type: integer example: 7 department_type: type: object properties: id: type: integer example: 7 name: type: string example: Division slug: type: string example: division color: type: string example: '#0052cc' parent_org_unit_id: type: integer example: 202 parent_department_id: type: integer example: 2 parent_department: type: object properties: id: type: integer example: 2 name: type: string example: Product scenario_id: type: integer example: 1 is_active: type: boolean example: true description: type: string example: 'Platform engineering for core services.' created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Departments requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Department name when changing department metadata (unique per tenant and scenario). Must not be greater than 255 characters.' example: Engineering department_type_id: type: integer description: 'Department type identifier when changing department metadata. The id of an existing record in the department_types table.' example: 5 parent_org_unit_id: type: integer description: 'Optional parent org unit id.' example: 42 nullable: true department_lead_id: type: integer description: 'Actor id of department lead.' example: 44 nullable: true cost_center: type: string description: 'Optional cost center code. Must not be greater than 50 characters.' example: ENG-1001 nullable: true is_active: type: boolean description: 'Whether the department is active.' example: true nullable: true description: type: string description: 'Optional department description.' example: 'Supports the full product org.' nullable: true delete: summary: 'Delete a department.' operationId: deleteADepartment description: "Requires `departments.delete` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Departments parameters: - in: path name: id description: 'The ID of the department.' example: 11 required: true schema: type: integer /api/v1/initiatives: get: summary: 'Display a paginated listing of initiatives.' operationId: displayAPaginatedListingOfInitiatives description: 'Requires `initiatives.read` ability.' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,title,created_at.' example: '-created_at,title' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,title,created_at.' example: '-created_at,title' - in: query name: search description: 'Search by title or description.' example: cost required: false schema: type: string description: 'Search by title or description.' example: cost - in: query name: 'filter[status]' description: 'Filter by status.' example: active required: false schema: type: string description: 'Filter by status.' example: active - in: query name: 'filter[baseline_scenario_id]' description: 'Filter by baseline scenario.' example: 1 required: false schema: type: integer description: 'Filter by baseline scenario.' example: 1 - in: query name: 'filter[target_scenario_id]' description: 'Filter by target scenario.' example: 2 required: false schema: type: integer description: 'Filter by target scenario.' example: 2 - in: query name: 'fields[initiatives]' description: 'Sparse fields for initiatives.' example: 'id,title' required: false schema: type: string description: 'Sparse fields for initiatives.' example: 'id,title' - in: query name: include description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' required: false schema: type: string description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 title: 'Cost Reduction' description: 'Reduce infrastructure spend by 15%' status: active baseline_scenario_id: 1 target_scenario_id: 2 created_at: '2025-01-10T09:00:00Z' updated_at: '2025-01-10T09:00:00Z' - id: 2 title: 'Mobile Workforce Rollout' description: null status: draft baseline_scenario_id: 1 target_scenario_id: 3 created_at: '2025-01-11T10:00:00Z' updated_at: '2025-01-11T10:00:00Z' links: first: null last: null prev: null next: null meta: total: 2 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 1 title: 'Cost Reduction' description: 'Reduce infrastructure spend by 15%' status: active baseline_scenario_id: 1 target_scenario_id: 2 created_at: '2025-01-10T09:00:00Z' updated_at: '2025-01-10T09:00:00Z' - id: 2 title: 'Mobile Workforce Rollout' description: null status: draft baseline_scenario_id: 1 target_scenario_id: 3 created_at: '2025-01-11T10:00:00Z' updated_at: '2025-01-11T10:00:00Z' items: type: object properties: id: type: integer example: 1 title: type: string example: 'Cost Reduction' description: type: string example: 'Reduce infrastructure spend by 15%' status: type: string example: active baseline_scenario_id: type: integer example: 1 target_scenario_id: type: integer example: 2 created_at: type: string example: '2025-01-10T09:00:00Z' updated_at: type: string example: '2025-01-10T09:00:00Z' links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 2 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - title - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - title - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - Initiatives post: summary: 'Store a newly created initiative.' operationId: storeANewlyCreatedInitiative description: 'Requires `initiatives.create` ability.' parameters: - in: query name: 'fields[initiatives]' description: 'Sparse fields for initiatives.' example: 'id,title' required: false schema: type: string description: 'Sparse fields for initiatives.' example: 'id,title' - in: query name: include description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' required: false schema: type: string description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 1 title: 'Cost Reduction' description: 'Reduce infrastructure spend by 15%' status: active baseline_scenario_id: 1 target_scenario_id: 2 created_at: '2025-01-10T09:00:00Z' updated_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 1 title: type: string example: 'Cost Reduction' description: type: string example: 'Reduce infrastructure spend by 15%' status: type: string example: active baseline_scenario_id: type: integer example: 1 target_scenario_id: type: integer example: 2 created_at: type: string example: '2025-01-10T09:00:00Z' updated_at: type: string example: '2025-01-10T09:00:00Z' 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: title: - 'The title field is required.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: title: type: array example: - 'The title field is required.' items: type: string tags: - Initiatives requestBody: required: true content: application/json: schema: type: object properties: title: type: string description: 'Initiative title. Must not be greater than 255 characters.' example: 'Reduce Cloud Spend' description: type: string description: 'Optional summary/description.' example: 'Target a 15% reduction by Q4.' nullable: true status: type: string description: 'Lifecycle status (draft, active, paused, completed).' example: active enum: - draft - active - paused - completed baseline_scenario_id: type: integer description: 'Baseline scenario id. The id of an existing record in the scenarios table.' example: 1 nullable: true target_scenario_id: type: integer description: 'Target scenario id. The id of an existing record in the scenarios table.' example: 2 nullable: true required: - title - status '/api/v1/initiatives/{id}': get: summary: 'Display a specific initiative.' operationId: displayASpecificInitiative description: 'Requires `initiatives.read` ability.' parameters: - in: query name: 'fields[initiatives]' description: 'Sparse fields for initiatives.' example: 'id,title' required: false schema: type: string description: 'Sparse fields for initiatives.' example: 'id,title' - in: query name: include description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' required: false schema: type: string description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 title: 'Cost Reduction' description: 'Reduce infrastructure spend by 15%' status: active baseline_scenario_id: 1 target_scenario_id: 2 baseline_scenario: id: 1 name: 'Current State' status: published target_scenario: id: 2 name: 'Optimized State' status: draft created_at: '2025-01-10T09:00:00Z' updated_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 1 title: type: string example: 'Cost Reduction' description: type: string example: 'Reduce infrastructure spend by 15%' status: type: string example: active baseline_scenario_id: type: integer example: 1 target_scenario_id: type: integer example: 2 baseline_scenario: type: object properties: id: type: integer example: 1 name: type: string example: 'Current State' status: type: string example: published target_scenario: type: object properties: id: type: integer example: 2 name: type: string example: 'Optimized State' status: type: string example: draft created_at: type: string example: '2025-01-10T09:00:00Z' updated_at: type: string example: '2025-01-10T09:00:00Z' tags: - Initiatives put: summary: 'Update a specific initiative.' operationId: updateASpecificInitiative description: 'Requires `initiatives.update` ability.' parameters: - in: query name: 'fields[initiatives]' description: 'Sparse fields for initiatives.' example: 'id,title' required: false schema: type: string description: 'Sparse fields for initiatives.' example: 'id,title' - in: query name: include description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' required: false schema: type: string description: 'Comma-separated relationships. Allowed: baseline_scenario,target_scenario.' example: 'baseline_scenario,target_scenario' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 title: 'Cost Optimization' description: 'Reduce infrastructure spend by 20%' status: active baseline_scenario_id: 1 target_scenario_id: 2 created_at: '2025-01-10T09:00:00Z' updated_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 1 title: type: string example: 'Cost Optimization' description: type: string example: 'Reduce infrastructure spend by 20%' status: type: string example: active baseline_scenario_id: type: integer example: 1 target_scenario_id: type: integer example: 2 created_at: type: string example: '2025-01-10T09:00:00Z' updated_at: type: string example: '2025-01-10T09:00:00Z' 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: status: - 'The selected status is invalid.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: status: type: array example: - 'The selected status is invalid.' items: type: string tags: - Initiatives requestBody: required: true content: application/json: schema: type: object properties: title: type: string description: 'Initiative title. Must not be greater than 255 characters.' example: 'Reduce Cloud Spend' description: type: string description: 'Optional summary/description.' example: 'Target a 15% reduction by Q4.' nullable: true status: type: string description: 'Lifecycle status (draft, active, paused, completed).' example: paused enum: - draft - active - paused - completed baseline_scenario_id: type: integer description: 'Baseline scenario id. The id of an existing record in the scenarios table.' example: 1 nullable: true target_scenario_id: type: integer description: 'Target scenario id. The id of an existing record in the scenarios table.' example: 2 nullable: true required: - title - status delete: summary: 'Delete a specific initiative.' operationId: deleteASpecificInitiative description: 'Requires `initiatives.delete` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Initiatives parameters: - in: path name: id description: 'The ID of the initiative.' example: 11 required: true schema: type: integer '/api/v1/integrations/bamboohr/webhook/{tenant}': post: summary: 'Receive BambooHR webhook payload.' operationId: receiveBambooHRWebhookPayload description: "This endpoint always responds quickly. When the tenant is missing, the plan\ndoes not include HRIS integrations, or webhook auth fails, it still returns\n`202 Accepted` without queueing sync work." parameters: [] responses: 202: description: '' content: application/json: schema: type: object example: status: accepted properties: status: type: string example: accepted tags: - Integrations security: [] parameters: - in: path name: tenant description: 'Tenant slug.' example: demo required: true schema: type: string /api/v1/lineage: get: summary: 'List baseline lineage events (promotions + snapshot restores).' operationId: listBaselineLineageEventspromotions+SnapshotRestores description: '' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: 'filter[type]' description: 'Filter by lineage type (scenario_promotion, snapshot_restore).' example: scenario_promotion required: false schema: type: string description: 'Filter by lineage type (scenario_promotion, snapshot_restore).' example: scenario_promotion - in: query name: 'filter[scenario_id]' description: 'Filter by related scenario id (promoted/applied/archived).' example: 42 required: false schema: type: integer description: 'Filter by related scenario id (promoted/applied/archived).' example: 42 - in: query name: 'filter[applied_from]' description: 'date Filter by applied_at date (inclusive, YYYY-MM-DD).' example: '2025-01-01' required: false schema: type: string description: 'date Filter by applied_at date (inclusive, YYYY-MM-DD).' example: '2025-01-01' - in: query name: 'filter[applied_to]' description: 'date Filter by applied_at date (inclusive, YYYY-MM-DD).' example: '2025-12-31' required: false schema: type: string description: 'date Filter by applied_at date (inclusive, YYYY-MM-DD).' example: '2025-12-31' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 type: scenario_promotion applied_at: '2026-01-30T12:00:00Z' links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 1 type: scenario_promotion applied_at: '2026-01-30T12:00:00Z' items: type: object properties: id: type: integer example: 1 type: type: string example: scenario_promotion applied_at: type: string example: '2026-01-30T12:00:00Z' links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 tags: - Lineage requestBody: required: false content: application/json: schema: type: object properties: page: type: integer description: 'Must be at least 1.' example: 61 per_page: type: integer description: 'Must be at least 1. Must not be greater than 100.' example: 7 filter: type: object description: '' example: [] properties: type: type: string description: '' example: scenario_promotion enum: - scenario_promotion - snapshot_restore nullable: true scenario_id: type: integer description: '' example: 11 nullable: true applied_from: type: string description: 'Must be a valid date.' example: '2026-10-11T21:57:37' nullable: true applied_to: type: string description: 'Must be a valid date.' example: '2026-10-11T21:57:37' nullable: true '/api/v1/actors/{actor_id}/placements': get: summary: 'Actor placements — List' operationId: actorPlacementsList description: "Return placements for an actor as of a date. By default only active placements\nare returned; pass `include_history=1` to include historical rows as well.\n\nRequires `placements.read` ability." parameters: - in: query name: as_of description: 'Filter placements active on this ISO 8601 date.' example: '2025-09-01' required: false schema: type: string description: 'Filter placements active on this ISO 8601 date.' example: '2025-09-01' - in: query name: include_history description: 'Include historical placements in addition to the active set.' example: true required: false schema: type: boolean description: 'Include historical placements in addition to the active set.' example: true - in: query name: scenario_id description: 'Scenario slug or id to scope placements; omit or pass `null` for baseline.' example: future-plan required: false schema: type: string description: 'Scenario slug or id to scope placements; omit or pass `null` for baseline.' example: future-plan - in: query name: include description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: 'actor,org_unit' required: false schema: type: string description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: 'actor,org_unit' - in: query name: 'fields[placements]' description: 'Sparse fieldset.' example: 'id,org_unit_id,valid_from,valid_to' required: false schema: type: string description: 'Sparse fieldset.' example: 'id,org_unit_id,valid_from,valid_to' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 actor_id: 42 org_unit_id: 10 scenario_id: null primary: true allocation_pct: 100 fte: 1.0 valid_from: '2025-01-01' valid_to: null is_active: true properties: data: type: array example: - id: 1 actor_id: 42 org_unit_id: 10 scenario_id: null primary: true allocation_pct: 100 fte: 1 valid_from: '2025-01-01' valid_to: null is_active: true items: type: object properties: id: type: integer example: 1 actor_id: type: integer example: 42 org_unit_id: type: integer example: 10 scenario_id: type: string example: null nullable: true primary: type: boolean example: true allocation_pct: type: integer example: 100 fte: type: number example: 1.0 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true is_active: type: boolean example: true tags: - 'Org Placements' post: summary: 'Actor placements — Create' operationId: actorPlacementsCreate description: "Creates a new placement for the given actor.\n\nRequires `placements.write` ability." parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: org_unit required: false schema: type: string description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: org_unit - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 9 actor_id: 11 org_unit_id: 10 scenario_id: null primary: false allocation_pct: 60 fte: 0.6 valid_from: '2025-09-01' valid_to: null is_active: true properties: data: type: object properties: id: type: integer example: 9 actor_id: type: integer example: 11 org_unit_id: type: integer example: 10 scenario_id: type: string example: null nullable: true primary: type: boolean example: false allocation_pct: type: integer example: 60 fte: type: number example: 0.6 valid_from: type: string example: '2025-09-01' valid_to: type: string example: null nullable: true is_active: type: boolean example: true tags: - 'Org Placements' requestBody: required: true content: application/json: schema: type: object properties: org_unit_id: type: integer description: 'Org unit receiving the placement.' example: 10 scenario_id: type: integer description: 'Scenario context for the placement; defaults to the org unit scenario.' example: 4 nullable: true primary: type: boolean description: 'Mark as the primary placement.' example: true allocation_pct: type: integer description: "Percentage of the actor's time for this team (0–300). Totals across placements may exceed 100% (over-allocation is allowed)." example: 60 valid_from: type: string description: 'Start date (YYYY-MM-DD).' example: '2025-09-01' nullable: true valid_to: type: string description: 'End date (YYYY-MM-DD).' example: '2025-12-31' nullable: true source: type: string description: 'Source identifier for auditing.' example: manual nullable: true meta: type: object description: 'Additional metadata payload.' example: [] properties: { } nullable: true required: - org_unit_id - allocation_pct parameters: - in: path name: actor_id description: 'The ID of the actor.' example: 11 required: true schema: type: integer - in: path name: actor description: 'The actor identifier.' example: 11 required: true schema: type: integer '/api/v1/placements/{id}': patch: summary: 'Actor placements — Update' operationId: actorPlacementsUpdate description: "Updates an existing placement.\n\nRequires `placements.write` ability." parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: actor required: false schema: type: string description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: actor - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 9 actor_id: 11 org_unit_id: 12 scenario_id: null primary: false allocation_pct: 50 fte: 0.5 valid_from: '2025-06-01' valid_to: '2025-09-30' is_active: false properties: data: type: object properties: id: type: integer example: 9 actor_id: type: integer example: 11 org_unit_id: type: integer example: 12 scenario_id: type: string example: null nullable: true primary: type: boolean example: false allocation_pct: type: integer example: 50 fte: type: number example: 0.5 valid_from: type: string example: '2025-06-01' valid_to: type: string example: '2025-09-30' is_active: type: boolean example: false tags: - 'Org Placements' requestBody: required: false content: application/json: schema: type: object properties: org_unit_id: type: integer description: 'Org unit receiving the placement.' example: 12 scenario_id: type: integer description: 'Scenario context for the placement; defaults to the current value.' example: 4 nullable: true primary: type: boolean description: 'Mark as the primary placement.' example: false allocation_pct: type: integer description: "Percentage of the actor's time for this team (0–300). Totals across placements may exceed 100% (over-allocation is allowed)." example: 50 valid_from: type: string description: 'Start date (YYYY-MM-DD).' example: '2025-06-01' nullable: true valid_to: type: string description: 'End date (YYYY-MM-DD).' example: '2025-09-30' nullable: true source: type: string description: 'Source identifier for auditing.' example: manual nullable: true meta: type: object description: 'Additional metadata payload.' example: [] properties: { } nullable: true delete: summary: 'Actor placements — End' operationId: actorPlacementsEnd description: "Ends a placement by stamping the `valid_to` date. Omitting the\n`valid_to` query parameter defaults to today.\n\nRequires `placements.delete` ability." parameters: - in: query name: valid_to description: 'Date to end the placement on (YYYY-MM-DD). Defaults to today.' example: '2025-10-01' required: false schema: type: string description: 'Date to end the placement on (YYYY-MM-DD). Defaults to today.' example: '2025-10-01' - in: query name: include description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: org_unit required: false schema: type: string description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: org_unit - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 9 actor_id: 11 org_unit_id: 12 scenario_id: null primary: false allocation_pct: 50 fte: 0.5 valid_from: '2025-06-01' valid_to: '2025-10-01' is_active: false properties: data: type: object properties: id: type: integer example: 9 actor_id: type: integer example: 11 org_unit_id: type: integer example: 12 scenario_id: type: string example: null nullable: true primary: type: boolean example: false allocation_pct: type: integer example: 50 fte: type: number example: 0.5 valid_from: type: string example: '2025-06-01' valid_to: type: string example: '2025-10-01' is_active: type: boolean example: false tags: - 'Org Placements' parameters: - in: path name: id description: 'The ID of the placement.' example: 11 required: true schema: type: integer - in: path name: placement description: 'Placement identifier.' example: 11 required: true schema: type: integer '/api/v1/org-units/{org_unit_id}/actors': get: summary: 'Org unit roster — List' operationId: orgUnitRosterList description: "Lists active placements for the specified org unit. Results default to placements\nactive today but may be scoped to a different date using `as_of`.\n\nRequires `placements.read` ability." parameters: - in: query name: as_of description: 'Date used to evaluate active placements.' example: '2025-09-01' required: false schema: type: string description: 'Date used to evaluate active placements.' example: '2025-09-01' - in: query name: scenario_id description: 'Scenario slug or id to scope placements; omit or pass `null` for baseline.' example: future-plan required: false schema: type: string description: 'Scenario slug or id to scope placements; omit or pass `null` for baseline.' example: future-plan - in: query name: include description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: actor required: false schema: type: string description: 'Comma-separated relationships. Allowed: actor,org_unit.' example: actor - in: query name: 'fields[placements]' description: 'Sparse fieldset.' example: 'id,actor_id,allocation_pct' required: false schema: type: string description: 'Sparse fieldset.' example: 'id,actor_id,allocation_pct' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 7 actor: id: 14 name: "Aisling O'Neill" allocation_pct: 100 fte: 1 valid_from: '2025-07-01' valid_to: null is_active: true properties: data: type: array example: - id: 7 actor: id: 14 name: "Aisling O'Neill" allocation_pct: 100 fte: 1 valid_from: '2025-07-01' valid_to: null is_active: true items: type: object properties: id: type: integer example: 7 actor: type: object properties: id: type: integer example: 14 name: type: string example: "Aisling O'Neill" allocation_pct: type: integer example: 100 fte: type: integer example: 1 valid_from: type: string example: '2025-07-01' valid_to: type: string example: null nullable: true is_active: type: boolean example: true tags: - 'Org Placements' parameters: - in: path name: org_unit_id description: 'The ID of the org unit.' example: 11 required: true schema: type: integer - in: path name: org_unit description: 'The org unit identifier.' example: 11 required: true schema: type: integer /api/v1/org-units: get: summary: 'Display a paginated listing of org units.' operationId: displayAPaginatedListingOfOrgUnits description: '' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: scenario description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan required: false schema: type: string description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan - in: query name: 'filter[scenario_id]' description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' required: false schema: type: string description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' - in: query name: 'filter[scenario]' description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan required: false schema: type: string description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,label,created_at.' example: '-created_at,label' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,label,created_at.' example: '-created_at,label' - in: query name: 'filter[type]' description: 'Filter by the org unit type (string). Typical values include node, team, and department. Other values may appear if inserted in data.' example: node required: false schema: type: string description: 'Filter by the org unit type (string). Typical values include node, team, and department. Other values may appear if inserted in data.' example: node - in: query name: 'filter[parent_id]' description: 'Filter by parent org unit ID.' example: 42 required: false schema: type: integer description: 'Filter by parent org unit ID.' example: 42 - in: query name: 'fields[org_units]' description: 'Sparse fields for org units.' example: 'id,label' required: false schema: type: string description: 'Sparse fields for org units.' example: 'id,label' - in: query name: include description: 'Comma-separated relationships. Allowed: scenario,actors,metrics.' example: 'scenario,actors,metrics' required: false schema: type: string description: 'Comma-separated relationships. Allowed: scenario,actors,metrics.' example: 'scenario,actors,metrics' - in: query name: as_of description: 'date Filter actors memberships or metrics by date. Format: YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'date Filter actors memberships or metrics by date. Format: YYYY-MM-DD.' example: '2025-09-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 202 type: node entity_id: 4102 label: 'Retail – Ireland' scenario_id: 1 parent_id: 101 links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 202 type: node entity_id: 4102 label: 'Retail – Ireland' scenario_id: 1 parent_id: 101 items: type: object properties: id: type: integer example: 202 type: type: string example: node entity_id: type: integer example: 4102 label: type: string example: 'Retail – Ireland' scenario_id: type: integer example: 1 parent_id: type: integer example: 101 links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - label - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - label - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - 'Org Units' post: summary: 'Create a new org unit.' operationId: createANewOrgUnit description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 210 type: team entity_id: 15 label: 'Team CúChulainn' scenario_id: 1 parent_id: 202 valid_from: '2025-01-01' valid_to: '2025-12-31' properties: data: type: object properties: id: type: integer example: 210 type: type: string example: team entity_id: type: integer example: 15 label: type: string example: 'Team CúChulainn' scenario_id: type: integer example: 1 parent_id: type: integer example: 202 valid_from: type: string example: '2025-01-01' valid_to: type: string example: '2025-12-31' tags: - 'Org Units' requestBody: required: true content: application/json: schema: type: object properties: type: type: string description: 'Org unit type.' example: team entity_id: type: integer description: 'Linked entity ID.' example: 15 label: type: string description: 'Optional display label.' example: 'Team CúChulainn' nullable: true scenario_id: type: integer description: 'Scenario identifier.' example: 1 nullable: true parent_id: type: integer description: 'Parent org unit ID.' example: 202 nullable: true valid_from: type: string description: 'The start date. Format: YYYY-MM-DD.' example: '2025-01-01' valid_to: type: string description: 'The end date. Format: YYYY-MM-DD.' example: '2025-12-31' nullable: true required: - type - entity_id - scenario_id - valid_from /api/v1/org-units/summary: get: summary: 'Get a summarized charts view.' operationId: getASummarizedChartsView description: '' parameters: - in: query name: root_id description: 'The ID of the root org unit.' example: 202 required: true schema: type: integer description: 'The ID of the root org unit.' example: 202 - in: query name: as_of description: 'date Format YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'date Format YYYY-MM-DD.' example: '2025-09-01' nullable: true - in: query name: sprint_id description: 'The sprint for velocity metrics.' example: 42 required: false schema: type: integer description: 'The sprint for velocity metrics.' example: 42 nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 202 label: 'Retail – Ireland' type: node entity_id: 4102 rollup: direct_headcount: 12 subtree_headcount: 38 direct_monthly_cost: value: 528500 formatted: '€5,285.00' currency: EUR subtree_monthly_cost: value: 1285000 formatted: '€12,850.00' currency: EUR direct_monthly_cost_cents: 528500 subtree_monthly_cost_cents: 1285000 velocity_sp: 35 velocity_team_count: 3 children: [] properties: data: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' type: type: string example: node entity_id: type: integer example: 4102 rollup: type: object properties: direct_headcount: type: integer example: 12 subtree_headcount: type: integer example: 38 direct_monthly_cost: type: object properties: value: type: integer example: 528500 formatted: type: string example: '€5,285.00' currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 1285000 formatted: type: string example: '€12,850.00' currency: type: string example: EUR direct_monthly_cost_cents: type: integer example: 528500 subtree_monthly_cost_cents: type: integer example: 1285000 velocity_sp: type: integer example: 35 velocity_team_count: type: integer example: 3 children: type: array example: [] tags: - 'Org Units' /api/v1/org-units/tree: get: summary: 'Get an org subtree (nested) for the current tenant.' operationId: getAnOrgSubtreenestedForTheCurrentTenant description: "Returns the subtree for the given root_id (or the tenant root if omitted),\nup to a maximum depth. Supports includes (members,parent,ancestors,path,metrics),\nsearch by name, and filtering by type." parameters: - in: query name: root_id description: 'Optional root node id.' example: 1 required: false schema: type: integer description: 'Optional root node id.' example: 1 - in: query name: depth description: 'The max depth to traverse from the root. Default: 2.' example: 3 required: false schema: type: integer description: 'The max depth to traverse from the root. Default: 2.' example: 3 - in: query name: include description: 'Comma-separated includes: actors,members,parent,ancestors,path,metrics.' example: 'actors,parent' required: false schema: type: string description: 'Comma-separated includes: actors,members,parent,ancestors,path,metrics.' example: 'actors,parent' - in: query name: filter description: '' example: [] required: false schema: type: object description: '' example: [] properties: { } - in: query name: filter.type description: 'Filter by org unit type.' example: team required: false schema: type: string description: 'Filter by org unit type.' example: team - in: query name: search description: 'Case-insensitive search on label.' example: Apollo required: false schema: type: string description: 'Case-insensitive search on label.' example: Apollo - in: query name: sort description: 'Sort by label asc/desc. Allowed: label, -label. Default: label.' example: '-label' required: false schema: type: string description: 'Sort by label asc/desc. Allowed: label, -label. Default: label.' example: '-label' - in: query name: 'filter[type]' description: 'Filter by org unit type.' example: team required: false schema: type: string description: 'Filter by org unit type.' example: team - in: header name: X-Tenant description: '' example: 'string required The tenant slug or id.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 101 label: Engineering code: null type: node entity_id: 4102 parent_id: null scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' parent: null ancestors: [] path: - Engineering metrics: as_of: '2025-01-10' direct_headcount: 0 subtree_headcount: 0 direct_monthly_cost: value: 0 formatted: €0.00 currency: EUR subtree_monthly_cost: value: 0 formatted: €0.00 currency: EUR children: - id: 202 label: 'Team CúChulainn' code: null type: team entity_id: 15 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' parent: id: 101 label: Engineering ancestors: - id: 101 label: Engineering path: - Engineering - 'Team CúChulainn' metrics: as_of: '2025-01-10' direct_headcount: 0 subtree_headcount: 0 direct_monthly_cost: value: 0 formatted: €0.00 currency: EUR subtree_monthly_cost: value: 0 formatted: €0.00 currency: EUR children: [] properties: data: type: object properties: id: type: integer example: 101 label: type: string example: Engineering code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: string example: null nullable: true scenario_id: type: integer example: 1 valid_from: type: string example: null nullable: true valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' parent: type: string example: null nullable: true ancestors: type: array example: [] path: type: array example: - Engineering items: type: string metrics: type: object properties: as_of: type: string example: '2025-01-10' direct_headcount: type: integer example: 0 subtree_headcount: type: integer example: 0 direct_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR children: type: array example: - id: 202 label: 'Team CúChulainn' code: null type: team entity_id: 15 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' parent: id: 101 label: Engineering ancestors: - id: 101 label: Engineering path: - Engineering - 'Team CúChulainn' metrics: as_of: '2025-01-10' direct_headcount: 0 subtree_headcount: 0 direct_monthly_cost: value: 0 formatted: €0.00 currency: EUR subtree_monthly_cost: value: 0 formatted: €0.00 currency: EUR children: [] items: type: object properties: id: type: integer example: 202 label: type: string example: 'Team CúChulainn' code: type: string example: null nullable: true type: type: string example: team entity_id: type: integer example: 15 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: null nullable: true valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' parent: type: object properties: id: type: integer example: 101 label: type: string example: Engineering ancestors: type: array example: - id: 101 label: Engineering items: type: object properties: id: type: integer example: 101 label: type: string example: Engineering path: type: array example: - Engineering - 'Team CúChulainn' items: type: string metrics: type: object properties: as_of: type: string example: '2025-01-10' direct_headcount: type: integer example: 0 subtree_headcount: type: integer example: 0 direct_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR children: type: array example: [] tags: - 'Org Units' '/api/v1/org-units/{id}/descendants': get: summary: 'Get flattened descendants with cursor pagination.' operationId: getFlattenedDescendantsWithCursorPagination description: '' parameters: - in: query name: depth description: 'The max depth to traverse from the root. Default: 3.' example: 2 required: false schema: type: integer description: 'The max depth to traverse from the root. Default: 3.' example: 2 - in: query name: include description: 'Comma-separated includes: actors,members,parent,ancestors,path,metrics.' example: parent required: false schema: type: string description: 'Comma-separated includes: actors,members,parent,ancestors,path,metrics.' example: parent - in: query name: filter description: '' example: [] required: false schema: type: object description: '' example: [] properties: { } - in: query name: filter.type description: 'Filter by org unit type.' example: team required: false schema: type: string description: 'Filter by org unit type.' example: team - in: query name: search description: 'Case-insensitive search on label.' example: Apollo required: false schema: type: string description: 'Case-insensitive search on label.' example: Apollo - in: query name: per_page description: 'Items per page. Max 200.' example: 50 required: false schema: type: integer description: 'Items per page. Max 200.' example: 50 - in: query name: cursor description: 'Pagination cursor' example: ab required: false schema: type: string description: 'Pagination cursor' example: ab - in: query name: 'filter[type]' description: 'Filter by org unit type.' example: team required: false schema: type: string description: 'Filter by org unit type.' example: team - in: header name: X-Tenant description: '' example: 'string required The tenant slug or id.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 202 label: 'Team CúChulainn' code: null type: team entity_id: 15 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' metrics: as_of: '2025-01-10' direct_headcount: 0 subtree_headcount: 0 direct_monthly_cost: value: 0 formatted: €0.00 currency: EUR subtree_monthly_cost: value: 0 formatted: €0.00 currency: EUR - id: 203 label: 'Team Fianna' code: null type: team entity_id: 16 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' links: next: null prev: null meta: per_page: 50 properties: data: type: array example: - id: 202 label: 'Team CúChulainn' code: null type: team entity_id: 15 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' metrics: as_of: '2025-01-10' direct_headcount: 0 subtree_headcount: 0 direct_monthly_cost: value: 0 formatted: €0.00 currency: EUR subtree_monthly_cost: value: 0 formatted: €0.00 currency: EUR - id: 203 label: 'Team Fianna' code: null type: team entity_id: 16 parent_id: 101 scenario_id: 1 valid_from: null valid_to: null created_at: '2025-01-10T09:00:00Z' items: type: object properties: id: type: integer example: 202 label: type: string example: 'Team CúChulainn' code: type: string example: null nullable: true type: type: string example: team entity_id: type: integer example: 15 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: null nullable: true valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' metrics: type: object properties: as_of: type: string example: '2025-01-10' direct_headcount: type: integer example: 0 subtree_headcount: type: integer example: 0 direct_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 0 formatted: type: string example: €0.00 currency: type: string example: EUR links: type: object properties: next: type: string example: null nullable: true prev: type: string example: null nullable: true meta: type: object properties: per_page: type: integer example: 50 tags: - 'Org Units' parameters: - in: path name: id description: 'The root id.' example: 1 required: true schema: type: integer '/api/v1/org-units/{id}': get: summary: 'Show a single org unit summary.' operationId: showASingleOrgUnitSummary description: '' parameters: - in: query name: as_of description: 'date Format YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'date Format YYYY-MM-DD.' example: '2025-09-01' nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 202 label: 'Retail – Ireland' type: node entity_id: 4102 rollup: direct_headcount: 12 subtree_headcount: 38 direct_monthly_cost: value: 528500 formatted: '€5,285.00' currency: EUR subtree_monthly_cost: value: 1285000 formatted: '€12,850.00' currency: EUR direct_monthly_cost_cents: 528500 subtree_monthly_cost_cents: 1285000 velocity_sp: 35 velocity_team_count: 3 children: [] properties: data: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' type: type: string example: node entity_id: type: integer example: 4102 rollup: type: object properties: direct_headcount: type: integer example: 12 subtree_headcount: type: integer example: 38 direct_monthly_cost: type: object properties: value: type: integer example: 528500 formatted: type: string example: '€5,285.00' currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 1285000 formatted: type: string example: '€12,850.00' currency: type: string example: EUR direct_monthly_cost_cents: type: integer example: 528500 subtree_monthly_cost_cents: type: integer example: 1285000 velocity_sp: type: integer example: 35 velocity_team_count: type: integer example: 3 children: type: array example: [] tags: - 'Org Units' patch: summary: 'Update an org unit.' operationId: updateAnOrgUnit description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 210 type: team entity_id: 15 label: 'Team CúChulainn' scenario_id: 1 parent_id: 202 valid_from: '2025-01-01' valid_to: '2025-12-31' properties: data: type: object properties: id: type: integer example: 210 type: type: string example: team entity_id: type: integer example: 15 label: type: string example: 'Team CúChulainn' scenario_id: type: integer example: 1 parent_id: type: integer example: 202 valid_from: type: string example: '2025-01-01' valid_to: type: string example: '2025-12-31' tags: - 'Org Units' requestBody: required: true content: application/json: schema: type: object properties: type: type: string description: 'Org unit type.' example: team entity_id: type: integer description: 'Linked entity ID.' example: 15 label: type: string description: 'Optional display label.' example: 'Team CúChulainn' nullable: true scenario_id: type: integer description: 'Scenario identifier.' example: 1 nullable: true parent_id: type: integer description: 'Parent org unit ID.' example: 202 nullable: true valid_from: type: string description: 'The start date. Format: YYYY-MM-DD.' example: '2025-01-01' valid_to: type: string description: 'The end date. Format: YYYY-MM-DD.' example: '2025-12-31' nullable: true required: - type - entity_id - scenario_id - valid_from delete: summary: 'Soft delete an org unit.' operationId: softDeleteAnOrgUnit description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required The tenant slug.' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - 'Org Units — Structure' requestBody: required: true content: application/json: schema: type: object properties: target_parent_id: type: integer description: 'New parent org unit id (nullable for root). The id of an existing record in the org_units table.' example: 101 nullable: true position: type: integer description: 'Desired index among siblings (0-based). Must be at least 0.' example: 2 nullable: true expected_updated_at: type: string description: 'ISO8601 timestamp.' example: ab required: - expected_updated_at parameters: - in: path name: id description: 'The ID of the org unit.' example: 11 required: true schema: type: integer - in: path name: org_unit description: 'The ID of the org unit.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit}/children': get: summary: "List an org unit's immediate children." operationId: listAnOrgUnitsImmediateChildren description: '' parameters: - in: query name: as_of description: 'date Format YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'date Format YYYY-MM-DD.' example: '2025-09-01' nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 210 label: 'Team CúChulainn' type: team entity_id: 15 rollup: direct_headcount: 8 subtree_headcount: 8 direct_monthly_cost: value: 428500 formatted: '€4,285.00' currency: EUR subtree_monthly_cost: value: 428500 formatted: '€4,285.00' currency: EUR direct_monthly_cost_cents: 428500 subtree_monthly_cost_cents: 428500 velocity_sp: 18 velocity_team_count: 1 children: [] properties: data: type: array example: - id: 210 label: 'Team CúChulainn' type: team entity_id: 15 rollup: direct_headcount: 8 subtree_headcount: 8 direct_monthly_cost: value: 428500 formatted: '€4,285.00' currency: EUR subtree_monthly_cost: value: 428500 formatted: '€4,285.00' currency: EUR direct_monthly_cost_cents: 428500 subtree_monthly_cost_cents: 428500 velocity_sp: 18 velocity_team_count: 1 children: [] items: type: object properties: id: type: integer example: 210 label: type: string example: 'Team CúChulainn' type: type: string example: team entity_id: type: integer example: 15 rollup: type: object properties: direct_headcount: type: integer example: 8 subtree_headcount: type: integer example: 8 direct_monthly_cost: type: object properties: value: type: integer example: 428500 formatted: type: string example: '€4,285.00' currency: type: string example: EUR subtree_monthly_cost: type: object properties: value: type: integer example: 428500 formatted: type: string example: '€4,285.00' currency: type: string example: EUR direct_monthly_cost_cents: type: integer example: 428500 subtree_monthly_cost_cents: type: integer example: 428500 velocity_sp: type: integer example: 18 velocity_team_count: type: integer example: 1 children: type: array example: [] tags: - 'Org Units' parameters: - in: path name: org_unit description: 'The ID of the org unit.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit}/metrics': get: summary: 'Show the cached aggregate metrics for a single org unit.' operationId: showTheCachedAggregateMetricsForASingleOrgUnit description: '' parameters: - in: query name: as_of description: 'date The date to compute metrics as of. Dates after today are projected from dated placements, pay and approved deployments.' example: '2025-09-01' required: false schema: type: string description: 'date The date to compute metrics as of. Dates after today are projected from dated placements, pay and approved deployments.' example: '2025-09-01' nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: as_of: '2025-09-01' direct_headcount: 8 subtree_headcount: 37 direct_monthly_cost_cents: 1250000 subtree_monthly_cost_cents: 4185000 metrics: span_of_control: 2 properties: data: type: object properties: as_of: type: string example: '2025-09-01' direct_headcount: type: integer example: 8 subtree_headcount: type: integer example: 37 direct_monthly_cost_cents: type: integer example: 1250000 subtree_monthly_cost_cents: type: integer example: 4185000 metrics: type: object properties: span_of_control: type: integer example: 2 tags: - 'Org Units — Metrics' parameters: - in: path name: org_unit description: 'The org unit id.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit_id}/move': post: summary: 'Move an org unit to a new parent.' operationId: moveAnOrgUnitToANewParent description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required The tenant slug.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 202 label: 'Retail – Ireland' code: null type: node entity_id: 4102 parent_id: 88 scenario_id: 1 valid_from: '2025-01-01' valid_to: null created_at: '2025-08-20T10:00:00Z' properties: data: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: integer example: 88 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-08-20T10:00:00Z' tags: - 'Org Units — Structure' requestBody: required: true content: application/json: schema: type: object properties: target_parent_id: type: integer description: 'nullable New parent org unit id.' example: 11 nullable: true position: type: integer description: 'The desired index among siblings.' example: 11 nullable: true expected_updated_at: type: string description: 'ISO8601 timestamp for optimistic concurrency.' example: ab required: - expected_updated_at parameters: - in: path name: org_unit_id description: 'The ID of the org unit.' example: 11 required: true schema: type: integer - in: path name: org_unit description: 'The org unit id being moved.' example: 202 required: true schema: type: integer /api/v1/org-units/merge: post: summary: 'Merge two org units.' operationId: mergeTwoOrgUnits description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required The tenant slug.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 202 label: 'Retail – Ireland' code: null type: node entity_id: 4102 parent_id: 101 scenario_id: 1 valid_from: '2025-01-01' valid_to: null created_at: '2025-08-20T10:00:00Z' properties: data: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-08-20T10:00:00Z' tags: - 'Org Units — Structure' requestBody: required: true content: application/json: schema: type: object properties: source_id: type: integer description: 'The source unit to merge from.' example: 11 target_id: type: integer description: 'The target unit to merge into.' example: 11 strategy: type: string description: 'The merge strategy.' example: ab nullable: true expected_updated_at_source: type: string description: 'Optimistic concurrency timestamp for the source.' example: '2025-08-29T10:00:00Z' nullable: true expected_updated_at_target: type: string description: 'Optimistic concurrency timestamp for the target.' example: '2025-08-29T10:00:00Z' nullable: true required: - source_id - target_id '/api/v1/org-units/{org_unit_id}/split': post: summary: 'Split an org unit.' operationId: splitAnOrgUnit description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required The tenant slug.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: original: id: 202 label: 'Retail – Ireland' code: null type: node entity_id: 4102 parent_id: 101 scenario_id: 1 valid_from: '2025-01-01' valid_to: null created_at: '2025-08-20T10:00:00Z' created: id: 244 label: 'Payments – Ops' code: null type: node entity_id: 4102 parent_id: 101 scenario_id: 1 valid_from: '2025-01-01' valid_to: null created_at: '2026-03-07T10:30:00Z' properties: data: type: object properties: original: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-08-20T10:00:00Z' created: type: object properties: id: type: integer example: 244 label: type: string example: 'Payments – Ops' code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true created_at: type: string example: '2026-03-07T10:30:00Z' tags: - 'Org Units — Structure' requestBody: required: true content: application/json: schema: type: object properties: mode: type: string description: 'new_sibling or new_child.' example: ab label: type: string description: 'Label for the new unit.' example: ab nullable: true children_to_move: type: array description: 'IDs of children to move.' example: - ab items: type: string expected_updated_at: type: string description: 'ISO8601 timestamp.' example: ab required: - mode - children_to_move - expected_updated_at parameters: - in: path name: org_unit_id description: 'The ID of the org unit.' example: 11 required: true schema: type: integer - in: path name: org_unit description: 'The org unit id being split.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit_id}/restore': post: summary: 'Restore an org unit.' operationId: restoreAnOrgUnit description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required The tenant slug.' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 202 label: 'Retail – Ireland' code: null type: node entity_id: 4102 parent_id: 101 scenario_id: 1 valid_from: '2025-01-01' valid_to: null created_at: '2025-08-20T10:00:00Z' properties: data: type: object properties: id: type: integer example: 202 label: type: string example: 'Retail – Ireland' code: type: string example: null nullable: true type: type: string example: node entity_id: type: integer example: 4102 parent_id: type: integer example: 101 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-01' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-08-20T10:00:00Z' tags: - 'Org Units — Structure' parameters: - in: path name: org_unit_id description: 'The ID of the org unit.' example: 11 required: true schema: type: integer - in: path name: org_unit description: 'The soft-deleted org unit id.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit}/velocity': get: summary: 'Get velocity for an org unit.' operationId: getVelocityForAnOrgUnit description: '' parameters: - in: query name: sprint_id description: 'The sprint to query.' example: 11 required: false schema: type: integer description: 'The sprint to query.' example: 11 nullable: true - in: query name: as_of description: 'date Mutually exclusive with sprint_id.' example: ab required: false schema: type: string description: 'date Mutually exclusive with sprint_id.' example: ab nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: org_unit_id: 202 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' velocity_sp: 88 teams_count: 4 properties: data: type: object properties: org_unit_id: type: integer example: 202 sprint_id: type: integer example: 42 period: type: object properties: label: type: string example: 'Sprint 18' starts_at: type: string example: '2025-09-01' ends_at: type: string example: '2025-09-14' velocity_sp: type: integer example: 88 teams_count: type: integer example: 4 tags: - 'Org Units — Velocity' parameters: - in: path name: org_unit description: 'The org unit ID.' example: 202 required: true schema: type: integer '/api/v1/org-units/{org_unit}/velocity/series': get: summary: 'Get velocity series for an org unit.' operationId: getVelocitySeriesForAnOrgUnit description: '' parameters: - in: query name: from_sprint_id description: 'Starting sprint ID. The id of an existing record in the sprint_periods table.' example: 1 required: false schema: type: integer description: 'Starting sprint ID. The id of an existing record in the sprint_periods table.' example: 1 nullable: true - in: query name: to_sprint_id description: 'Ending sprint ID. The id of an existing record in the sprint_periods table.' example: 10 required: false schema: type: integer description: 'Ending sprint ID. The id of an existing record in the sprint_periods table.' example: 10 nullable: true - in: query name: from description: 'Start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-01-01' required: false schema: type: string description: 'Start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-01-01' nullable: true - in: query name: to description: 'End date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-06-01' required: false schema: type: string description: 'End date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-06-01' nullable: true - in: query name: limit description: 'Maximum number of sprints to return. Must be at least 1. Must not be greater than 50.' example: 12 required: false schema: type: integer description: 'Maximum number of sprints to return. Must be at least 1. Must not be greater than 50.' example: 12 nullable: true - in: query name: order description: 'Sort order by sprint start.' example: asc required: false schema: type: string description: 'Sort order by sprint start.' example: asc enum: - asc - desc nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - org_unit_id: 202 sprint_id: 41 period: label: 'Sprint 17' starts_at: '2025-08-18' ends_at: '2025-08-31' velocity_sp: 81 teams_count: 4 - org_unit_id: 202 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' velocity_sp: 88 teams_count: 4 properties: data: type: array example: - org_unit_id: 202 sprint_id: 41 period: label: 'Sprint 17' starts_at: '2025-08-18' ends_at: '2025-08-31' velocity_sp: 81 teams_count: 4 - org_unit_id: 202 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' velocity_sp: 88 teams_count: 4 items: type: object properties: org_unit_id: type: integer example: 202 sprint_id: type: integer example: 41 period: type: object properties: label: type: string example: 'Sprint 17' starts_at: type: string example: '2025-08-18' ends_at: type: string example: '2025-08-31' velocity_sp: type: integer example: 81 teams_count: type: integer example: 4 tags: - 'Org Units — Velocity' parameters: - in: path name: org_unit description: 'The org unit ID.' example: 202 required: true schema: type: integer /api/v1/me/organisations: get: summary: 'List Organisation Accounts available to the authenticated user.' operationId: listOrganisationAccountsAvailableToTheAuthenticatedUser description: 'Returns the Organisation Accounts that the authenticated user can access.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 12 name: 'Acme Product' slug: acme-product plan_code: team subscription_status: trialing trial_ends_at: '2026-03-06T12:00:00Z' onboarding_completed_at: null role: owner properties: data: type: array example: - id: 12 name: 'Acme Product' slug: acme-product plan_code: team subscription_status: trialing trial_ends_at: '2026-03-06T12:00:00Z' onboarding_completed_at: null role: owner items: type: object properties: id: type: integer example: 12 name: type: string example: 'Acme Product' slug: type: string example: acme-product plan_code: type: string example: team subscription_status: type: string example: trialing trial_ends_at: type: string example: '2026-03-06T12:00:00Z' onboarding_completed_at: type: string example: null nullable: true role: type: string example: owner tags: - 'Organisation Accounts' /api/v1/organisations/current: get: summary: 'Get the current Organisation Account details.' operationId: getTheCurrentOrganisationAccountDetails description: 'Resolution follows the standard precedence (subdomain, last-used Organisation, session fallback).' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: id: 12 name: 'Acme Product' slug: acme-product plan_code: team onboarding_choice: import onboarding_completed_at: null trial_started_at: '2026-02-21T12:00:00Z' trial_ends_at: '2026-03-06T12:00:00Z' subscription_status: trialing grace_ends_at: null locked_at: null role: owner saml_enabled: false current_token: abilities: - cli.access - actors.read - scenarios.read cli_access: true properties: data: type: object properties: id: type: integer example: 12 description: 'Organisation Account identifier.' name: type: string example: 'Acme Product' description: 'Organisation Account display name.' slug: type: string example: acme-product description: 'Organisation Account slug.' plan_code: type: string example: team description: 'Active subscription plan code.' onboarding_choice: type: string example: import description: 'Selected onboarding path (`hris`, `import`, `demo`, `empty`).' onboarding_completed_at: type: string example: null description: 'ISO-8601 onboarding completion timestamp.' trial_started_at: type: string example: '2026-02-21T12:00:00Z' description: 'ISO-8601 trial start timestamp.' trial_ends_at: type: string example: '2026-03-06T12:00:00Z' description: 'ISO-8601 trial end timestamp.' subscription_status: type: string example: trialing description: 'Current lifecycle state.' grace_ends_at: type: string example: null description: 'ISO-8601 grace-period end timestamp.' locked_at: type: string example: null description: 'ISO-8601 lock timestamp.' role: type: string example: owner description: 'Membership role for the authenticated user in this Organisation Account.' saml_enabled: type: boolean example: false description: 'Included only when the user has `sso.manage` capability and the tenant plan includes tenant-managed SAML.' current_token: type: object properties: abilities: type: array example: - cli.access - actors.read - scenarios.read description: 'Normalized abilities/scopes declared on the current bearer token.' items: type: string cli_access: type: boolean example: true description: 'Whether the current bearer token explicitly includes `cli.access`.' 404: description: '' content: application/json: schema: type: object example: code: tenant.not_resolved message: 'No Organisation Account is currently selected.' properties: code: type: string example: tenant.not_resolved message: type: string example: 'No Organisation Account is currently selected.' tags: - 'Organisation Accounts' /api/v1/organisations/current/onboarding-choice: post: summary: 'Update onboarding choice for the current Organisation Account.' operationId: updateOnboardingChoiceForTheCurrentOrganisationAccount description: 'Persists how this Organisation Account will start onboarding.' parameters: [] responses: 200: description: '' content: application/json: schema: type: object example: data: id: 12 name: 'Acme Product' slug: acme-product onboarding_choice: import subscription_status: trialing properties: data: type: object properties: id: type: integer example: 12 description: 'Organisation Account identifier.' name: type: string example: 'Acme Product' description: 'Organisation Account display name.' slug: type: string example: acme-product description: 'Organisation Account slug.' onboarding_choice: type: string example: import description: 'Persisted onboarding choice for the current Organisation Account.' subscription_status: type: string example: trialing description: 'Current lifecycle state.' 404: description: '' content: application/json: schema: type: object example: code: tenant.not_resolved message: 'No Organisation Account is currently selected.' properties: code: type: string example: tenant.not_resolved message: type: string example: 'No Organisation Account is currently selected.' tags: - 'Organisation Accounts' requestBody: required: true content: application/json: schema: type: object properties: onboarding_choice: type: string description: 'How this Organisation Account will add data first.' example: import enum: - hris - import - demo - empty required: - onboarding_choice /api/v1/positions: get: summary: 'List positions with pagination, filters, and search.' operationId: listPositionsWithPaginationFiltersAndSearch description: "Returns baseline data when no scenario filter is supplied; pass\n`filter[scenario]=current` to reflect the active scenario, or pass\na tenant scenario slug such as `sandbox`." parameters: - in: query name: page description: 'The page number to return.' example: 1 required: false schema: type: integer description: 'The page number to return.' example: 1 - in: query name: per_page description: 'Results per page (max 100).' example: 25 required: false schema: type: integer description: 'Results per page (max 100).' example: 25 - in: query name: scenario description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan required: false schema: type: string description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan - in: query name: 'filter[scenario_id]' description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' required: false schema: type: string description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' - in: query name: 'filter[scenario]' description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan required: false schema: type: string description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,valid_from,valid_to,fte,created_at.' example: '-valid_from' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,valid_from,valid_to,fte,created_at.' example: '-valid_from' - in: query name: 'filter[org_unit_team_id]' description: 'Filter by a budgeted team org unit (planning metadata only).' example: 4201 required: false schema: type: integer description: 'Filter by a budgeted team org unit (planning metadata only).' example: 4201 - in: query name: 'filter[target_employment_type]' description: 'Restrict by budgeted employment type (perm or contractor).' example: perm required: false schema: type: string description: 'Restrict by budgeted employment type (perm or contractor).' example: perm - in: query name: 'filter[active_as_of]' description: 'date Return positions active on this ISO date (YYYY-MM-DD).' example: '2025-09-20' required: false schema: type: string description: 'date Return positions active on this ISO date (YYYY-MM-DD).' example: '2025-09-20' - in: query name: q description: 'Free-text search across title, team name, and actor name.' example: 'platform engineer' required: false schema: type: string description: 'Free-text search across title, team name, and actor name.' example: 'platform engineer' - in: query name: include description: 'Comma-separated relationships. Allowed: team,actor.' example: 'team,actor' required: false schema: type: string description: 'Comma-separated relationships. Allowed: team,actor.' example: 'team,actor' - in: query name: 'fields[positions]' description: 'Sparse fieldset selection.' example: 'id,title,team' required: false schema: type: string description: 'Sparse fieldset selection.' example: 'id,title,team' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 17 org_unit_team_id: 4201 actor_id: null title: 'Staff Platform Engineer' target_employment_type: perm fte: 1 target_annual_salary_cents: 11850000 target_daily_rate_cents: null target_currency: EUR valid_from: '2024-03-01' valid_to: null scenario_id: null meta: cost_centre: ENG-PLAT notes: 'Supports platform migration.' team: id: 8 name: 'Platform Experience' slug: platform-experience actor: null budget: employment_type: perm fte: 1 annual_salary_cents: 11850000 daily_rate_cents: null currency: EUR links: self: 'https://tenant.orgonaut.test/api/v1/positions/17' team: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: null created_at: '2025-09-19T09:00:00Z' updated_at: '2025-09-19T09:00:00Z' links: first: 'https://tenant.orgonaut.test/api/v1/positions?page=1' last: 'https://tenant.orgonaut.test/api/v1/positions?page=3' prev: null next: 'https://tenant.orgonaut.test/api/v1/positions?page=2' meta: current_page: 1 from: 1 last_page: 3 path: 'https://tenant.orgonaut.test/api/v1/positions' per_page: 25 to: 25 total: 62 properties: data: type: array example: - id: 17 org_unit_team_id: 4201 actor_id: null title: 'Staff Platform Engineer' target_employment_type: perm fte: 1 target_annual_salary_cents: 11850000 target_daily_rate_cents: null target_currency: EUR valid_from: '2024-03-01' valid_to: null scenario_id: null meta: cost_centre: ENG-PLAT notes: 'Supports platform migration.' team: id: 8 name: 'Platform Experience' slug: platform-experience actor: null budget: employment_type: perm fte: 1 annual_salary_cents: 11850000 daily_rate_cents: null currency: EUR links: self: 'https://tenant.orgonaut.test/api/v1/positions/17' team: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: null created_at: '2025-09-19T09:00:00Z' updated_at: '2025-09-19T09:00:00Z' items: type: object properties: id: type: integer example: 17 org_unit_team_id: type: integer example: 4201 actor_id: type: string example: null nullable: true title: type: string example: 'Staff Platform Engineer' target_employment_type: type: string example: perm fte: type: integer example: 1 target_annual_salary_cents: type: integer example: 11850000 target_daily_rate_cents: type: string example: null nullable: true target_currency: type: string example: EUR valid_from: type: string example: '2024-03-01' valid_to: type: string example: null nullable: true scenario_id: type: string example: null nullable: true meta: type: object properties: cost_centre: type: string example: ENG-PLAT notes: type: string example: 'Supports platform migration.' team: type: object properties: id: type: integer example: 8 name: type: string example: 'Platform Experience' slug: type: string example: platform-experience actor: type: string example: null nullable: true budget: type: object properties: employment_type: type: string example: perm fte: type: integer example: 1 annual_salary_cents: type: integer example: 11850000 daily_rate_cents: type: string example: null nullable: true currency: type: string example: EUR links: type: object properties: self: type: string example: 'https://tenant.orgonaut.test/api/v1/positions/17' team: type: string example: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: type: string example: null nullable: true created_at: type: string example: '2025-09-19T09:00:00Z' updated_at: type: string example: '2025-09-19T09:00:00Z' links: type: object properties: first: type: string example: 'https://tenant.orgonaut.test/api/v1/positions?page=1' last: type: string example: 'https://tenant.orgonaut.test/api/v1/positions?page=3' prev: type: string example: null nullable: true next: type: string example: 'https://tenant.orgonaut.test/api/v1/positions?page=2' meta: type: object properties: current_page: type: integer example: 1 from: type: integer example: 1 last_page: type: integer example: 3 path: type: string example: 'https://tenant.orgonaut.test/api/v1/positions' per_page: type: integer example: 25 to: type: integer example: 25 total: type: integer example: 62 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported filter[scenario]: future' details: allowed: - baseline - live - current - '{id}' - '{slug}' properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported filter[scenario]: future' details: type: object properties: allowed: type: array example: - baseline - live - current - '{id}' - '{slug}' items: type: string tags: - Positions post: summary: 'Create a position within the active scenario.' operationId: createAPositionWithinTheActiveScenario description: "Applies baseline defaults when the current scenario guard is baseline,\notherwise stores the record against the active scenario id." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 24 org_unit_team_id: 4201 actor_id: null title: 'Staff Platform Engineer' target_employment_type: perm fte: 1 target_annual_salary_cents: 11850000 target_daily_rate_cents: null target_currency: EUR valid_from: '2024-03-01' valid_to: null scenario_id: null meta: cost_centre: ENG-PLAT notes: 'Supports platform migration.' team: id: 8 name: 'Platform Experience' slug: platform-experience actor: null budget: employment_type: perm fte: 1 annual_salary_cents: 11850000 daily_rate_cents: null currency: EUR links: self: 'https://tenant.orgonaut.test/api/v1/positions/24' team: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: null created_at: '2025-09-19T09:15:00Z' updated_at: '2025-09-19T09:15:00Z' properties: data: type: object properties: id: type: integer example: 24 description: 'Newly created position id.' org_unit_team_id: type: integer example: 4201 description: 'Budgeted team org unit id (planning metadata only; null when unassigned).' actor_id: type: string example: null description: 'Assigned actor id when filled.' title: type: string example: 'Staff Platform Engineer' description: 'Role title displayed in listings.' target_employment_type: type: string example: perm description: 'Budgeted employment type (perm or contractor).' fte: type: integer example: 1 description: 'Full-time equivalent allocation.' target_annual_salary_cents: type: integer example: 11850000 description: 'Budgeted annual salary when target_employment_type is perm.' target_daily_rate_cents: type: string example: null description: 'Budgeted daily rate when target_employment_type is contractor.' target_currency: type: string example: EUR description: 'ISO-4217 currency code for the budget target.' valid_from: type: string example: '2024-03-01' description: 'Effective start date (YYYY-MM-DD).' valid_to: type: string example: null description: 'Effective end date when specified.' scenario_id: type: string example: null description: 'Scenario identifier; null when stored in baseline.' meta: type: object properties: cost_centre: type: string example: ENG-PLAT notes: type: string example: 'Supports platform migration.' description: 'Arbitrary metadata supplied during creation.' team: type: object properties: id: type: integer example: 8 name: type: string example: 'Platform Experience' slug: type: string example: platform-experience actor: type: string example: null nullable: true budget: type: object properties: employment_type: type: string example: perm fte: type: integer example: 1 annual_salary_cents: type: integer example: 11850000 daily_rate_cents: type: string example: null nullable: true currency: type: string example: EUR description: 'Nested budget details including salary and employment type.' links: type: object properties: self: type: string example: 'https://tenant.orgonaut.test/api/v1/positions/24' team: type: string example: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: type: string example: null nullable: true description: 'Hypermedia links to related resources.' created_at: type: string example: '2025-09-19T09:15:00Z' description: 'ISO-8601 timestamp for creation.' updated_at: type: string example: '2025-09-19T09:15:00Z' description: 'ISO-8601 timestamp for the latest update.' 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: target_annual_salary_cents: - 'Budgeted annual salary applies to permanent roles only.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: target_annual_salary_cents: type: array example: - 'Budgeted annual salary applies to permanent roles only.' items: type: string tags: - Positions requestBody: required: true content: application/json: schema: type: object properties: title: type: string description: 'The seat title.' example: ab role_type_id: type: integer description: 'The id of an existing record in the role_types table.' example: 11 nullable: true org_unit_team_id: type: integer description: 'The budgeted team for this seat (planning metadata only; does not assign an actor).' example: 4201 nullable: true target_employment_type: type: string description: 'The budgeted employment type (e.g., perm|contractor).' example: ab nullable: true fte: type: number description: 'Full-time equivalent allocation between 0.01 and 2.0. Must be between 0.01 and 2.00.' example: 1.0 target_annual_salary_cents: type: integer description: 'Budgeted annual salary in cents for permanent roles. Must be at least 0.' example: 8500000 nullable: true target_daily_rate_cents: type: integer description: 'Budgeted daily rate in cents for contractor roles. Must be at least 0.' example: 45000 nullable: true target_currency: type: string description: 'Three-letter currency code defined in Settings → Currencies. The code of an existing record in the currencies table. Must be 3 characters.' example: EUR nullable: true valid_from: type: string description: 'Effective start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-09-20' valid_to: type: string description: 'Effective end date (optional, YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to valid_from.' example: '2026-03-31' nullable: true meta: type: object description: 'Arbitrary structured metadata for the position.' example: cost_centre: ENG-PLAT properties: { } nullable: true create_placement: type: boolean description: 'Set to false to prevent auto-creating a placement when linking this record to downstream observers.' example: true nullable: true tenant_id: type: integer description: '' example: 11 scenario_id: type: integer description: '' example: 11 nullable: true required: - title - fte - valid_from - tenant_id '/api/v1/positions/{id}': get: summary: 'Display a specific position.' operationId: displayASpecificPosition description: "Honors the active scenario guard unless `filter[scenario]`/`filter[scenario_id]`\nwere provided on the index request and cached via query options middleware." parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: team,actor.' example: team required: false schema: type: string description: 'Comma-separated relationships. Allowed: team,actor.' example: team - in: query name: 'fields[positions]' description: 'Sparse fieldset.' example: 'id,title,team' required: false schema: type: string description: 'Sparse fieldset.' example: 'id,title,team' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 24 org_unit_team_id: 4201 actor_id: null title: 'Staff Platform Engineer' target_employment_type: perm fte: 1 target_annual_salary_cents: 11850000 target_daily_rate_cents: null target_currency: EUR valid_from: '2024-03-01' valid_to: null scenario_id: null meta: cost_centre: ENG-PLAT notes: 'Supports platform migration.' team: id: 8 name: 'Platform Experience' slug: platform-experience actor: null budget: employment_type: perm fte: 1 annual_salary_cents: 11850000 daily_rate_cents: null currency: EUR links: self: 'https://tenant.orgonaut.test/api/v1/positions/24' team: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: null created_at: '2025-09-19T09:15:00Z' updated_at: '2025-09-19T09:15:00Z' properties: data: type: object properties: id: type: integer example: 24 description: 'Position identifier.' org_unit_team_id: type: integer example: 4201 description: 'Budgeted team org unit id (planning metadata only; null when unassigned).' actor_id: type: string example: null description: 'Assigned actor id when filled.' title: type: string example: 'Staff Platform Engineer' description: 'Role title displayed in listings.' target_employment_type: type: string example: perm description: 'Budgeted employment type (perm or contractor).' fte: type: integer example: 1 description: 'Full-time equivalent allocation.' target_annual_salary_cents: type: integer example: 11850000 description: 'Budgeted annual salary in cents.' target_daily_rate_cents: type: string example: null description: 'Budgeted daily rate in cents.' target_currency: type: string example: EUR description: 'ISO-4217 currency code for the budget target.' valid_from: type: string example: '2024-03-01' description: 'Effective start date (YYYY-MM-DD).' valid_to: type: string example: null description: 'Effective end date (YYYY-MM-DD).' scenario_id: type: string example: null description: 'Scenario identifier when not baseline.' meta: type: object properties: cost_centre: type: string example: ENG-PLAT notes: type: string example: 'Supports platform migration.' description: 'Arbitrary structured metadata payload.' team: type: object properties: id: type: integer example: 8 name: type: string example: 'Platform Experience' slug: type: string example: platform-experience description: 'Team details when requested via include.' actor: type: string example: null description: 'Actor summary when requested via include.' budget: type: object properties: employment_type: type: string example: perm fte: type: integer example: 1 annual_salary_cents: type: integer example: 11850000 daily_rate_cents: type: string example: null nullable: true currency: type: string example: EUR description: 'Nested budget details including salary and employment type.' links: type: object properties: self: type: string example: 'https://tenant.orgonaut.test/api/v1/positions/24' team: type: string example: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: type: string example: null nullable: true description: 'Hypermedia links for related resources.' created_at: type: string example: '2025-09-19T09:15:00Z' description: 'ISO-8601 timestamp for creation.' updated_at: type: string example: '2025-09-19T09:15:00Z' description: 'ISO-8601 timestamp for the latest update.' tags: - Positions put: summary: 'Update a position.' operationId: updateAPosition description: "Validation enforces mutual exclusivity between salary and daily rate,\nmirroring the create endpoint rules." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 24 org_unit_team_id: 4201 actor_id: null title: 'Principal Platform Engineer' target_employment_type: perm fte: 1 target_annual_salary_cents: 12600000 target_daily_rate_cents: null target_currency: EUR valid_from: '2024-03-01' valid_to: null scenario_id: null meta: cost_centre: ENG-PLAT notes: 'Role upgraded to lead migration.' team: id: 8 name: 'Platform Experience' slug: platform-experience actor: null budget: employment_type: perm fte: 1 annual_salary_cents: 12600000 daily_rate_cents: null currency: EUR links: self: 'https://tenant.orgonaut.test/api/v1/positions/24' team: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: null created_at: '2025-09-19T09:15:00Z' updated_at: '2025-09-26T11:20:00Z' properties: data: type: object properties: id: type: integer example: 24 description: 'Position identifier.' org_unit_team_id: type: integer example: 4201 description: 'Budgeted team org unit id (planning metadata only; null when unassigned).' actor_id: type: string example: null description: 'Assigned actor id when filled.' title: type: string example: 'Principal Platform Engineer' description: 'Role title displayed in listings.' target_employment_type: type: string example: perm description: 'Budgeted employment type (perm or contractor).' fte: type: integer example: 1 description: 'Full-time equivalent allocation.' target_annual_salary_cents: type: integer example: 12600000 description: 'Budgeted annual salary in cents.' target_daily_rate_cents: type: string example: null description: 'Budgeted daily rate in cents.' target_currency: type: string example: EUR description: 'ISO-4217 currency code for the budget target.' valid_from: type: string example: '2024-03-01' description: 'Effective start date (YYYY-MM-DD).' valid_to: type: string example: null description: 'Effective end date.' scenario_id: type: string example: null description: 'Scenario identifier; null for baseline.' meta: type: object properties: cost_centre: type: string example: ENG-PLAT notes: type: string example: 'Role upgraded to lead migration.' description: 'Arbitrary structured metadata payload.' team: type: object properties: id: type: integer example: 8 name: type: string example: 'Platform Experience' slug: type: string example: platform-experience actor: type: string example: null nullable: true budget: type: object properties: employment_type: type: string example: perm fte: type: integer example: 1 annual_salary_cents: type: integer example: 12600000 daily_rate_cents: type: string example: null nullable: true currency: type: string example: EUR description: 'Nested budget details including salary and employment type.' links: type: object properties: self: type: string example: 'https://tenant.orgonaut.test/api/v1/positions/24' team: type: string example: 'https://tenant.orgonaut.test/api/v1/teams/8' actor: type: string example: null nullable: true description: 'Hypermedia links for related resources.' created_at: type: string example: '2025-09-19T09:15:00Z' description: 'ISO-8601 timestamp for creation.' updated_at: type: string example: '2025-09-26T11:20:00Z' description: 'ISO-8601 timestamp for the latest update.' 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: target_daily_rate_cents: - 'Budgeted daily rate applies to contractor roles only.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: target_daily_rate_cents: type: array example: - 'Budgeted daily rate applies to contractor roles only.' items: type: string tags: - Positions requestBody: required: true content: application/json: schema: type: object properties: title: type: string description: 'The seat title.' example: ab role_type_id: type: integer description: 'The id of an existing record in the role_types table.' example: 11 nullable: true org_unit_team_id: type: integer description: 'The budgeted team for this seat (planning metadata only; does not assign an actor).' example: 4201 nullable: true target_employment_type: type: string description: 'The budgeted employment type (e.g., perm|contractor).' example: ab nullable: true fte: type: number description: 'Full-time equivalent allocation between 0.01 and 2.0. Must be between 0.01 and 2.00.' example: 1.0 target_annual_salary_cents: type: integer description: 'Budgeted annual salary in cents for permanent roles. Must be at least 0.' example: 8500000 nullable: true target_daily_rate_cents: type: integer description: 'Budgeted daily rate in cents for contractor roles. Must be at least 0.' example: 45000 nullable: true target_currency: type: string description: 'Three-letter currency code defined in Settings → Currencies. The code of an existing record in the currencies table. Must be 3 characters.' example: EUR nullable: true valid_from: type: string description: 'Effective start date (YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d.' example: '2025-09-20' valid_to: type: string description: 'Effective end date (optional, YYYY-MM-DD). Must be a valid date. Must be a valid date in the format Y-m-d. Must be a date after or equal to valid_from.' example: '2026-03-31' nullable: true meta: type: object description: 'Arbitrary structured metadata for the position.' example: cost_centre: ENG-PLAT properties: { } nullable: true create_placement: type: boolean description: 'Set to false to prevent auto-creating a placement when linking this record to downstream observers.' example: true nullable: true tenant_id: type: integer description: '' example: 11 scenario_id: type: integer description: '' example: 11 nullable: true required: - title - fte - valid_from - tenant_id delete: summary: 'Soft delete a position.' operationId: softDeleteAPosition description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Positions parameters: - in: path name: id description: 'The ID of the position.' example: 11 required: true schema: type: integer - in: path name: position description: 'The ID of the position.' example: 24 required: true schema: type: integer /api/v1/role-types/search: get: summary: 'Search Role Types for the active tenant.' operationId: searchRoleTypesForTheActiveTenant description: "Returns up to 20 matching role types ordered by display order, level, then name.\nRequires `settings.manage` ability." parameters: - in: query name: q description: 'Search term applied to name and slug.' example: engineer required: false schema: type: string description: 'Search term applied to name and slug.' example: engineer - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: 'Senior Engineer' slug: senior-engineer level: 4 color: '#2563eb' description: null is_active: true display_order: 6 properties: data: type: array example: - id: 1 name: 'Senior Engineer' slug: senior-engineer level: 4 color: '#2563eb' description: null is_active: true display_order: 6 items: type: object properties: id: type: integer example: 1 name: type: string example: 'Senior Engineer' slug: type: string example: senior-engineer level: type: integer example: 4 color: type: string example: '#2563eb' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 6 tags: - 'Role Types' /api/v1/scenarios/tasks: get: summary: 'List scenario tasks.' operationId: listScenarioTasks description: "Returns scenario tasks for the authenticated tenant, ordered by latest first.\nThese tasks mirror the progress shown in the Flux UI at /scenarios/tasks." parameters: - in: query name: status description: 'Filter by status (pending, running, failed, complete).' example: running required: false schema: type: string description: 'Filter by status (pending, running, failed, complete).' example: running - in: query name: type description: 'Filter by task type (clone, promotion, snapshot, all). Defaults to clone for compatibility.' example: promotion required: false schema: type: string description: 'Filter by task type (clone, promotion, snapshot, all). Defaults to clone for compatibility.' example: promotion - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: array items: type: object properties: id: type: integer example: 81 scenario_id: type: integer example: 42 scenario_name: type: string example: 'Q2 Planning' type: type: string example: clone stage: type: string example: actors status: type: string example: running percent: type: integer example: 33 message: type: string example: 'Copying Actors...' error_message: type: string example: null nullable: true created_at: type: string example: '2025-11-01T12:14:22Z' updated_at: type: string example: '2025-11-01T12:15:03Z' example: - id: 81 scenario_id: 42 scenario_name: 'Q2 Planning' type: clone stage: actors status: running percent: 33 message: 'Copying Actors...' error_message: null created_at: '2025-11-01T12:14:22Z' updated_at: '2025-11-01T12:15:03Z' tags: - 'Scenario Tasks' /api/v1/scenarios: get: summary: 'Display a paginated listing of scenarios.' operationId: displayAPaginatedListingOfScenarios description: '' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' - in: query name: 'filter[status]' description: 'Filter by status.' example: cloning required: false schema: type: string description: 'Filter by status.' example: cloning - in: query name: 'filter[kind]' description: 'Filter by kind (planning, baseline_archive, applied_scenario).' example: planning required: false schema: type: string description: 'Filter by kind (planning, baseline_archive, applied_scenario).' example: planning - in: query name: 'fields[scenarios]' description: 'Sparse fields for scenarios.' example: 'id,name,status' required: false schema: type: string description: 'Sparse fields for scenarios.' example: 'id,name,status' - in: query name: include description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: 'teams,aggregate' required: false schema: type: string description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: 'teams,aggregate' - in: query name: as_of description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: 'FY26 Cost Optimisation' status: new links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 1 name: 'FY26 Cost Optimisation' status: new items: type: object properties: id: type: integer example: 1 name: type: string example: 'FY26 Cost Optimisation' status: type: string example: new links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - name - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - name - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - Scenarios post: summary: 'Create a new scenario.' operationId: createANewScenario description: 'Triggers a background clone pipeline and returns immediately with status=initialising.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 202: description: '' content: application/json: schema: type: object example: message: 'Scenario creation started. Cloning in progress.' status: initialising scenario: id: 42 name: 'Q2 Planning' status: initialising notes: 'Budget-cut planning run.' tasks_url: 'https://app.orgonaut.test/api/v1/scenarios/tasks' properties: message: type: string example: 'Scenario creation started. Cloning in progress.' status: type: string example: initialising scenario: type: object properties: id: type: integer example: 42 name: type: string example: 'Q2 Planning' status: type: string example: initialising notes: type: string example: 'Budget-cut planning run.' tasks_url: type: string example: 'https://app.orgonaut.test/api/v1/scenarios/tasks' 403: description: '' content: application/json: schema: type: object example: code: feature.unavailable message: 'Your current subscription plan does not include this feature.' properties: code: type: string example: feature.unavailable message: type: string example: 'Your current subscription plan does not include this feature.' 429: description: '' content: application/json: schema: type: object example: code: limit.scenarios message: 'Active planning scenario limit reached (1/1). Archive or apply an existing scenario, or upgrade your plan.' limit: 1 current: 1 properties: code: type: string example: limit.scenarios message: type: string example: 'Active planning scenario limit reached (1/1). Archive or apply an existing scenario, or upgrade your plan.' limit: type: integer example: 1 current: type: integer example: 1 tags: - Scenarios requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'The scenario name.' example: '"Q2 Planning"' notes: type: string description: 'Notes about the scenario.' example: '"Budget-cut planning run."' nullable: true source_scenario_id: type: integer description: 'The active scenario to clone from (null to clone from baseline).' example: 12 nullable: true required: - name '/api/v1/scenarios/{slug}': get: summary: 'Display the specified scenario.' operationId: displayTheSpecifiedScenario description: '' parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate required: false schema: type: string description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate - in: query name: as_of description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: 'FY26 Cost Optimisation' status: new aggregate: as_of_date: '2025-09-01' scenario_direct_headcount: 0 scenario_subtree_headcount: 0 scenario_direct_monthly_cost_cents: 0 scenario_subtree_monthly_cost_cents: 0 velocity_completed_sp: 0 velocity_team_count: 0 computed_at: '2025-09-06T12:00:00Z' metrics: { } properties: data: type: object properties: id: type: integer example: 1 name: type: string example: 'FY26 Cost Optimisation' status: type: string example: new aggregate: type: object properties: as_of_date: type: string example: '2025-09-01' scenario_direct_headcount: type: integer example: 0 scenario_subtree_headcount: type: integer example: 0 scenario_direct_monthly_cost_cents: type: integer example: 0 scenario_subtree_monthly_cost_cents: type: integer example: 0 velocity_completed_sp: type: integer example: 0 velocity_team_count: type: integer example: 0 computed_at: type: string example: '2025-09-06T12:00:00Z' metrics: type: object properties: { } tags: - Scenarios parameters: - in: path name: slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'The ID of the scenario.' example: 1 required: true schema: type: integer '/api/v1/scenarios/{scenario_slug}/delta': get: summary: 'Show changed-record deltas between a scenario and its comparison source.' operationId: showChangedRecordDeltasBetweenAScenarioAndItsComparisonSource description: '' parameters: - in: query name: compare description: 'Optional comparison source. Use "baseline" to compare against the live baseline instead of the scenario''s source.' example: baseline required: false schema: type: string description: 'Optional comparison source. Use "baseline" to compare against the live baseline instead of the scenario''s source.' example: baseline nullable: true - in: query name: as_of description: 'Optional comparison date (YYYY-MM-DD). Dates up to today use recorded snapshots; later dates are projected from dated placements, pay and approved deployments. Ignored for archived scenarios.' example: '2028-01-01' required: false schema: type: string description: 'Optional comparison date (YYYY-MM-DD). Dates up to today use recorded snapshots; later dates are projected from dated placements, pay and approved deployments. Ignored for archived scenarios.' example: '2028-01-01' nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: scenario: id: 7 name: 'FY26 Restructure' slug: fy26-restructure status: draft kind: planning notes: 'Budget-cut planning run.' live_from: null live_to: null locked_at: null archived_at: null is_read_only: false source_scenario_id: null created_at: '2026-03-01T09:00:00Z' updated_at: '2026-03-01T09:00:00Z' source: null source_label: 'Live baseline' as_of: null counts: added: 1 changed: 1 removed: 0 total: 2 families: - family: Teams counts: added: 1 changed: 0 removed: 0 total: 1 files: - 'team:44' - family: 'Scenario Metrics' counts: added: 0 changed: 1 removed: 0 total: 1 files: - 'metrics:summary' files: - key: 'team:44' family: Teams status: added title: 'Platform Enablement' path: teams/platform-enablement icon: users added_lines: 4 removed_lines: 0 activity_event_ids: - 311 hunks: - title: 'Team details' status: added lines: - type: added prefix: + field: Name content: 'Name: Platform Enablement' metrics: - key: 'metrics:summary' family: 'Scenario Metrics' status: changed title: 'Scenario metrics' path: metrics/summary icon: chart-bar added_lines: 1 removed_lines: 1 activity_event_ids: [] hunks: - title: Headcount status: changed lines: - type: removed prefix: '-' field: Headcount content: 'Headcount: 123' - type: added prefix: + field: Headcount content: 'Headcount: 118' properties: scenario: type: object properties: id: type: integer example: 7 name: type: string example: 'FY26 Restructure' slug: type: string example: fy26-restructure status: type: string example: draft kind: type: string example: planning notes: type: string example: 'Budget-cut planning run.' live_from: type: string example: null nullable: true live_to: type: string example: null nullable: true locked_at: type: string example: null nullable: true archived_at: type: string example: null nullable: true is_read_only: type: boolean example: false source_scenario_id: type: string example: null nullable: true created_at: type: string example: '2026-03-01T09:00:00Z' updated_at: type: string example: '2026-03-01T09:00:00Z' source: type: string example: null nullable: true source_label: type: string example: 'Live baseline' as_of: type: string example: null nullable: true counts: type: object properties: added: type: integer example: 1 changed: type: integer example: 1 removed: type: integer example: 0 total: type: integer example: 2 families: type: array example: - family: Teams counts: added: 1 changed: 0 removed: 0 total: 1 files: - 'team:44' - family: 'Scenario Metrics' counts: added: 0 changed: 1 removed: 0 total: 1 files: - 'metrics:summary' items: type: object properties: family: type: string example: Teams counts: type: object properties: added: type: integer example: 1 changed: type: integer example: 0 removed: type: integer example: 0 total: type: integer example: 1 files: type: array example: - 'team:44' items: type: string files: type: array example: - key: 'team:44' family: Teams status: added title: 'Platform Enablement' path: teams/platform-enablement icon: users added_lines: 4 removed_lines: 0 activity_event_ids: - 311 hunks: - title: 'Team details' status: added lines: - type: added prefix: + field: Name content: 'Name: Platform Enablement' items: type: object properties: key: type: string example: 'team:44' family: type: string example: Teams status: type: string example: added title: type: string example: 'Platform Enablement' path: type: string example: teams/platform-enablement icon: type: string example: users added_lines: type: integer example: 4 removed_lines: type: integer example: 0 activity_event_ids: type: array example: - 311 items: type: integer hunks: type: array example: - title: 'Team details' status: added lines: - type: added prefix: + field: Name content: 'Name: Platform Enablement' items: type: object properties: title: type: string example: 'Team details' status: type: string example: added lines: type: array example: - type: added prefix: + field: Name content: 'Name: Platform Enablement' items: type: object properties: type: type: string example: added prefix: type: string example: + field: type: string example: Name content: type: string example: 'Name: Platform Enablement' metrics: type: array example: - key: 'metrics:summary' family: 'Scenario Metrics' status: changed title: 'Scenario metrics' path: metrics/summary icon: chart-bar added_lines: 1 removed_lines: 1 activity_event_ids: [] hunks: - title: Headcount status: changed lines: - type: removed prefix: '-' field: Headcount content: 'Headcount: 123' - type: added prefix: + field: Headcount content: 'Headcount: 118' items: type: object properties: key: type: string example: 'metrics:summary' family: type: string example: 'Scenario Metrics' status: type: string example: changed title: type: string example: 'Scenario metrics' path: type: string example: metrics/summary icon: type: string example: chart-bar added_lines: type: integer example: 1 removed_lines: type: integer example: 1 activity_event_ids: type: array example: [] hunks: type: array example: - title: Headcount status: changed lines: - type: removed prefix: '-' field: Headcount content: 'Headcount: 123' - type: added prefix: + field: Headcount content: 'Headcount: 118' items: type: object properties: title: type: string example: Headcount status: type: string example: changed lines: type: array example: - type: removed prefix: '-' field: Headcount content: 'Headcount: 123' - type: added prefix: + field: Headcount content: 'Headcount: 118' items: type: object properties: type: type: string example: removed prefix: type: string example: '-' field: type: string example: Headcount content: type: string example: 'Headcount: 123' tags: - Scenarios parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'Scenario id or slug.' example: fy26-restructure required: true schema: type: string '/api/v1/scenarios/{scenario_slug}/timeline': get: summary: "Show a scenario's path against its comparison source on several dates." operationId: showAScenariosPathAgainstItsComparisonSourceOnSeveralDates description: "One call returns headcount, FTE and monthly cost (total, people and agent)\nfor the scenario and its source on each date, plus the difference, so an\nagent can draw the path without calling the delta once per date. Dates up\nto today read recorded snapshots; later dates are projected from dated\nplacements, pay records and approved AI deployments. Cost figures need\n`costs.read` and are otherwise removed." parameters: - in: query name: compare description: 'Optional comparison source. Use "baseline" to compare against the live baseline instead of the scenario''s source.' example: baseline required: false schema: type: string description: 'Optional comparison source. Use "baseline" to compare against the live baseline instead of the scenario''s source.' example: baseline enum: - baseline nullable: true - in: query name: dates description: 'Must be a valid date in the format Y-m-d. Must be a date after or equal to 1970-01-01. Must be a date before or equal to 2076-10-11.' example: - '2039-11-03' required: false schema: type: array description: 'Must be a valid date in the format Y-m-d. Must be a date after or equal to 1970-01-01. Must be a date before or equal to 2076-10-11.' example: - '2039-11-03' items: type: string - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: scenario: id: 7 name: 'Path to 2030' slug: path-to-2030 source: null source_label: 'Live baseline' has_dated_changes: true last_change: '2030-06-30' points: - date: '2026-09-27' label: Today projected: false scenario: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 source: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 difference: headcount: 0 fte: 0 cost: 0 people_cost: 0 agent_cost: 0 - date: '2028-01-01' label: '2028' projected: true scenario: headcount: 936 fte: 903.1 cost: 890000000 people_cost: 868000000 agent_cost: 22000000 source: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 difference: headcount: -64 fte: -59.3 cost: -44000000 people_cost: -65000000 agent_cost: 21000000 properties: scenario: type: object properties: id: type: integer example: 7 name: type: string example: 'Path to 2030' slug: type: string example: path-to-2030 source: type: string example: null nullable: true source_label: type: string example: 'Live baseline' has_dated_changes: type: boolean example: true last_change: type: string example: '2030-06-30' points: type: array example: - date: '2026-09-27' label: Today projected: false scenario: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 source: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 difference: headcount: 0 fte: 0 cost: 0 people_cost: 0 agent_cost: 0 - date: '2028-01-01' label: '2028' projected: true scenario: headcount: 936 fte: 903.1 cost: 890000000 people_cost: 868000000 agent_cost: 22000000 source: headcount: 1000 fte: 962.4 cost: 934000000 people_cost: 933000000 agent_cost: 1000000 difference: headcount: -64 fte: -59.3 cost: -44000000 people_cost: -65000000 agent_cost: 21000000 items: type: object properties: date: type: string example: '2026-09-27' label: type: string example: Today projected: type: boolean example: false scenario: type: object properties: headcount: type: integer example: 1000 fte: type: number example: 962.4 cost: type: integer example: 934000000 people_cost: type: integer example: 933000000 agent_cost: type: integer example: 1000000 source: type: object properties: headcount: type: integer example: 1000 fte: type: number example: 962.4 cost: type: integer example: 934000000 people_cost: type: integer example: 933000000 agent_cost: type: integer example: 1000000 difference: type: object properties: headcount: type: integer example: 0 fte: type: integer example: 0 cost: type: integer example: 0 people_cost: type: integer example: 0 agent_cost: type: integer example: 0 409: description: '' content: application/json: schema: type: object example: message: 'Archived scenarios keep the figures recorded when they were archived. Use the scenario delta instead.' properties: message: type: string example: 'Archived scenarios keep the figures recorded when they were archived. Use the scenario delta instead.' tags: - Scenarios parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'Scenario id or slug.' example: path-to-2030 required: true schema: type: string '/api/v1/scenarios/{scenario_slug}/promote': post: summary: 'Promote a planning scenario to the live baseline.' operationId: promoteAPlanningScenarioToTheLiveBaseline description: 'Queues the existing promotion pipeline with type `scenario_promotion`.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 202: description: '' content: application/json: schema: type: object example: message: 'Scenario promotion started. Promotion in progress.' status: queued scenario_id: 1 tasks_url: 'https://app.orgonaut.test/api/v1/scenarios/tasks' properties: message: type: string example: 'Scenario promotion started. Promotion in progress.' status: type: string example: queued scenario_id: type: integer example: 1 tasks_url: type: string example: 'https://app.orgonaut.test/api/v1/scenarios/tasks' tags: - Scenarios requestBody: required: false content: application/json: schema: type: object properties: confirmed_promotion: type: boolean description: 'Optional confirmation flag for audit.' example: true nullable: true confirmed_archive: type: boolean description: 'Optional confirmation flag for audit.' example: true nullable: true parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'The ID of the scenario.' example: 1 required: true schema: type: integer '/api/v1/scenarios/{scenario_slug}/submit': post: summary: 'Submit the scenario for review.' operationId: submitTheScenarioForReview description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: 'FY26 Cost Optimisation' status: under_review properties: data: type: object properties: id: type: integer example: 1 name: type: string example: 'FY26 Cost Optimisation' status: type: string example: under_review tags: - Scenarios parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'The ID of the scenario.' example: 1 required: true schema: type: integer '/api/v1/scenarios/{scenario_slug}/approve': post: summary: 'Approve the scenario.' operationId: approveTheScenario description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: 'FY26 Cost Optimisation' status: approved properties: data: type: object properties: id: type: integer example: 1 name: type: string example: 'FY26 Cost Optimisation' status: type: string example: approved tags: - Scenarios parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'The ID of the scenario.' example: 1 required: true schema: type: integer /api/v1/snapshots: get: summary: 'List baseline snapshots.' operationId: listBaselineSnapshots description: '' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' - in: query name: 'filter[status]' description: 'Filter by status.' example: archived required: false schema: type: string description: 'Filter by status.' example: archived - in: query name: 'fields[scenarios]' description: 'Sparse fields for scenarios.' example: 'id,name,kind' required: false schema: type: string description: 'Sparse fields for scenarios.' example: 'id,name,kind' - in: query name: include description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate required: false schema: type: string description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate - in: query name: as_of description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 12 name: 'Baseline Snapshot 2025-11-01' kind: baseline_archive links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 12 name: 'Baseline Snapshot 2025-11-01' kind: baseline_archive items: type: object properties: id: type: integer example: 12 name: type: string example: 'Baseline Snapshot 2025-11-01' kind: type: string example: baseline_archive links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 tags: - Snapshots '/api/v1/snapshots/{scenario_slug}': get: summary: 'Display a baseline snapshot.' operationId: displayABaselineSnapshot description: '' parameters: - in: query name: include description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate required: false schema: type: string description: 'Comma-separated relationships. Allowed: teams,aggregate.' example: aggregate - in: query name: as_of description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' required: false schema: type: string description: 'Snapshot date for aggregates. Format: YYYY-MM-DD.' example: '2025-09-01' - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 12 name: 'Baseline Snapshot 2025-11-01' kind: baseline_archive status: archived properties: data: type: object properties: id: type: integer example: 12 name: type: string example: 'Baseline Snapshot 2025-11-01' kind: type: string example: baseline_archive status: type: string example: archived tags: - Snapshots parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'Snapshot id.' example: 12 required: true schema: type: integer '/api/v1/snapshots/{scenario_slug}/restore': post: summary: 'Restore a baseline snapshot to become the live baseline.' operationId: restoreABaselineSnapshotToBecomeTheLiveBaseline description: 'Queues the existing promotion pipeline with type `snapshot_restore`.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 202: description: '' content: application/json: schema: type: object example: message: 'Snapshot restore started. Promotion in progress.' status: queued snapshot_id: 12 tasks_url: 'https://app.orgonaut.test/api/v1/scenarios/tasks' properties: message: type: string example: 'Snapshot restore started. Promotion in progress.' status: type: string example: queued snapshot_id: type: integer example: 12 tasks_url: type: string example: 'https://app.orgonaut.test/api/v1/scenarios/tasks' tags: - Snapshots requestBody: required: false content: application/json: schema: type: object properties: confirmed_promotion: type: boolean description: 'Optional confirmation flag for audit.' example: true nullable: true confirmed_archive: type: boolean description: 'Optional confirmation flag for audit.' example: true nullable: true parameters: - in: path name: scenario_slug description: 'The slug of the scenario.' example: ab required: true schema: type: string - in: path name: scenario description: 'Snapshot id.' example: 12 required: true schema: type: integer /api/v1/team-types/search: get: summary: 'Search Team Types for the active tenant.' operationId: searchTeamTypesForTheActiveTenant description: "Returns up to 20 matching types ordered by display order then name.\nRequires `settings.manage` ability." parameters: - in: query name: q description: 'Search term applied to name and slug.' example: stream required: false schema: type: string description: 'Search term applied to name and slug.' example: stream - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: Stream-aligned slug: stream-aligned color: '#fbbf24' description: null is_active: true display_order: 0 properties: data: type: array example: - id: 1 name: Stream-aligned slug: stream-aligned color: '#fbbf24' description: null is_active: true display_order: 0 items: type: object properties: id: type: integer example: 1 name: type: string example: Stream-aligned slug: type: string example: stream-aligned color: type: string example: '#fbbf24' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 0 tags: - 'Team Types' /api/v1/team-types: post: summary: 'Create a Team Type for the active tenant.' operationId: createATeamTypeForTheActiveTenant description: 'Requires `settings.manage` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 5 name: Platform slug: platform color: '#7c3aed' description: null is_active: true display_order: 0 properties: data: type: object properties: id: type: integer example: 5 name: type: string example: Platform slug: type: string example: platform color: type: string example: '#7c3aed' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 0 422: description: '' content: application/json: schema: type: object example: message: 'The name has already been taken.' errors: name: - 'The name has already been taken.' properties: message: type: string example: 'The name has already been taken.' errors: type: object properties: name: type: array example: - 'The name has already been taken.' items: type: string tags: - 'Team Types' requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Display name. Max 120 characters. Must be unique within the tenant.' example: Platform slug: type: string description: 'URL-safe slug. Auto-derived from name if omitted. Max 120 characters. Must be unique within the tenant.' example: platform nullable: true color: type: string description: 'Hex colour code with or without leading `#`.' example: '#7c3aed' nullable: true description: type: string description: 'Optional description. Max 500 characters.' example: 'Infrastructure and tooling teams' nullable: true display_order: type: integer description: 'Sort order for selectors. Between 0 and 999.' example: 2 nullable: true required: - name '/api/v1/team-types/{teamType_id}': get: summary: 'Show a specific Team Type.' operationId: showASpecificTeamType description: 'Requires `settings.manage` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: Stream-aligned slug: stream-aligned color: '#fbbf24' description: null is_active: true display_order: 0 properties: data: type: object properties: id: type: integer example: 1 name: type: string example: Stream-aligned slug: type: string example: stream-aligned color: type: string example: '#fbbf24' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 0 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' tags: - 'Team Types' patch: summary: 'Update a Team Type.' operationId: updateATeamType description: 'Requires `settings.manage` ability. All fields are optional for partial updates.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 1 name: Platform slug: platform color: '#7c3aed' description: null is_active: true display_order: 2 properties: data: type: object properties: id: type: integer example: 1 name: type: string example: Platform slug: type: string example: platform color: type: string example: '#7c3aed' description: type: string example: null nullable: true is_active: type: boolean example: true display_order: type: integer example: 2 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' 422: description: '' content: application/json: schema: type: object example: message: 'The name has already been taken.' errors: name: - 'The name has already been taken.' properties: message: type: string example: 'The name has already been taken.' errors: type: object properties: name: type: array example: - 'The name has already been taken.' items: type: string tags: - 'Team Types' requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Display name. Max 120 characters. Must be unique within the tenant.' example: Platform slug: type: string description: 'URL-safe slug. Auto-derived from name if omitted on create; not auto-updated on edit. Max 120 characters. Must be unique within the tenant.' example: platform nullable: true color: type: string description: 'Hex colour code with or without leading `#`.' example: '#7c3aed' nullable: true description: type: string description: 'Optional description. Max 500 characters.' example: 'Infrastructure and tooling teams' nullable: true is_active: type: boolean description: 'Whether the type is active.' example: true display_order: type: integer description: 'Sort order for selectors. Between 0 and 999.' example: 2 nullable: true delete: summary: 'Delete a Team Type.' operationId: deleteATeamType description: "Requires `settings.manage` ability. Associated teams will have their\n`team_type_id` set to null on delete." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } 404: description: '' content: application/json: schema: type: object example: message: 'Not found.' properties: message: type: string example: 'Not found.' tags: - 'Team Types' parameters: - in: path name: teamType_id description: 'The ID of the teamType.' example: 11 required: true schema: type: integer - in: path name: teamType description: 'The ID of the team type.' example: 1 required: true schema: type: integer /api/v1/teams: get: summary: 'Display a paginated listing of teams.' operationId: displayAPaginatedListingOfTeams description: 'Requires `teams.read` ability.' parameters: - in: query name: page description: 'The page number.' example: 1 required: false schema: type: integer description: 'The page number.' example: 1 - in: query name: per_page description: 'Max 100.' example: 25 required: false schema: type: integer description: 'Max 100.' example: 25 - in: query name: scenario description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan required: false schema: type: string description: 'Scenario slug to read from. Omit for the Live Baseline. Unknown scenarios return 400.' example: q3-plan - in: query name: 'filter[scenario_id]' description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' required: false schema: type: string description: 'Scenario id or slug; `baseline`/`live`/`null` for the Live Baseline. Overrides `scenario`.' example: '7' - in: query name: 'filter[scenario]' description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan required: false schema: type: string description: 'Same as filter[scenario_id]; also accepts `current`.' example: q3-plan - in: query name: sort description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' required: false schema: type: string description: 'Comma-separated columns, prefix with - for DESC. Allowed: id,name,created_at.' example: '-created_at,name' - in: query name: 'filter[department_id]' description: 'Filter by department id.' example: 5 required: false schema: type: integer description: 'Filter by department id.' example: 5 - in: query name: 'filter[org_unit_id]' description: 'Filter by org unit id (department subtree).' example: 10 required: false schema: type: integer description: 'Filter by org unit id (department subtree).' example: 10 - in: query name: 'fields[teams]' description: 'Sparse fields for teams.' example: 'id,name' required: false schema: type: string description: 'Sparse fields for teams.' example: 'id,name' - in: query name: include description: 'Comma-separated relationships. Allowed: aggregate.' example: aggregate required: false schema: type: string description: 'Comma-separated relationships. Allowed: aggregate.' example: aggregate - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 15 name: 'Team CúChulainn' team_type_id: 3 team_type: id: 3 name: Cross-functional slug: cross-functional purpose: 'Platform delivery' parent_org_unit_id: 202 scenario_id: 1 valid_from: '2025-01-10' valid_to: null created_at: '2025-01-10T09:00:00Z' links: first: null last: null prev: null next: null meta: total: 1 current_page: 1 per_page: 15 last_page: 1 properties: data: type: array example: - id: 15 name: 'Team CúChulainn' team_type_id: 3 team_type: id: 3 name: Cross-functional slug: cross-functional purpose: 'Platform delivery' parent_org_unit_id: 202 scenario_id: 1 valid_from: '2025-01-10' valid_to: null created_at: '2025-01-10T09:00:00Z' items: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' team_type_id: type: integer example: 3 team_type: type: object properties: id: type: integer example: 3 name: type: string example: Cross-functional slug: type: string example: cross-functional purpose: type: string example: 'Platform delivery' parent_org_unit_id: type: integer example: 202 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-10' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' links: type: object properties: first: type: string example: null nullable: true last: type: string example: null nullable: true prev: type: string example: null nullable: true next: type: string example: null nullable: true meta: type: object properties: total: type: integer example: 1 current_page: type: integer example: 1 per_page: type: integer example: 15 last_page: type: integer example: 1 400: description: '' content: application/json: schema: type: object example: code: query.invalid_parameter message: 'Unsupported sort: salary' details: allowed: - id - name - created_at properties: code: type: string example: query.invalid_parameter message: type: string example: 'Unsupported sort: salary' details: type: object properties: allowed: type: array example: - id - name - created_at items: type: string 422: description: '' content: application/json: schema: type: object example: message: 'The given data was invalid.' errors: per_page: - 'The per page must be at least 1.' properties: message: type: string example: 'The given data was invalid.' errors: type: object properties: per_page: type: array example: - 'The per page must be at least 1.' items: type: string tags: - Teams post: summary: 'Store a newly created team.' operationId: storeANewlyCreatedTeam description: "Requires `teams.create` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 15 name: 'Team CúChulainn' team_type_id: 3 team_type: id: 3 name: Cross-functional slug: cross-functional purpose: 'Platform delivery' parent_org_unit_id: 202 scenario_id: 1 valid_from: '2025-01-10' valid_to: null created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' team_type_id: type: integer example: 3 team_type: type: object properties: id: type: integer example: 3 name: type: string example: Cross-functional slug: type: string example: cross-functional purpose: type: string example: 'Platform delivery' parent_org_unit_id: type: integer example: 202 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-10' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Teams requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Team name. Must not be greater than 255 characters.' example: Platform team_type_id: type: integer description: 'Optional team type id. The id of an existing record in the team_types table.' example: 1 nullable: true purpose: type: string description: 'Optional purpose/mission. Must not be greater than 255 characters.' example: 'Internal platforms' nullable: true valid_from: type: string description: 'Date the team becomes active. Must be a valid date.' example: '2025-01-01' valid_to: type: string description: 'Optional date the team becomes inactive. Must be a valid date. Must be a date after or equal to valid_from.' example: '2025-12-31' nullable: true parent_org_unit_id: type: integer description: nullable example: 7 nullable: true required: - name - valid_from '/api/v1/teams/{id}': get: summary: 'Display a specific team.' operationId: displayASpecificTeam description: 'Requires `teams.read` ability.' parameters: - in: query name: 'filter[scenario_id]' description: 'Filter by scenario ID.' example: 1 required: false schema: type: integer description: 'Filter by scenario ID.' example: 1 - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 15 name: 'Team CúChulainn' team_type_id: 3 team_type: id: 3 name: Cross-functional slug: cross-functional purpose: 'Platform delivery' parent_org_unit_id: 202 scenario_id: 1 valid_from: '2025-01-10' valid_to: null created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 15 name: type: string example: 'Team CúChulainn' team_type_id: type: integer example: 3 team_type: type: object properties: id: type: integer example: 3 name: type: string example: Cross-functional slug: type: string example: cross-functional purpose: type: string example: 'Platform delivery' parent_org_unit_id: type: integer example: 202 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-10' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Teams put: summary: 'Update a team.' operationId: updateATeam description: "Requires `teams.update` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 15 name: 'Team Fianna' team_type_id: 3 team_type: id: 3 name: Cross-functional slug: cross-functional purpose: 'Platform delivery' parent_org_unit_id: 202 scenario_id: 1 valid_from: '2025-01-10' valid_to: null created_at: '2025-01-10T09:00:00Z' properties: data: type: object properties: id: type: integer example: 15 name: type: string example: 'Team Fianna' team_type_id: type: integer example: 3 team_type: type: object properties: id: type: integer example: 3 name: type: string example: Cross-functional slug: type: string example: cross-functional purpose: type: string example: 'Platform delivery' parent_org_unit_id: type: integer example: 202 scenario_id: type: integer example: 1 valid_from: type: string example: '2025-01-10' valid_to: type: string example: null nullable: true created_at: type: string example: '2025-01-10T09:00:00Z' tags: - Teams requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Team name when changing team metadata. Must not be greater than 255 characters.' example: Platform team_type_id: type: integer description: 'Optional team type id. The id of an existing record in the team_types table.' example: 1 nullable: true purpose: type: string description: 'Optional purpose/mission. Must not be greater than 255 characters.' example: 'Internal platforms' nullable: true valid_from: type: string description: 'Date the team becomes active when changing validity. Must be a valid date.' example: '2025-01-01' valid_to: type: string description: 'Optional date the team becomes inactive. Must be a valid date. Must be a date after or equal to valid_from.' example: '2025-12-31' nullable: true parent_org_unit_id: type: integer description: nullable example: 7 nullable: true delete: summary: 'Delete a team.' operationId: deleteATeam description: "Requires `teams.delete` ability.\nCLI-scoped tokens can call this endpoint only inside an active mutable scenario." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 204: description: '' content: application/json: schema: type: object example: { } properties: { } tags: - Teams parameters: - in: path name: id description: 'The ID of the team.' example: 11 required: true schema: type: integer '/api/v1/teams/{team_id}/velocity': get: summary: 'Get velocity stats for a team in a sprint.' operationId: getVelocityStatsForATeamInASprint description: '' parameters: - in: query name: sprint_id description: 'The sprint to query.' example: 11 required: false schema: type: integer description: 'The sprint to query.' example: 11 nullable: true - in: query name: as_of description: 'date Mutually exclusive with sprint_id.' example: ab required: false schema: type: string description: 'date Mutually exclusive with sprint_id.' example: ab nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: team_id: 12 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' committed_sp: 34 completed_sp: 31 scope_added_sp: 3 carried_over_sp: 5 stories_done: 12 source: jira properties: data: type: object properties: team_id: type: integer example: 12 sprint_id: type: integer example: 42 period: type: object properties: label: type: string example: 'Sprint 18' starts_at: type: string example: '2025-09-01' ends_at: type: string example: '2025-09-14' committed_sp: type: integer example: 34 completed_sp: type: integer example: 31 scope_added_sp: type: integer example: 3 carried_over_sp: type: integer example: 5 stories_done: type: integer example: 12 source: type: string example: jira tags: - 'Teams — Velocity' parameters: - in: path name: team_id description: 'The ID of the team.' example: 11 required: true schema: type: integer - in: path name: team description: 'The team ID.' example: 12 required: true schema: type: integer '/api/v1/teams/{team_id}/velocity/series': get: summary: 'Get a velocity series for a team.' operationId: getAVelocitySeriesForATeam description: '' parameters: - in: query name: from_sprint_id description: 'Starting sprint ID.' example: 11 required: false schema: type: integer description: 'Starting sprint ID.' example: 11 nullable: true - in: query name: to_sprint_id description: 'Ending sprint ID.' example: 11 required: false schema: type: integer description: 'Ending sprint ID.' example: 11 nullable: true - in: query name: from description: 'date Start date.' example: ab required: false schema: type: string description: 'date Start date.' example: ab nullable: true - in: query name: to description: 'date End date.' example: ab required: false schema: type: string description: 'date End date.' example: ab nullable: true - in: query name: limit description: 'Maximum results. Defaults to 12.' example: 11 required: false schema: type: integer description: 'Maximum results. Defaults to 12.' example: 11 nullable: true - in: query name: order description: 'asc or desc. Defaults to asc.' example: ab required: false schema: type: string description: 'asc or desc. Defaults to asc.' example: ab nullable: true - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "demo"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - team_id: 12 sprint_id: 41 period: label: 'Sprint 17' starts_at: '2025-08-18' ends_at: '2025-08-31' committed_sp: 29 completed_sp: 27 scope_added_sp: 2 carried_over_sp: 4 stories_done: 10 source: jira - team_id: 12 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' committed_sp: 34 completed_sp: 31 scope_added_sp: 3 carried_over_sp: 5 stories_done: 12 source: jira properties: data: type: array example: - team_id: 12 sprint_id: 41 period: label: 'Sprint 17' starts_at: '2025-08-18' ends_at: '2025-08-31' committed_sp: 29 completed_sp: 27 scope_added_sp: 2 carried_over_sp: 4 stories_done: 10 source: jira - team_id: 12 sprint_id: 42 period: label: 'Sprint 18' starts_at: '2025-09-01' ends_at: '2025-09-14' committed_sp: 34 completed_sp: 31 scope_added_sp: 3 carried_over_sp: 5 stories_done: 12 source: jira items: type: object properties: team_id: type: integer example: 12 sprint_id: type: integer example: 41 period: type: object properties: label: type: string example: 'Sprint 17' starts_at: type: string example: '2025-08-18' ends_at: type: string example: '2025-08-31' committed_sp: type: integer example: 29 completed_sp: type: integer example: 27 scope_added_sp: type: integer example: 2 carried_over_sp: type: integer example: 4 stories_done: type: integer example: 10 source: type: string example: jira tags: - 'Teams — Velocity' parameters: - in: path name: team_id description: 'The ID of the team.' example: 11 required: true schema: type: integer - in: path name: team description: 'The team ID.' example: 12 required: true schema: type: integer /api/v1/tenant-invitations: get: summary: 'List invitations for the current tenant.' operationId: listInvitationsForTheCurrentTenant description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 17 tenant_id: 3 email: new.user@example.com role: manager state: pending invited_by: 7 expires_at: '2026-03-14T09:00:00Z' accepted_at: null accepted_by_user_id: null revoked_at: null created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' properties: data: type: array example: - id: 17 tenant_id: 3 email: new.user@example.com role: manager state: pending invited_by: 7 expires_at: '2026-03-14T09:00:00Z' accepted_at: null accepted_by_user_id: null revoked_at: null created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' items: type: object properties: id: type: integer example: 17 tenant_id: type: integer example: 3 email: type: string example: new.user@example.com role: type: string example: manager state: type: string example: pending invited_by: type: integer example: 7 expires_at: type: string example: '2026-03-14T09:00:00Z' accepted_at: type: string example: null nullable: true accepted_by_user_id: type: string example: null nullable: true revoked_at: type: string example: null nullable: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T09:00:00Z' tags: - 'Tenant Invitations' post: summary: 'Create a new tenant invitation.' operationId: createANewTenantInvitation description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 17 tenant_id: 3 email: new.user@example.com role: manager state: pending invited_by: 7 expires_at: '2026-03-14T09:00:00Z' accepted_at: null accepted_by_user_id: null revoked_at: null created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T09:00:00Z' properties: data: type: object properties: id: type: integer example: 17 tenant_id: type: integer example: 3 email: type: string example: new.user@example.com role: type: string example: manager state: type: string example: pending invited_by: type: integer example: 7 expires_at: type: string example: '2026-03-14T09:00:00Z' accepted_at: type: string example: null nullable: true accepted_by_user_id: type: string example: null nullable: true revoked_at: type: string example: null nullable: true created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T09:00:00Z' tags: - 'Tenant Invitations' requestBody: required: true content: application/json: schema: type: object properties: email: type: string description: 'Invitee email address.' example: '"new.user@example.com"' role: type: string description: 'User type to assign on acceptance. Must be one of `admin`, `manager`, `member`, or `viewer`.' example: '"manager"' required: - email - role '/api/v1/tenant-invitations/{invite}/resend': post: summary: 'Resend a pending invitation and revoke the existing pending token.' operationId: resendAPendingInvitationAndRevokeTheExistingPendingToken description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 23 tenant_id: 3 email: new.user@example.com role: manager state: pending invited_by: 7 expires_at: '2026-03-21T09:00:00Z' accepted_at: null accepted_by_user_id: null revoked_at: null created_at: '2026-03-07T10:00:00Z' updated_at: '2026-03-07T10:00:00Z' properties: data: type: object properties: id: type: integer example: 23 tenant_id: type: integer example: 3 email: type: string example: new.user@example.com role: type: string example: manager state: type: string example: pending invited_by: type: integer example: 7 expires_at: type: string example: '2026-03-21T09:00:00Z' accepted_at: type: string example: null nullable: true accepted_by_user_id: type: string example: null nullable: true revoked_at: type: string example: null nullable: true created_at: type: string example: '2026-03-07T10:00:00Z' updated_at: type: string example: '2026-03-07T10:00:00Z' tags: - 'Tenant Invitations' parameters: - in: path name: invite description: 'Invitation id.' example: 17 required: true schema: type: integer '/api/v1/tenant-invitations/{invite}/revoke': post: summary: 'Revoke a pending invitation.' operationId: revokeAPendingInvitation description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 17 tenant_id: 3 email: new.user@example.com role: manager state: revoked invited_by: 7 expires_at: '2026-03-14T09:00:00Z' accepted_at: null accepted_by_user_id: null revoked_at: '2026-03-07T10:15:00Z' created_at: '2026-03-07T09:00:00Z' updated_at: '2026-03-07T10:15:00Z' properties: data: type: object properties: id: type: integer example: 17 tenant_id: type: integer example: 3 email: type: string example: new.user@example.com role: type: string example: manager state: type: string example: revoked invited_by: type: integer example: 7 expires_at: type: string example: '2026-03-14T09:00:00Z' accepted_at: type: string example: null nullable: true accepted_by_user_id: type: string example: null nullable: true revoked_at: type: string example: '2026-03-07T10:15:00Z' created_at: type: string example: '2026-03-07T09:00:00Z' updated_at: type: string example: '2026-03-07T10:15:00Z' tags: - 'Tenant Invitations' parameters: - in: path name: invite description: 'Invitation id.' example: 17 required: true schema: type: integer /api/v1/tenant-members: get: summary: 'List active and archived tenant memberships.' operationId: listActiveAndArchivedTenantMemberships description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: active: - tenant_id: 3 user_id: 42 name: "Aisling O'Neill" email: aisling@example.com role: admin membership_state: active archived_at: null archived_by: null reactivated_at: null reactivated_by: null archived: - tenant_id: 3 user_id: 51 name: 'Niall Byrne' email: niall@example.com role: member membership_state: archived archived_at: '2026-02-21T09:30:00Z' archived_by: 42 reactivated_at: null reactivated_by: null properties: data: type: object properties: active: type: array example: - tenant_id: 3 user_id: 42 name: "Aisling O'Neill" email: aisling@example.com role: admin membership_state: active archived_at: null archived_by: null reactivated_at: null reactivated_by: null description: 'Active memberships.' items: type: object properties: tenant_id: type: integer example: 3 user_id: type: integer example: 42 name: type: string example: "Aisling O'Neill" email: type: string example: aisling@example.com role: type: string example: admin membership_state: type: string example: active archived_at: type: string example: null nullable: true archived_by: type: string example: null nullable: true reactivated_at: type: string example: null nullable: true reactivated_by: type: string example: null nullable: true archived: type: array example: - tenant_id: 3 user_id: 51 name: 'Niall Byrne' email: niall@example.com role: member membership_state: archived archived_at: '2026-02-21T09:30:00Z' archived_by: 42 reactivated_at: null reactivated_by: null description: 'Archived memberships.' items: type: object properties: tenant_id: type: integer example: 3 user_id: type: integer example: 51 name: type: string example: 'Niall Byrne' email: type: string example: niall@example.com role: type: string example: member membership_state: type: string example: archived archived_at: type: string example: '2026-02-21T09:30:00Z' archived_by: type: integer example: 42 reactivated_at: type: string example: null nullable: true reactivated_by: type: string example: null nullable: true tags: - 'Tenant Members' '/api/v1/tenant-members/{user}/archive': patch: summary: 'Archive a non-owner tenant membership.' operationId: archiveANonOwnerTenantMembership description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: tenant_id: 3 user_id: 42 name: "Aisling O'Neill" email: aisling@example.com role: member membership_state: archived archived_at: '2026-03-07T10:15:00Z' archived_by: 7 reactivated_at: null reactivated_by: null properties: data: type: object properties: tenant_id: type: integer example: 3 user_id: type: integer example: 42 description: 'Membership user id.' name: type: string example: "Aisling O'Neill" email: type: string example: aisling@example.com role: type: string example: member membership_state: type: string example: archived description: 'Updated membership state.' archived_at: type: string example: '2026-03-07T10:15:00Z' archived_by: type: integer example: 7 reactivated_at: type: string example: null nullable: true reactivated_by: type: string example: null nullable: true tags: - 'Tenant Members' parameters: - in: path name: user description: 'Tenant user id.' example: 42 required: true schema: type: integer '/api/v1/tenant-members/{user}/reactivate': patch: summary: 'Reactivate an archived tenant membership.' operationId: reactivateAnArchivedTenantMembership description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: tenant_id: 3 user_id: 42 name: "Aisling O'Neill" email: aisling@example.com role: member membership_state: active archived_at: '2026-03-01T10:15:00Z' archived_by: 7 reactivated_at: '2026-03-07T11:05:00Z' reactivated_by: 7 properties: data: type: object properties: tenant_id: type: integer example: 3 user_id: type: integer example: 42 description: 'Membership user id.' name: type: string example: "Aisling O'Neill" email: type: string example: aisling@example.com role: type: string example: member membership_state: type: string example: active description: 'Updated membership state.' archived_at: type: string example: '2026-03-01T10:15:00Z' archived_by: type: integer example: 7 reactivated_at: type: string example: '2026-03-07T11:05:00Z' reactivated_by: type: integer example: 7 tags: - 'Tenant Members' parameters: - in: path name: user description: 'Tenant user id.' example: 42 required: true schema: type: integer '/api/v1/tenant-members/{user}/role': patch: summary: 'Update a tenant member role without using ownership transfer.' operationId: updateATenantMemberRoleWithoutUsingOwnershipTransfer description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: tenant_id: 3 user_id: 42 name: "Aisling O'Neill" email: aisling@example.com role: viewer membership_state: active archived_at: null archived_by: null reactivated_at: null reactivated_by: null properties: data: type: object properties: tenant_id: type: integer example: 3 user_id: type: integer example: 42 description: 'Membership user id.' name: type: string example: "Aisling O'Neill" email: type: string example: aisling@example.com role: type: string example: viewer description: 'Updated membership role.' membership_state: type: string example: active archived_at: type: string example: null nullable: true archived_by: type: string example: null nullable: true reactivated_at: type: string example: null nullable: true reactivated_by: type: string example: null nullable: true 422: description: '' content: application/json: schema: type: object example: message: 'Archived memberships must be reactivated before changing the user type.' code: tenancy.invalid_state properties: message: type: string example: 'Archived memberships must be reactivated before changing the user type.' code: type: string example: tenancy.invalid_state tags: - 'Tenant Members' requestBody: required: true content: application/json: schema: type: object properties: role: type: string description: 'User type to assign. Must be one of `admin`, `manager`, `member`, or `viewer`.' example: '"viewer"' required: - role parameters: - in: path name: user description: 'Tenant user id.' example: 42 required: true schema: type: integer /api/v1/tenant-members/transfer-ownership: post: summary: 'Transfer tenant ownership to an existing active admin.' operationId: transferTenantOwnershipToAnExistingActiveAdmin description: '' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: status: ok properties: data: type: object properties: status: type: string example: ok tags: - 'Tenant Members' requestBody: required: true content: application/json: schema: type: object properties: target_user_id: type: integer description: 'Existing active admin user id.' example: 42 required: - target_user_id /api/v1/work-activities: get: summary: 'List work activities for the active context.' operationId: listWorkActivitiesForTheActiveContext description: 'Requires `work.read` ability.' parameters: - in: query name: q description: 'Search term applied to name and slug.' example: triage required: false schema: type: string description: 'Search term applied to name and slug.' example: triage - in: query name: category description: 'Filter by work category slug (see Settings → Work Categories).' example: delivery required: false schema: type: string description: 'Filter by work category slug (see Settings → Work Categories).' example: delivery - in: query name: include_archived description: 'Include archived activities.' example: false required: false schema: type: boolean description: 'Include archived activities.' example: false - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: - id: 1 name: 'Triage support tickets' slug: triage-support-tickets category: id: 2 name: Coordination slug: coordination color: '#f59e0b' source: manual description: null archived_at: null role_type_count: 2 team_count: 1 properties: data: type: array example: - id: 1 name: 'Triage support tickets' slug: triage-support-tickets category: id: 2 name: Coordination slug: coordination color: '#f59e0b' source: manual description: null archived_at: null role_type_count: 2 team_count: 1 items: type: object properties: id: type: integer example: 1 name: type: string example: 'Triage support tickets' slug: type: string example: triage-support-tickets category: type: object properties: id: type: integer example: 2 name: type: string example: Coordination slug: type: string example: coordination color: type: string example: '#f59e0b' source: type: string example: manual description: type: string example: null nullable: true archived_at: type: string example: null nullable: true role_type_count: type: integer example: 2 team_count: type: integer example: 1 tags: - 'Work Catalog' post: summary: 'Create a work activity in the active context.' operationId: createAWorkActivityInTheActiveContext description: 'Requires `work.write` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 201: description: '' content: application/json: schema: type: object example: data: id: 5 name: 'Draft release notes' slug: draft-release-notes category: id: 2 name: Delivery slug: delivery color: '#f59e0b' source: manual description: null archived_at: null properties: data: type: object properties: id: type: integer example: 5 name: type: string example: 'Draft release notes' slug: type: string example: draft-release-notes category: type: object properties: id: type: integer example: 2 name: type: string example: Delivery slug: type: string example: delivery color: type: string example: '#f59e0b' source: type: string example: manual description: type: string example: null nullable: true archived_at: type: string example: null nullable: true 422: description: '' content: application/json: schema: type: object example: message: 'A work activity with this name already exists in this context.' errors: name: - 'A work activity with this name already exists in this context.' properties: message: type: string example: 'A work activity with this name already exists in this context.' errors: type: object properties: name: type: array example: - 'A work activity with this name already exists in this context.' items: type: string tags: - 'Work Catalog' requestBody: required: true content: application/json: schema: type: object properties: name: type: string description: 'Display name. Max 120 characters. Must be unique within the context.' example: 'Draft release notes' description: type: string description: 'Optional description. Max 2000 characters.' example: 'Summarise shipped changes for customers' nullable: true work_category_id: type: integer description: "Work category identifier from the tenant's taxonomy." example: 2 required: - name - work_category_id '/api/v1/work-activities/{workActivity_id}': get: summary: 'Show a work activity with its role-type and team mappings.' operationId: showAWorkActivityWithItsRoleTypeAndTeamMappings description: 'Requires `work.read` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 name: 'Draft release notes' slug: draft-release-notes category: id: 2 name: Delivery slug: delivery color: '#f59e0b' source: manual description: null archived_at: null role_types: - id: 3 name: Engineer slug: engineer time_share_pct: 15 teams: - id: 9 name: Payments emphasis: primary properties: data: type: object properties: id: type: integer example: 5 name: type: string example: 'Draft release notes' slug: type: string example: draft-release-notes category: type: object properties: id: type: integer example: 2 name: type: string example: Delivery slug: type: string example: delivery color: type: string example: '#f59e0b' source: type: string example: manual description: type: string example: null nullable: true archived_at: type: string example: null nullable: true role_types: type: array example: - id: 3 name: Engineer slug: engineer time_share_pct: 15 items: type: object properties: id: type: integer example: 3 name: type: string example: Engineer slug: type: string example: engineer time_share_pct: type: integer example: 15 teams: type: array example: - id: 9 name: Payments emphasis: primary items: type: object properties: id: type: integer example: 9 name: type: string example: Payments emphasis: type: string example: primary tags: - 'Work Catalog' patch: summary: 'Update a work activity.' operationId: updateAWorkActivity description: 'Requires `work.write` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 name: 'Draft customer release notes' slug: draft-customer-release-notes category: id: 2 name: Delivery slug: delivery color: '#f59e0b' source: manual description: null archived_at: null properties: data: type: object properties: id: type: integer example: 5 name: type: string example: 'Draft customer release notes' slug: type: string example: draft-customer-release-notes category: type: object properties: id: type: integer example: 2 name: type: string example: Delivery slug: type: string example: delivery color: type: string example: '#f59e0b' source: type: string example: manual description: type: string example: null nullable: true archived_at: type: string example: null nullable: true tags: - 'Work Catalog' requestBody: required: false content: application/json: schema: type: object properties: name: type: string description: 'Display name. Max 120 characters.' example: 'Draft customer release notes' description: type: string description: 'Optional description. Max 2000 characters.' example: 'Customer-facing change summaries' nullable: true work_category_id: type: integer description: "Work category identifier from the tenant's taxonomy." example: 2 parameters: - in: path name: workActivity_id description: 'The ID of the workActivity.' example: 11 required: true schema: type: integer - in: path name: workActivity description: 'Work activity identifier.' example: 5 required: true schema: type: integer '/api/v1/work-activities/{workActivity_id}/archive': post: summary: 'Archive a work activity.' operationId: archiveAWorkActivity description: "Archived activities keep their mappings but are excluded from listings by default.\nRequires `work.write` ability." parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 archived_at: '2026-07-02T10:00:00.000000Z' properties: data: type: object properties: id: type: integer example: 5 archived_at: type: string example: '2026-07-02T10:00:00.000000Z' tags: - 'Work Catalog' parameters: - in: path name: workActivity_id description: 'The ID of the workActivity.' example: 11 required: true schema: type: integer - in: path name: workActivity description: 'Work activity identifier.' example: 5 required: true schema: type: integer '/api/v1/work-activities/{workActivity_id}/restore': post: summary: 'Restore an archived work activity.' operationId: restoreAnArchivedWorkActivity description: 'Requires `work.write` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 archived_at: null properties: data: type: object properties: id: type: integer example: 5 archived_at: type: string example: null nullable: true tags: - 'Work Catalog' parameters: - in: path name: workActivity_id description: 'The ID of the workActivity.' example: 11 required: true schema: type: integer - in: path name: workActivity description: 'Work activity identifier.' example: 5 required: true schema: type: integer '/api/v1/work-activities/{workActivity_id}/role-types': put: summary: 'Replace the role-type mappings for a work activity.' operationId: replaceTheRoleTypeMappingsForAWorkActivity description: 'Requires `work.write` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 role_types: - id: 3 name: Engineer slug: engineer time_share_pct: 15 properties: data: type: object properties: id: type: integer example: 5 role_types: type: array example: - id: 3 name: Engineer slug: engineer time_share_pct: 15 items: type: object properties: id: type: integer example: 3 name: type: string example: Engineer slug: type: string example: engineer time_share_pct: type: integer example: 15 tags: - 'Work Catalog' requestBody: required: true content: application/json: schema: type: object properties: role_types: type: array description: 'Full replacement set of mappings.' example: - role_type_id: 3 time_share_pct: 15 items: type: object properties: role_type_id: type: integer description: 'Role type identifier.' example: 3 time_share_pct: type: integer description: "Share of the role's time (0–100)." example: 15 required: - role_type_id - time_share_pct required: - role_types parameters: - in: path name: workActivity_id description: 'The ID of the workActivity.' example: 11 required: true schema: type: integer - in: path name: workActivity description: 'Work activity identifier.' example: 5 required: true schema: type: integer '/api/v1/work-activities/{workActivity_id}/teams': put: summary: 'Replace the team mappings for a work activity.' operationId: replaceTheTeamMappingsForAWorkActivity description: 'Requires `work.write` ability.' parameters: - in: header name: X-Tenant description: '' example: 'string required Tenant slug; required for tenant-scoped endpoints. Example: "acme"' schema: type: string responses: 200: description: '' content: application/json: schema: type: object example: data: id: 5 teams: - id: 9 name: Payments emphasis: primary properties: data: type: object properties: id: type: integer example: 5 teams: type: array example: - id: 9 name: Payments emphasis: primary items: type: object properties: id: type: integer example: 9 name: type: string example: Payments emphasis: type: string example: primary tags: - 'Work Catalog' requestBody: required: true content: application/json: schema: type: object properties: teams: type: array description: 'Full replacement set of mappings.' example: - team_id: 9 emphasis: primary items: type: object properties: team_id: type: integer description: "Team identifier (must share the activity's scenario context)." example: 9 emphasis: type: string description: 'One of: primary, secondary.' example: primary required: - team_id - emphasis required: - teams parameters: - in: path name: workActivity_id description: 'The ID of the workActivity.' example: 11 required: true schema: type: integer - in: path name: workActivity description: 'Work activity identifier.' example: 5 required: true schema: type: integer