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.

How to address a cycle
SpellingExampleWhen to use it
A cycle id/api/v1/cycles/2026-C03/boardPinning one quarter. The id comes from the cycles endpoint.
A year and shorthand/api/v1/cycles/2026/q3/boardThe same grammar the product's own URLs use.
The literal current/api/v1/cycles/current/boardFollowing 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

Fields on an objective
FieldWhat it is
idThe objective's id. Stable across polls.
titleWhat it says on the card.
teamIdJoins to teams[].id. null means company level.
ownerJoins to people[].id, or null.
progress0–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.
keyResultsThe key results, below.

keyResults

Fields on a key result
FieldWhat it is
idThe key result's id.
titleWhat it says on the card.
ownerJoins to people[].id, or null.
unitcurrency, percent or count — null while nothing measures this key result.
directionWhich way is good: up or down. null while nothing measures it.
baselineWhere it started, or null.
targetWhere it is going, or null.
kindmetric, 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.
doneOnA milestone's done day, YYYY-MM-DD, or null until it is done. Always null on a metric key result.
currentThe reading for this cycle, or null.
progress0–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.
unmeasuredWhy 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.