Site platform API (`window.atrio`)
Site platform API (window.atrio)
Team plan and above. Use this for new agent-built apps that keep state in JSON (trackers, forms, timeline data).
Migrating Vincent-style Circulon review sites (copy-edit-in-place, devclient.js, /capi/comments, devlog + undo) is not this API — see CIRCULON_HOST.md. Those pages need HTML + a same-origin /api/circulon adapter on Atrio; do not rewrite them to atrio.store unless rebuilding the UX.
Sites opt in with platformApi: true (push_site sets this). The viewer injects a bridge; the iframe calls window.atrio, the parent posts to /api/site, and every call re-checks access.
Quick start
const { signedIn, email } = await atrio.user();
// Shared state for everyone allowed to edit (org members on an org-scoped link, etc.)
const doc = await atrio.store.get("circulon-timeline", { scope: "shared" });
let revision = doc.revision ?? 0;
let state = doc.value ?? { rows: [] };
state.rows.push({ id: 1, label: "Milestone A" });
const saved = await atrio.store.set("circulon-timeline", state, {
scope: "shared",
ifRevision: revision,
});
if (saved.error === "conflict") {
// Another editor saved first — reload and merge, or prompt the user.
const fresh = await atrio.store.get("circulon-timeline", { scope: "shared" });
revision = fresh.revision;
// ...
} else {
revision = saved.revision;
}
// Undo last change to this key (server-side history, up to 50 steps per key)
await atrio.store.undo("circulon-timeline", { scope: "shared" });
Store (atrio.store)
| Method | Description |
|---|---|
get(key, { scope }) | Returns { value, revision }. Missing key → { value: null, revision: 0 }. |
set(key, value, { scope, ifRevision }) | Saves JSON (max 64 KB). Returns { ok: true, revision } or HTTP 409 { error: "conflict", revision, value } if ifRevision does not match. |
list({ scope }) | { items: [{ key, value, revision }] } (up to 1000 keys). |
delete(key, { scope }) | Removes a key. |
history(key, { scope, limit }) | Prior snapshots { items: [{ revision, value, at, by }] } for undo UI. |
revert(key, { scope, revision }) | Restores a historical revision as a new head (append-only). |
undo(key, { scope }) | Restores the previous revision (convenience wrapper). |
Scopes
shared— one namespace per site. Writes require a member (or a link with store writes enabled).me— per signed-in user. Requires sign-in.
Simultaneous edits: There is no live merge/cursor sync. Use ifRevision on set for optimistic concurrency; on conflict, reload get and reconcile. This matches “editable tool with shared data,” not Google Docs co-editing.
Data (atrio.data.query)
Read-only SQL over attached datasets (same DuckDB sandbox as Ask the data). Requires data access on the link.
const rows = await atrio.data.query(
"select region, sum(amount) as total from data group by 1",
);
User (atrio.user)
{ signedIn: boolean, email: string | null }
The iframe cannot read the Atrio session cookie; this is the supported way to know who is signed in.
HTML / agent versions (separate from store)
- Each MCP
push_site/push_dashboardcreates a new content version (what you see under History in the library). publish_versionchooses which version viewers see.- Undo for the whole page = restore an older content version in the library, or republish via MCP.
- Undo for in-app edits =
atrio.store.undo/reverton your state keys.
Migrating from Cloudflare / Dots
Pick one:
| Source | Doc |
|---|---|
Circulon Worker (devclient.js, /capi/*, pin comments, copy publish) | CIRCULON_HOST.md (spec; host on Atrio with circulon-compat API) |
New interactive app (state in JSON, no /capi) | This file (window.atrio.store + MCP below) |
New apps (this API):
- Connect MCP to
https://app.atrio.cc/api/mcp; callatrio_whoamiwith Global Meyerworkspace_id. - Publish with
push_site(multi-file) orpush_dashboard(single HTML). Useset_audience(orgfor all of Meyer,privatefor Restricted). - Move persisted state from Workers KV / localStorage into
atrio.store(scope: "shared"). - On load:
get→ render; on save:setwithifRevision; wire Undo tostore.undoorhistory+revert. - Point Dots at
https://app.atrio.cc/site-apiandMCP_TOOLS.md.
Security
Iframe is sandboxed; only the bridge can call the API. Values are JSON only; no arbitrary server code from the site.