Embed links

An embed link is a public, revocable URL to one cycle's canvas. Anyone holding it can read the board — no account, no key, no sign-in — and any page can frame it.

It is the third door onto the board, and it is the one that needs nothing from this documentation to use: you paste it into a wiki and a board appears. This page is here because it is also the disclosure floor the other two doors are measured against.

Making one

In the product: the header's Export dialog, Embed row. Name it, and make two decisions.

The name is required. A workspace accumulates links, and a register whose rows read "Current cycle · 12 August" three times over is a register nobody can safely revoke from — opening each URL to find out which is which is not an audit, it is spending a view on every link.

Which cycle:

What a link is pinned to
ChoiceWhat it does
Always show this cyclePinned. Good for a retrospective or a board pack.
Always show the current cycleFollows the calendar. Paste once, never revisit.

And whether to Include the masthead — the mission, the BHAG and that cycle's year goals written above the board. It is off unless you tick it; see what a link discloses, below.

Then Create embed link. The link is listed in that panel afterwards, with a Copy link and a Copy embed code button, and it stays copyable for as long as it is live — unlike an API key, whose plaintext is shown once.

Workspace settings → Embeds is where it is found again months later: every link the workspace has ever published, live and retired, newest first, with who published it, when, and what has been opening it. That page publishes nothing — it is the record and the off switch. Publishing belongs beside the export the link was wanted for.

What it looks like

https://app.improbable.work/embed/emb_<token>

No query parameters. The token in the path is the whole of the authorization: there is no session, no account and no cookie behind it, and nothing about the request except that token decides what comes back.

The panel's Copy embed code button gives you the <iframe> around it.

What it shows, and what it cannot be made to show

One workspace, one cycle, one view — the OKR canvas. There is no other view a link can carry, and a canvas served this way is re-clamped on every render, so an embed cannot be steered into the roster.

The cycle is the server's decision, not the caller's. A pinned link shows its cycle forever; an unpinned one resolves "current" against the workspace's calendar on every request. A cycle named in the URL is ignored — otherwise "always show the current cycle" would quietly mean "read any cycle you can name".

An embed is not team-scoped. A link publishes the whole cycle's canvas, every team frame in it.

What it discloses

Names, job titles, avatars and tints for the people actually on that board — not the whole roster. Teams, objectives, key results, progress, stickies and the canvas layout.

Not: email addresses, directory ids, countries, pronouns, phone numbers, timezones, bios, companies, employment types, departments, salary bands, employee ids, cost centres, managers, or employment start and end dates. Not the long-range strategy document either — the mission, the core values, the three-to-five-year picture and the year goals are dropped, not merely hidden — unless the link was minted with Include the masthead ticked, in which case the mission, the BHAG and that cycle's year goals are written above the board as they are in the product. The core values and the picture are never published. Not a metric's source endpoint, its notes, or the workspace's other metrics' history: the actuals published with a board are narrowed to the metrics that board's key results reference.

This is the line the API key doors are held to. A board document from /api/v1 may publish less than an embed link shows, and never more.

Headers

Every embed response carries Cache-Control: private, no-store, Referrer-Policy: no-referrer, X-Robots-Tag: noindex, nofollow, and Content-Security-Policy: frame-ancestors *. Framing by anybody is the product, so X-Frame-Options is deliberately not sent — it has no allow-any value.

Access-Control-Allow-Origin: * is sent here, unlike on the API-key doors. It is safe for exactly the reason it would not be there: the token is already the whole authorization, the reads are read-only, and nothing reads a cookie. It is also necessary — an embedder that frames the page with a sandbox withholding allow-same-origin sends Origin: null even to the page's own host, and without the header the canvas renders "No objectives for this cycle yet": not an error, a confident lie about a workspace that has eight.

Turning one off

Revoke it, from either place it is listed: the Export dialog's Embed row, or Workspace settings → Embeds. Both ask you to type the link's own name back before the revoke fires. There is no undo of any kind — the URL is already pasted into somebody's wiki, it stops on its next load, and a replacement is a different URL somebody has to go and put there by hand.

It takes effect on the next request — the row is kept as history and every read checks it, and no-store means no intermediary is still holding a copy to serve. A retired row keeps its place in the register and stops carrying its token: the string opens nothing, and what it can still do is travel.

Nothing expires a link. There is no TTL, and revocation is the only off switch. A revoked link and a link that never existed both answer 404, so probing cannot tell them apart.

Do not build an integration on this

The link renders a page, and behind that page is a JSON read the page uses. It is not a versioned contract, it is not an allowlist anybody outside this product designs against, and it will change shape when the canvas does.

If you are writing code, use the key-authenticated API: GET {CYCLES_PATH} and the board endpoint. That surface is versioned precisely because it is consumed by code nobody here can edit. Start at Overview.

There is also no rate limit on the public read, which is a fact about the current implementation rather than a promise. Poll the versioned API instead.