openapi: 3.0.3
info:
title: 'Orgonaut API Documentation'
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: 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: 'Scenario Tasks'
description: ''
-
name: Scenarios
description: ''
-
name: Snapshots
description: ''
-
name: Teams
description: ''
-
name: 'Teams — Velocity'
description: ''
-
name: 'Tenant Invitations'
description: ''
-
name: 'Tenant Members'
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: 'The id of an existing record in the actors table.'
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-04-13T21:34:33'
to:
type: string
description: 'Must be a valid date. Must be a date after or equal to from.'
example: '2069-05-04'
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-04-13T21:34:33'
to:
type: string
description: 'Must be a valid date. Must be a date after or equal to from.'
example: '2069-05-04'
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: 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
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
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
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
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
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–1.00). 1.00 = full-time, 0.50 = half-time.'
example: 2306379.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
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
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
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
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: 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 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–1.00). 1.00 = full-time, 0.50 = half-time.'
example: 2306379.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
required:
- first_name
- last_name
- fte
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` ability."
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. 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. 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, department, org_unit, scenario, team).'
example: team
required: false
schema:
type: string
description: 'Optional focused entity type (actor, department, org_unit, scenario, team).'
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, department, org_unit, scenario, team).'
example: team
required: false
schema:
type: string
description: 'Optional focused entity type (actor, department, org_unit, scenario, team).'
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'
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'
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/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
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
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
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
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. 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. 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/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: 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
department_type_id: 7
department_type:
id: 7
name: Division
slug: division
color: '#0052cc'
parent_department_id: 2
parent_department:
id: 2
name: Product
is_active: true
description: 'Platform engineering for core services.'
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
department_type_id: 7
department_type:
id: 7
name: Division
slug: division
color: '#0052cc'
parent_department_id: 2
parent_department:
id: 2
name: Product
is_active: true
description: 'Platform engineering for core services.'
items:
type: object
properties:
id:
type: integer
example: 3
name:
type: string
example: Engineering
lead_id:
type: integer
example: 44
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_department_id:
type: integer
example: 2
parent_department:
type: object
properties:
id:
type: integer
example: 2
name:
type: string
example: Product
is_active:
type: boolean
example: true
description:
type: string
example: 'Platform engineering for core services.'
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.'
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_department_id: null
parent_department: null
is_active: true
description: 'Owns platform reliability.'
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_department_id:
type: string
example: null
nullable: true
parent_department:
type: string
example: null
nullable: true
is_active:
type: boolean
example: true
description:
type: string
example: 'Owns platform reliability.'
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. The id of an existing record in the actors table.'
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_department_id: 2
parent_department:
id: 2
name: Product
is_active: true
description: 'Platform engineering for core services.'
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_department_id:
type: integer
example: 2
parent_department:
type: object
properties:
id:
type: integer
example: 2
name:
type: string
example: Product
is_active:
type: boolean
example: true
description:
type: string
example: 'Platform engineering for core services.'
tags:
- Departments
put:
summary: 'Update a department.'
operationId: updateADepartment
description: 'Requires `departments.update` 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 & Infrastructure'
lead_id: 44
cost_center: ENG-001
department_type_id: 7
department_type:
id: 7
name: Division
slug: division
color: '#0052cc'
parent_department_id: 2
parent_department:
id: 2
name: Product
is_active: true
description: 'Platform engineering for core services.'
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_department_id:
type: integer
example: 2
parent_department:
type: object
properties:
id:
type: integer
example: 2
name:
type: string
example: Product
is_active:
type: boolean
example: true
description:
type: string
example: 'Platform engineering for core services.'
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
department_lead_id:
type: integer
description: 'Actor id of department lead. The id of an existing record in the actors table.'
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
required:
- name
- department_type_id
delete:
summary: 'Delete a department.'
operationId: deleteADepartment
description: 'Requires `departments.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:
- 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'
status: active
-
id: 2
title: 'Mobile Workforce Rollout'
status: draft
links:
first: null
last: null
prev: null
next: null
meta:
total: 4
current_page: 1
per_page: 15
last_page: 1
properties:
data:
type: array
example:
-
id: 1
title: 'Cost Reduction'
status: active
-
id: 2
title: 'Mobile Workforce Rollout'
status: draft
items:
type: object
properties:
id:
type: integer
example: 1
title:
type: string
example: 'Cost Reduction'
status:
type: string
example: active
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: 4
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
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
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.'
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
name: 'Current State'
status: published
target_scenario:
id: 2
name: 'Optimized State'
status: draft
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:
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
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
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
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.'
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: snapshot_restore
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-04-13T21:34:33'
nullable: true
applied_to:
type: string
description: 'Must be a valid date.'
example: '2026-04-13T21:34:33'
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
org_unit_id: 10
primary: true
allocation_pct: 100
fte: 1
valid_from: '2025-01-01'
valid_to: null
is_active: true
properties:
data:
type: array
example:
-
id: 1
org_unit_id: 10
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
org_unit_id:
type: integer
example: 10
primary:
type: boolean
example: true
allocation_pct:
type: integer
example: 100
fte:
type: integer
example: 1
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
org_unit_id: 10
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
org_unit_id:
type: integer
example: 10
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
fte:
type: number
description: "nullable Effective FTE override for this placement window (0.01–1.00). If omitted, the actor's contractual FTE is used."
example: 0.6
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
org_unit_id: 12
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
org_unit_id:
type: integer
example: 12
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
fte:
type: number
description: "nullable Effective FTE override for this placement window (0.01–1.00). If omitted, the actor's contractual FTE is used."
example: 0.5
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
valid_to: '2025-10-01'
is_active: false
properties:
data:
type: object
properties:
id:
type: integer
example: 9
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: 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[scenario_id]'
description: 'Filter by scenario ID.'
example: 1
required: false
schema:
type: integer
description: 'Filter by scenario ID.'
example: 1
-
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_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_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/{org_unit}':
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_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_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
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}/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_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_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_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.'
example: '2025-09-01'
required: false
schema:
type: string
description: 'date The date to compute metrics as of.'
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}':
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: 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 deleted.'
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."
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: 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: 'filter[scenario]'
description: 'Scenario shorthand: baseline, current, or an id.'
example: current
required: false
schema:
type: string
description: 'Scenario shorthand: baseline, current, or an id.'
example: current
-
in: query
name: 'filter[scenario_id]'
description: 'Scenario identifier; overrides shorthand when present.'
example: 7
required: false
schema:
type: integer
description: 'Scenario identifier; overrides shorthand when present.'
example: 7
-
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:
text/plain:
schema:
type: string
example: "{\n \"data\": [\n {\n \"id\": 17,\n \"org_unit_team_id\": 4201, // planning metadata only\n \"title\": \"Staff Platform Engineer\",\n \"target_employment_type\": \"perm\",\n \"fte\": 1,\n \"target_annual_salary_cents\": 11850000,\n \"target_daily_rate_cents\": null,\n \"target_currency\": \"EUR\",\n \"valid_from\": \"2024-03-01\",\n \"valid_to\": null,\n \"scenario_id\": null,\n \"meta\": {\n \"cost_centre\": \"ENG-PLAT\",\n \"notes\": \"Supports platform migration.\"\n },\n \"team\": {\n \"id\": 8,\n \"name\": \"Platform Experience\",\n \"slug\": \"platform-experience\"\n },\n \"actor\": null,\n \"links\": {\n \"self\": \"https://tenant.orgonaut.test/api/v1/positions/17\",\n \"team\": \"https://tenant.orgonaut.test/api/v1/teams/8\",\n \"actor\": null\n },\n \"created_at\": \"2025-09-19T09:00:00Z\",\n \"updated_at\": \"2025-09-19T09:00:00Z\"\n }\n ],\n \"links\": {\n \"first\": \"https://tenant.orgonaut.test/api/v1/positions?page=1\",\n \"last\": \"https://tenant.orgonaut.test/api/v1/positions?page=3\",\n \"prev\": null,\n \"next\": \"https://tenant.orgonaut.test/api/v1/positions?page=2\"\n },\n \"meta\": {\n \"current_page\": 1,\n \"from\": 1,\n \"last_page\": 3,\n \"path\": \"https://tenant.orgonaut.test/api/v1/positions\",\n \"per_page\": 25,\n \"to\": 25,\n \"total\": 62\n }\n}"
400:
description: ''
content:
application/json:
schema:
type: object
example:
code: query.invalid_parameter
message: 'Unsupported scenario filter value: future'
details:
allowed:
- baseline
- current
- '{id}'
properties:
code:
type: string
example: query.invalid_parameter
message:
type: string
example: 'Unsupported scenario filter value: future'
details:
type: object
properties:
allowed:
type: array
example:
- baseline
- current
- '{id}'
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:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"id\": 24,\n \"org_unit_team_id\": 4201, // planning metadata only\n \"title\": \"Staff Platform Engineer\",\n \"target_employment_type\": \"perm\",\n \"fte\": 1,\n \"target_annual_salary_cents\": 11850000,\n \"target_daily_rate_cents\": null,\n \"target_currency\": \"EUR\",\n \"valid_from\": \"2024-03-01\",\n \"valid_to\": null,\n \"scenario_id\": null,\n \"meta\": {\n \"cost_centre\": \"ENG-PLAT\",\n \"notes\": \"Supports platform migration.\"\n },\n \"team\": {\n \"id\": 8,\n \"name\": \"Platform Experience\",\n \"slug\": \"platform-experience\"\n },\n \"actor\": null,\n \"links\": {\n \"self\": \"https://tenant.orgonaut.test/api/v1/positions/24\",\n \"team\": \"https://tenant.orgonaut.test/api/v1/teams/8\",\n \"actor\": null\n },\n \"created_at\": \"2025-09-19T09:15:00Z\",\n \"updated_at\": \"2025-09-19T09:15:00Z\"\n }\n}"
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: ''
example: 11
nullable: true
org_unit_team_id:
type: integer
description: 'The budgeted team for this seat (planning metadata only; does not assign a 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. 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:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"id\": 24,\n \"org_unit_team_id\": 4201, // planning metadata only\n \"actor_id\": null,\n \"title\": \"Staff Platform Engineer\",\n \"target_employment_type\": \"perm\",\n \"fte\": 1,\n \"target_annual_salary_cents\": 11850000,\n \"target_daily_rate_cents\": null,\n \"target_currency\": \"EUR\",\n \"valid_from\": \"2024-03-01\",\n \"valid_to\": null,\n \"scenario_id\": null,\n \"meta\": {\n \"cost_centre\": \"ENG-PLAT\",\n \"notes\": \"Supports platform migration.\"\n },\n \"team\": {\n \"id\": 8,\n \"name\": \"Platform Experience\",\n \"slug\": \"platform-experience\"\n },\n \"actor\": null,\n \"links\": {\n \"self\": \"https://tenant.orgonaut.test/api/v1/positions/24\",\n \"team\": \"https://tenant.orgonaut.test/api/v1/teams/8\",\n \"actor\": null\n },\n \"created_at\": \"2025-09-19T09:15:00Z\",\n \"updated_at\": \"2025-09-19T09:15:00Z\"\n }\n}"
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:
text/plain:
schema:
type: string
example: "{\n \"data\": {\n \"id\": 24,\n \"org_unit_team_id\": 4201, // planning metadata only\n \"actor_id\": null,\n \"title\": \"Principal Platform Engineer\",\n \"target_employment_type\": \"perm\",\n \"fte\": 1,\n \"target_annual_salary_cents\": 12600000,\n \"target_daily_rate_cents\": null,\n \"target_currency\": \"EUR\",\n \"valid_from\": \"2024-03-01\",\n \"valid_to\": null,\n \"scenario_id\": null,\n \"meta\": {\n \"cost_centre\": \"ENG-PLAT\",\n \"notes\": \"Role upgraded to lead migration.\"\n },\n \"team\": {\n \"id\": 8,\n \"name\": \"Platform Experience\",\n \"slug\": \"platform-experience\"\n },\n \"actor\": null,\n \"links\": {\n \"self\": \"https://tenant.orgonaut.test/api/v1/positions/24\",\n \"team\": \"https://tenant.orgonaut.test/api/v1/teams/8\",\n \"actor\": null\n },\n \"created_at\": \"2025-09-19T09:15:00Z\",\n \"updated_at\": \"2025-09-26T11:20:00Z\"\n }\n}"
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: ''
example: 11
nullable: true
org_unit_team_id:
type: integer
description: 'The budgeted team for this seat (planning metadata only; does not assign a 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. 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/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 computed deltas between a scenario and its source baseline.'
operationId: showComputedDeltasBetweenAScenarioAndItsSourceBaseline
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:
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:
id: 1
name: 'Current State'
slug: current-state
status: published
kind: planning
notes: null
live_from: null
live_to: null
locked_at: null
archived_at: null
is_read_only: false
source_scenario_id: null
created_at: '2025-12-15T09:00:00Z'
updated_at: '2025-12-15T09:00:00Z'
metrics:
headcount:
label: Headcount
scenario: 118
source: 123
delta: -5
fte:
label: FTE
scenario: 111.4
source: 116.4
delta: -5
cost:
label: 'Monthly Cost (cents)'
scenario: 142500000
source: 148000000
delta: -5500000
velocity:
label: 'Velocity (SP)'
scenario: 89
source: 95
delta: -6
teams:
-
key: 'team:44'
status: changed
department: Engineering
placements: []
badges: []
actors:
-
key: 'actor:98'
status: changed
badges: []
change_log:
-
entity_type: Team
action: updated
classification: updated
identity:
name: 'Platform Enablement'
created_at: '2026-03-01T10:15:00Z'
count: 1
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: object
properties:
id:
type: integer
example: 1
name:
type: string
example: 'Current State'
slug:
type: string
example: current-state
status:
type: string
example: published
kind:
type: string
example: planning
notes:
type: string
example: null
nullable: true
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: '2025-12-15T09:00:00Z'
updated_at:
type: string
example: '2025-12-15T09:00:00Z'
metrics:
type: object
properties:
headcount:
type: object
properties:
label:
type: string
example: Headcount
scenario:
type: integer
example: 118
source:
type: integer
example: 123
delta:
type: integer
example: -5
fte:
type: object
properties:
label:
type: string
example: FTE
scenario:
type: number
example: 111.4
source:
type: number
example: 116.4
delta:
type: integer
example: -5
cost:
type: object
properties:
label:
type: string
example: 'Monthly Cost (cents)'
scenario:
type: integer
example: 142500000
source:
type: integer
example: 148000000
delta:
type: integer
example: -5500000
velocity:
type: object
properties:
label:
type: string
example: 'Velocity (SP)'
scenario:
type: integer
example: 89
source:
type: integer
example: 95
delta:
type: integer
example: -6
teams:
type: array
example:
-
key: 'team:44'
status: changed
department: Engineering
placements: []
badges: []
items:
type: object
properties:
key:
type: string
example: 'team:44'
status:
type: string
example: changed
department:
type: string
example: Engineering
placements:
type: array
example: []
badges:
type: array
example: []
actors:
type: array
example:
-
key: 'actor:98'
status: changed
badges: []
items:
type: object
properties:
key:
type: string
example: 'actor:98'
status:
type: string
example: changed
badges:
type: array
example: []
change_log:
type: array
example:
-
entity_type: Team
action: updated
classification: updated
identity:
name: 'Platform Enablement'
created_at: '2026-03-01T10:15:00Z'
count: 1
items:
type: object
properties:
entity_type:
type: string
example: Team
action:
type: string
example: updated
classification:
type: string
example: updated
identity:
type: object
properties:
name:
type: string
example: 'Platform Enablement'
created_at:
type: string
example: '2026-03-01T10:15:00Z'
count:
type: integer
example: 1
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.'
example: 7
required: true
schema:
type: integer
'/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/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: 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'
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'
items:
type: object
properties:
id:
type: integer
example: 15
name:
type: string
example: 'Team CúChulainn'
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.'
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
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
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: 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
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
created_at:
type: string
example: '2025-01-10T09:00:00Z'
tags:
- Teams
put:
summary: 'Update a team.'
operationId: updateATeam
description: 'Requires `teams.update` 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: 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
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
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
delete:
summary: 'Delete a team.'
operationId: deleteATeam
description: 'Requires `teams.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:
- 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