Read a board
GET https://app.improbable.work/api/v1/cycles/2026-C03/board
One cycle's board: its objectives, their key results, the teams that own them and the people named on them. This is the endpoint an integration polls.
Naming the cycle
Three spellings resolve to the same document, and none of them replaces the others.
| Spelling | Example | When to use it |
|---|---|---|
| A cycle id | /api/v1/cycles/2026-C03/board | Pinning one quarter. The id comes from the cycles endpoint. |
| A year and shorthand | /api/v1/cycles/2026/q3/board | The same grammar the product's own URLs use. |
The literal current | /api/v1/cycles/current/board | Following the calendar — whatever is running today. |
They fail differently, and the difference is worth knowing. An id is
checked for shape only, so a well-formed cycle that nobody ever cut still
answers 200 with an empty board; a malformed one is 400. A shorthand is
a lookup, so one no cycle answers to is 404.
Request
curl -H "Authorization: Bearer imp_key_…" \ https://app.improbable.work/api/v1/cycles/2026-C03/board
Response
data holds the board document. It has 7
top-level fields and no others.
{
"ok": true,
"data": {
"cycle": {
"id": "2026-C03",
"label": "Q3 2026",
"startsOn": "2026-07-01",
"endsOn": "2026-09-30"
},
"generatedAt": "2026-09-07T04:12:55.108Z",
"teams": [
{ "id": "t_growth", "name": "Growth", "parentTeamId": null }
],
"objectives": [
{
"id": "o_9f2c",
"title": "Make the first week obvious",
"teamId": "t_growth",
"owner": "p_amara",
"progress": 61,
"keyResults": [
{
"id": "kr_31a8",
"title": "Activation within 7 days",
"owner": "p_amara",
"unit": "percent",
"direction": "up",
"baseline": 22,
"target": 40,
"kind": "metric",
"doneOn": null,
"current": 33,
"progress": 61,
"unmeasured": null
},
{
"id": "kr_47d0",
"title": "Hold support tickets per new account down",
"owner": null,
"unit": "count",
"direction": "down",
"baseline": null,
"target": null,
"kind": "metric",
"doneOn": null,
"current": null,
"progress": null,
"unmeasured": "no_target"
}
]
}
],
"people": [
{
"id": "p_amara",
"name": "Amara Osei",
"jobTitle": "Head of Growth",
"photoUrl": null,
"tint": "#7C5CD6"
}
],
"features": [
{ "feature": "departments", "enabled": true, "effectiveCycle": "2026-C03" }
],
"departments": [
{
"id": "d_growth",
"name": "Revenue",
"purpose": "Bring in and keep the revenue",
"leaderIds": ["p_amara"]
}
]
}
}cycle
id, label, startsOn, endsOn. The dates are YYYY-MM-DD and endsOn is
inclusive — the last day of the cycle, not the first day after it. Both are
null for a cycle nobody cut.
generatedAt
An ISO timestamp for when the document was built. Use it to tell one poll from
the next; do not use it as a cache validator, because there is no ETag and
nothing here promises a stable body between reads.
teams
id, name, parentTeamId. A parentTeamId of null means the team reports
straight into the company. Where this cycle has its own org frame, the parent
is that cycle's; otherwise it is the team's standing one.
objectives
| Field | What it is |
|---|---|
id | The objective's id. Stable across polls. |
title | What it says on the card. |
teamId | Joins to teams[].id. null means company level. |
owner | Joins to people[].id, or null. |
progress | 0–100. The mean of the objective's metric-measured key results — an unmeasured key result is not counted as zero, and a milestone is not averaged in. null where it has no metric-measured key result, such as an objective made only of milestones: there is nothing to average. |
keyResults | The key results, below. |
keyResults
| Field | What it is |
|---|---|
id | The key result's id. |
title | What it says on the card. |
owner | Joins to people[].id, or null. |
unit | currency, percent or count — null while nothing measures this key result. |
direction | Which way is good: up or down. null while nothing measures it. |
baseline | Where it started, or null. |
target | Where it is going, or null. |
kind | metric, or milestone for a key result that is done or not done by the end of its cycle. A milestone has no unit, direction, baseline, target or current. |
doneOn | A milestone's done day, YYYY-MM-DD, or null until it is done. Always null on a metric key result. |
current | The reading for this cycle, or null. |
progress | 0–100, computed on the server so that two consumers cannot disagree about what 42% means. A milestone reads 100 if it was done inside the cycle, else 0. null while the key result is unmeasured: there is no score yet, and a 0 would read as failing. |
unmeasured | Why the key result cannot be scored yet: no_metric (nothing measures it), no_target (it has a metric and needs a target) or metric_requested (waiting on a requested metric). progress and current are null beside it, and it is left out of its objective's progress. null when it can be scored, milestones included. |
people
id, name, jobTitle, photoUrl, tint. The id is the key every
objective and key result references, so people joins to the rest of the
document. photoUrl and tint are null where the product will not vouch for
the value.
features
feature, enabled, effectiveCycle. Which capabilities the workspace has
turned on, and the first cycle each applies from. Always present, even when
every feature is off: "no row is off" is an answer, and this is what lets you
tell a workspace with departments off from a document built before the key
existed. A feature turned off keeps its settings, so you may read
enabled: false beside a cycle. effectiveCycle is null where the feature
names none.
departments
id, name, purpose, leaderIds. The workspace's divisions.
Present only where departments are on — an absent key and an empty array
are different answers: [] is a workspace that turned departments on and has
not named one, and no key at all is a workspace where the product holds no
department data. leaderIds are people[].ids, so a department joins to the
rest of the document; a leader this document does not publish as a person is
left out rather than emitted to join to nothing.
A department carries no headcount and no start or end dates here. The headcount is the company's roster cut by division, which is exactly the kind of total this endpoint does not publish, and the dates are administrative. Which teams sit in a department is not published either.
What the document does not carry
A board document may publish less than a public embed link shows, and never more. That rule runs one direction only, and it is why the list below is a promise rather than a backlog.
No email addresses. No directory ids. No employment records, salary bands, managers, start dates, countries or company. No person's department — the divisions are published as records of their own, and a per-person link would point at rows this document did not send. No department headcounts, no department dates, and nobody's name against the turning-on of a feature. Nothing a person wrote about themselves in their own profile — no pronouns, phone numbers, timezones or bios. No metric definitions behind a key result, and no time series of actuals. No canvas coordinates on an objective, and no workspace id anywhere.
If a field you need is not in the list above, it is not omitted by oversight. The honest way to publish it would be a new endpoint with its own name, not a widening of this one.
Finding an id
Call the cycles endpoint — GET {CYCLES_PATH} — and use the id
or the path it hands back.