PI Objectives — Endpoints¶
Base URL: https://rest.kendis.io/api/<companyPrefix>/… · Auth: HTTP Basic (email : API key).
1. List collections¶
GET /api/<companyPrefix>/collections
Returns all board collections visible to the authenticated user. Run this first to discover collection IDs for filtering boards.
curl -H "Authorization: Basic <base64-token>" \
"https://rest.kendis.io/api/<companyPrefix>/collections"
Response — 200 OK
{
"data": [
{ "id": "<collection-id-1>", "title": "Program Collection" },
{ "id": "<collection-id-2>", "title": "Operations Collection" }
]
}
2. List boards¶
GET /api/<companyPrefix>/boards
Returns boards visible to the authenticated user. Use the id returned here as the boardId on the next endpoint — there is no other reliable way to get a board ID.
Query parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
collectionIds |
string | No | Comma-separated collection IDs from List Collections. Plural form only — collectionId (singular) is silently ignored. |
# all boards
curl -H "Authorization: Basic <base64-token>" \
"https://rest.kendis.io/api/<companyPrefix>/boards"
# boards in specific collections
curl -H "Authorization: Basic <base64-token>" \
"https://rest.kendis.io/api/<companyPrefix>/boards?collectionIds=<collection-id-1>,<collection-id-2>"
Response — 200 OK
{
"data": [
{
"id": "<board-id>",
"title": "ADO PI#9",
"state": { "id": "<state-id>", "title": "Tracking" }
}
]
}
3. List objective groups¶
GET /api/<companyPrefix>/objective-groups
Returns objective groups for a single board, each with its objectives and (optionally) their linked board items and sub-items.
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
boardId |
string | Yes | — | The PI Board ID, from List Boards |
completionType |
string | No | 2 (or 6 when OKRs are enabled on the board) |
Which completion calculation to project onto objective.completion. See reference. |
additionalColumns |
string | No | — | Set to 1 to add a denormalised teamsAndSprints array on each linked item / sub-item. See reference. |
curl -H "Authorization: Basic <base64-token>" \
"https://rest.kendis.io/api/<companyPrefix>/objective-groups?boardId=<board-id>&completionType=2&additionalColumns=1"
Response — 200 OK (ADO-connected board, truncated; IDs replaced with placeholders)
{
"data": [
{
"id": "<group-id>",
"title": "Team Sky",
"type": "TeamGroup",
"completion": 25.0,
"bvPlan": 7.0,
"bvActual": 8.0,
"achievement": 114.29,
"status": { "id": "<status-id>", "title": "Planned", "category": "ToDo" },
"objectives": [
{
"id": "OBJ-23",
"title": "Objective for 5",
"type": "Objective-Committed",
"completion": 25.0,
"bvPlan": 7.0,
"bvActual": 5.0,
"sequence": 17371185352.0,
"status": { "id": "<status-id>", "title": "Planned", "category": "ToDo" },
"linkedItems": [
{
"id": "<kendis-item-id>",
"tfsId": "415",
"tfsURL": "https://dev.azure.com/<org>/<project-guid>/_workitems/edit/415",
"tfsItemTypeIcon": "https://tfsprodweu5.visualstudio.com/_apis/wit/workItemIcons/icon_trophy?color=773B93&v=2",
"title": "Feature 1",
"type": "Feature",
"issueType": "Feature",
"storyPoints": 0.0,
"completion": 0.0,
"status": { "id": "<status-id>", "title": "New", "category": "ToDo" },
"teamsAndSprints": [
{
"sequence": 23121024284.0,
"team": { "id": "<team-id>", "title": "Team Earth", "label": "EART", "color": "#f39c12" },
"sprint": { "id": "<sprint-id>", "title": "Iteration 3", "label": "S3", "startDate": 1777852800000, "endDate": 1778803200000, "isIPSprint": false }
}
],
"subItems": [
{
"id": "<sub-item-kendis-id>",
"tfsId": "1178",
"tfsURL": "https://dev.azure.com/<org>/<project-guid>/_workitems/edit/1178",
"tfsItemTypeIcon": "https://tfsprodweu5.visualstudio.com/_apis/wit/workItemIcons/icon_book?color=0098C7&v=2",
"title": "Story 2",
"type": "UserStory",
"issueType": "Story",
"storyPoints": 0.0,
"status": { "id": "<status-id>", "title": "New", "category": "ToDo" }
}
]
}
]
}
]
}
]
}
Jira-connected boards
On Jira boards, replace tfsId → jiraId, drop tfsURL (the Jira URL is built from jiraKey), and
add jiraKey (e.g. FEA-123) and jiraIssueTypeId. tfsItemTypeIcon is absent. Everything else is
identical. See Compatibility notes.
completionType reference¶
completionType selects which completion calculation the server projects onto each objective.completion.
The valid values mirror the dropdown in the in-app Objectives screen.
| Value | Calculation | Notes |
|---|---|---|
1 |
Linked-item status | Counts only the direct linked items' statuses |
2 |
Children linked-item status (default when OKR mode is off) | Counts direct items + their sub-items by status |
3 |
Children linked-item estimate | Uses estimates (story points / effort) of children, not status counts |
4 |
Team-story status | Filters to stories under each Team's iterations and counts by status |
5 |
Team-story estimate | Same as 4 but uses estimates |
6 |
OKR / KR-weighted formula (default when OKR mode is on) | Uses the Key Result weighting formula — only meaningful when OKRs are enabled on the board |
Defaulting behaviour
- OKR mode off and
completionTypeomitted → server falls back to2. - OKR mode on and
completionType=6→ server uses the OKR formula path. - OKR mode on and
completionTypeis1–5→ respected as-is. - Any unrecognised value silently falls back to the OKR / status default for that board.
Tip
When the chosen completionType produces no data (e.g. 4/5 on a board with no Team mappings),
objective.completion may be 0 or null — both are valid; treat null as "not computable".
additionalColumns¶
additionalColumns=1 adds a denormalised teamsAndSprints array to every linkedItems and subItems
entry. Omit it (or set 0) for a leaner payload without that array.
Because team / sprint titles and labels are denormalised onto the entry, a single call to
/objective-groups?additionalColumns=1 is enough — no separate /teams or /sprints calls are needed
for this endpoint.
See the teamsAndSprints entry for the shape.