Skip to content
Esc
↑↓navigate↵open⌘Jpreview
On this page

History

Every save of an App is a revision. List them, read one, and restore an earlier one.

What you can do

See every revision. Each save of an App makes a new revision. History lists them newest first, with when each was saved, which contract it was written against, and which one is live.

Read an earlier revision. Open a revision to see its source: the package’s files, or the module, UI, and styles of an App on a dated contract.

Restore one. Restoring saves the earlier revision’s source as a new revision. Nothing is deleted: the revision you restored over stays in History, so you can restore it back. App data does not change. Connector approvals for services or levels the restored revision does not declare are removed, and have to be given again if you restore back.

Where it lives. In App settings, choose History (charm.ing/<owner>/~/apps/<app>/settings/history). Agents use the MCP tools or the HTTP API below.

Who can use it. The App’s owner, members of the team that owns it, and anyone the owner invited as an editor. Viewers and visitors cannot.

How far back. Your plan decides how many earlier revisions History keeps in reach: 10 on Free, more on paid plans. Older revisions are kept but cannot be read or restored until the plan reaches them.

Technical

Copy this prompt for your agent

Restore my Charming app to an earlier revision. Read
https://charm.ing/docs/capabilities/history.md first. List the
app's revisions, show me what changed in the one I pick, then
restore it and tell me the new revision number.

How an agent performs this job

Over MCP, three tools use the same checks as the routes below: list_app_revisions({ app_id, before?, limit? }), get_app_revision({ app_id, revision }), and restore_app_revision({ app_id, revision, expected_revision, idempotency_key? }). Pass the currentRevision from the list as expected_revision. A package restore that does not publish at once returns a build; poll it with get_app_build({ build_id }). Failures come back as tool errors with the error kinds in the table below.

From the CLI, charming apps history list <app-id>, charming apps history get <app-id> <revision> [--out <dir>], and charming apps history restore <app-id> <revision> --yes [--wait] run the three routes below. restore reads the current revision for If-Match, supplies the Idempotency-Key, previews the request offline with --dry-run, and with --wait follows a package revision’s build until it publishes.

Over HTTP, three routes under the App, all accepting a user token (chrm_user_*) or a session cookie. They are in the OpenAPI document as list-app-revisions, get-app-revision, and restore-app-revision.

Route Does
GET /api/v1/apps/{appId}/revisions Lists revisions newest first. before and limit (default 50, at most 200) page through them.
GET /api/v1/apps/{appId}/revisions/{revision} Returns one revision and its source.
POST /api/v1/apps/{appId}/revisions/{revision}/restore Saves that revision’s source as a new revision.

Each listed revision carries revision, createdAt, manifestVersion (the contract, such as 2026-10-25 for a package or 2026-10-16 for a module contract), current (the desired revision), live (the revision serving visitors), and restorable. The list also returns currentRevision, historyLimit (how many earlier revisions the plan keeps), and nextBefore, which you pass as before to read the next page; it is null on the last page.

restorable reflects the checks restore runs on the stored source, and restore runs them again, so a marked revision can still be refused if its source stopped validating in between.

A revision’s source is source.files for a package revision, or source.module, source.ui, and source.styles for a module-contract revision.

A restore needs the current desired revision as If-Match: "<N>" (or { "expectedRevision": N } in the body) and an Idempotency-Key header. A module-contract revision publishes immediately and answers 200 with the new revision. A package revision publishes from its retained build: usually at once (200), otherwise the response is 202 with a build whose statusUrl you poll until state is published. The App keeps running its current revision until then. Repeating a package restore with the same Idempotency-Key returns the same build.

The revision you send is a compare-and-set. If two people, or a person and an agent, restore from the same revision at once, one restore wins and the other answers revision_mismatch without changing the App. List History again, check what changed, and restore against the new current revision.

GET /api/v1/apps/3f1c…/revisions?limit=2
Authorization: Bearer chrm_user_…

→ 200 { "ok": true, "currentRevision": 6, "historyLimit": 10, "nextBefore": 5,
        "items": [
          { "revision": 6, "manifestVersion": "2026-10-25", "current": true, "live": true, "restorable": false, … },
          { "revision": 5, "manifestVersion": "2026-10-25", "current": false, "live": false, "restorable": true, … } ] }

POST /api/v1/apps/3f1c…/revisions/5/restore
Authorization: Bearer chrm_user_…
If-Match: "6"
Idempotency-Key: 9b7e2c40-…

→ 200 { "ok": true, "state": "published", "intent": "restore", "sourceRevision": 5, "revision": 7, … }

Across contracts. An App moved to the package contract can restore a revision from before the move. Charming validates that source under its own dated contract and publishes it as a new revision, so the App goes back to that contract. See Migrate to packages.

Errors

Error kind Status Meaning
forbidden 403 The caller can open the App but cannot change it.
not_found 404 The App or revision does not exist, or the caller cannot open the App.
precondition_required 428 The restore sent neither If-Match nor expectedRevision.
idempotency_key_required 400 The restore sent no Idempotency-Key.
revision_mismatch 412 The App was saved or restored since you listed it. The ETag header and the error’s current field carry the current revision.
version_limit_exceeded 429 The revision is older than the plan’s History window. The error carries plan, max, and upgradeUrl.
version_not_restorable 422 The revision is missing, has no completed build, or no longer passes its contract. Upgrading does not fix this.
version_history_unavailable 409 The App’s contract has no History that can be restored.
app_build_capacity / app_build_unavailable 429 / 503 Builds are busy or unavailable. Retry later.

The plan window is checked first, so a revision that is both out of reach and not restorable answers version_limit_exceeded.

Was this page helpful?