Reference
Platform API
The HTTP API behind the CLI and the dashboard: API keys, projects, worlds, reviews and usage.
SoonThese pages describe Chronos Cloud, which opens from the waitlist. Running Chronos yourself? Self-host docs
Basics
- Base URL:
https://chronos.2090.tech. Every call is under/v1, JSON in and out. - Auth:
Authorization: Bearer ck_…(an API key), or an MCP access token (mt_…). - A person, not an agent: calls marked person need someone signed in to the dashboard. An API key or MCP token gets
403on them, since an agent in your terminal can hold either. Approving a merge, revealing a password and deleting a project are person-only. - Roles: a viewer can read but not change; owner-only calls say so.
KEY=$(sed -n 's/^api_key = "\(.*\)"/\1/p' ~/.config/chronos/cloud.toml)curl -s -H "Authorization: Bearer $KEY" https://chronos.2090.tech/v1/meAPI keys
chronos cloud login (or chronos init) makes one through the device sign-in below and keeps it in ~/.config/chronos/cloud.toml. Keys are stored by Chronos only as a SHA-256 hash and act as you, in your org. DELETE /v1/auth/key revokes the key it’s called with (chronos cloud logout does this).
Device sign-in
For tools without a browser of their own, as chronos init does it:
| Call | Answer |
|---|---|
POST /v1/auth/device | {device_code, user_code, verification_uri, interval, expires_in}. Show user_code (WDJB-MJHT) and verification_uri (…/device); the code lasts 15 minutes |
POST /v1/auth/device/poll {device_code} | every interval seconds: 428 while waiting; 200 {api_key, user: {id, login}} once approved; 410 expired or already used; 429: slow down by 5 s |
POST /v1/auth/device/approve {user_code} | person: what the /device page sends. 204, or 404 for no such waiting code |
Projects
| Call | Answer |
|---|---|
GET /v1/me | you, your org (id, name, plan, role), your orgs, and this month’s usage against the plan |
POST /v1/projects {name, git_remote?} | 201, or 200 if the org has it already (matched by git_remote as host/owner/repo, else name): {project, database_url, http_url, mcp_url}. 409 plan_limit past the plan |
GET /v1/projects | {projects: [{id, name, database: {id, state}, production}]} |
GET /v1/projects/{id} | as the POST’s answer |
GET /v1/projects/{id}/connection[?reveal=1] | {database_url, mcp_url, tools}: the password masked unless reveal=1, which is person and logged |
GET /v1/projects/{id}/usage | {disk_bytes, disk_limit_bytes, worlds, egress_bytes_month, awake_seconds_month, limits} |
POST /v1/projects/{id}/production {production} | owner: development or production for the project’s agents |
DELETE /v1/projects/{id} | owner: deletes the project and its database |
Worlds and reviews
| Call | Answer |
|---|---|
GET /v1/projects/{id}/worlds | {worlds: [world], production}, newest first. A world: {name, id, owner, parent, created, version, changes, status, review}; status is waiting or open |
GET /v1/projects/{id}/reviews | the waiting worlds, oldest first, each with its review (policy, reasons, version, changed_since) |
GET …/worlds/{w}/diff?limit&after&version&deletes | a page of changes {changes: [{key, before, after, columns}], next, version}; the first adds counts, owner, parent, review. deletes=true pages only the deleted rows |
POST …/worlds/{w}/merge {version, confirm_deletes?} | person: merges at that version; changed since, nothing merges. {merged, world, by} |
POST …/worlds/{w}/discard | person: drops the world |
POST …/worlds/{w}/undo | person: undoes its merge, skipping rows changed since: {undone, skipped} |
Activity and agents
| Call | Answer |
|---|---|
GET /v1/projects/{id}/activity?days=30 | per day and agent: rows written, worlds merged, forked and discarded; and a feed of recent actions |
GET /v1/projects/{id}/agents | each agent: its person, rights, the programs that used it (name, version, last seen), whether it merges itself |
POST /v1/projects/{id}/agents/{name}/revoke | person: disables the agent and drops its stored token |
Org and team
| Call | Answer |
|---|---|
GET /v1/org | {org, you, members: [{id, login, role}], invites: [{login, role, created, by}]} |
POST /v1/org/invites {login, role} | person, owner: an email or GitHub username; someone known joins at once (joined: true) |
DELETE /v1/org/invites/{login} | person, owner |
POST /v1/org/members/{user} {role} | person, owner: owner, member or viewer |
DELETE /v1/org/members/{user} | person, owner, or yourself to leave. 409 if it would leave no owner |
POST /v1/me/org {org} | person: which of your orgs the dashboard works in |
GET / POST /v1/me/notifications {email_reviews} | your email when a world waits for review |
App auth admin
| Call | Answer |
|---|---|
GET /v1/projects/{id}/auth | {enabled, signups, origins, access_ttl_s, endpoint, jwks_url, users, total}, newest 500 users |
POST /v1/projects/{id}/auth {enabled?, signups?, origins?, access_ttl_s?} | owner: the settings |
POST /v1/projects/{id}/auth/users/{user} {disabled} | member or owner: disabling ends the user’s sessions |
DELETE /v1/projects/{id}/auth/users/{user} | member or owner |
POST /v1/projects/{id}/auth/keys/rotate | owner: a new signing key; the newest two stay published |
The public sign-in endpoints your app calls are on Auth for your app.
Errors
{ "error": "plan_limit", "message": "the free plan has 2 databases and you have 2: delete one or upgrade" }| Status | Means |
|---|---|
400 | a body that isn’t what the call takes; the message says what |
401 | no token, or one that is unknown, expired or revoked |
403 | not yours to do: a viewer changing something, a non-owner on an owner call, an API key on a person call |
404 | no such project, world or call, or not in your org |
409 | a conflict: the plan’s limit, a world changed since the version given, an owner who’d leave no owner |
502 | the project’s database didn’t answer: it may be waking; try again |