Revisions
Enscrive records revisions of your tenant’s data automatically: a revision every 15 minutes through the current UTC day, and one daily revision for each prior day. A revision is a point-in-time restore point — restoring one returns every corpus in your tenant to its state at that moment.
Three surfaces expose the same capability, all under normal API-key auth (no admin capability required):
- the Timeline page in the developer portal (
/revisions), - the
/v1endpoints documented below, - the CLI:
enscrive revisions list,enscrive revisions show,enscrive restore --revision.
A note on naming. The API paths and field names say
backup(/v1/backups,backup_id) — that is the storage-layer vocabulary. The product concept is a revision; the portal, the CLI, and these docs use that word.
What a revision is
Each revision records, for the moment it was taken:
- the point in time (
timestamp), - total embedding points and compressed size,
- per-collection point counts and content checksums,
- whether the artifact is encrypted, and its parentage in the revision chain,
expires_at— when retention will purge it (see Retention by tier).
Revisions are recorded by the platform on a fixed cadence; you do not create them through the API.
The Timeline
The portal’s Timeline page lists every revision recorded for the active tenant, newest first, grouped by UTC day. The two generations are badged distinctly:
- Today — revisions on a 15-minute cadence.
- Prior days — one daily revision per day.
Each row expands to the full detail (per-collection points, checksums, parent revision, expiry), and carries the Restore this revision… action, which runs the same preview → typed-confirm → async-job flow described below.
List revisions
GET /v1/backups?limit=20&cursor=…
| Param | Notes |
|---|---|
limit | Page size; the server defaults to 20 when omitted |
cursor | Opaque pagination cursor — pass the previous page’s next_cursor |
Returns backups (an array of revision summaries: backup_id, timestamp, backup_type, total_points, compressed_bytes, encrypted, optional label and expires_at), plus total and next_cursor.
CLI: enscrive revisions list [--limit N] [--cursor C].
Get a revision
GET /v1/backups/{backup_id}
Returns the full detail, including the collections map (point_count + checksum per collection), parent_backup_id, encryption flags, and expires_at.
A revision belonging to another tenant is a 404 — the lookup is keyed by (tenant_id, backup_id) at the source of truth, so a foreign id simply does not resolve. The isolation is structural, not a filter.
CLI: enscrive revisions show <revision-id>.
Verified restore
Restore is tenant-wide and point-in-time: every corpus in the tenant returns to its state as of the named revision’s timestamp. Data added or changed after that moment is not in the restored state. Because of that, the flow has four deliberate stages.
1. Dry-run preview
POST /v1/restore/dry-run
{ "target_time": "2026-06-11T14:45:00Z" }
A synchronous read — nothing is executed. Reports can_restore, backups_in_chain, total_points, estimated_restore_time_seconds, glacier_backups (revisions in cold storage, which restore slower), and warnings. The portal fetches this preview the moment you open the restore dialog; the CLI’s enscrive restore --revision <id> --dry-run resolves the revision’s timestamp and runs the same validation.
2. Typed confirmation
POST /v1/restore
{ "backup_id": "<revision-id>", "confirm": "<revision-id>" }
confirm must repeat the exact revision id. Missing or mismatched → 400, with an error that states the policy: restore is destructive and tenant-wide, and restores never default to the latest revision — you confirm exactly the revision being restored. The portal enforces the same gate by making you type the revision id; the CLI requires --confirm plus an interactive re-type of the id (non-TTY and JSON/agent invocations are refused before any API call; managed mode additionally requires --confirm-token).
3. Always-async launch
A valid request returns 202 Accepted with a job_id and poll_url before any restore work happens — the standard async-mutation contract. Poll it like any other job: the job moves through resolving → restoring → verifying phases, with the row kept visibly alive by a heartbeat while the restore runs.
CLI: enscrive restore --revision <id> --confirm polls to terminal status by default (--timeout-secs, default 1800); pass --async to return immediately with the launched job.
4. Convergence-verified completion
After the restore finishes, the server verifies it per corpus before claiming success: the restored embedding store is compared against the document catalog, corpus by corpus, requiring an exact match in both directions.
- Every corpus matches → job
completewithparams.verified: true. - Any corpus diverges → job
failed, naming the exact shortfall (substrate=X, catalog=Y, corpus=Z). A restore that lands data in a different state than the current catalog is reported as exactly that — never silently stamped complete. - The check itself can’t be trusted (stats absent, stale, or unreadable) → job
failedas unverifiable. Unknown is never treated as zero and never as converged.
The CLI mirrors this honesty: a restore job that reaches a terminal state without params.verified == true exits non-zero.
Retention by tier
Retention follows a two-generation policy:
- Current day — revisions every 15 minutes.
- Day rollover — the last revision of each UTC day becomes that day’s daily revision; the day’s other revisions expire.
- Dailies window — daily revisions are kept for the most recent days per your subscription tier:
| Tier | Daily revisions kept |
|---|---|
| Solo | 7 days |
| Pro | 14 days |
| Enterprise | 30 days |
Two safety properties apply regardless of expiry: the two newest revisions are never purged (a stalled recording cadence must not let the clock destroy the only restorable artifacts), and the windows come from the active rate card — if no retention policy is declared there, nothing is purged.
Every revision’s expires_at is visible on the list and detail endpoints, and on the Timeline.
Erasure
Tenant erasure permanently purges revisions. When a tenant is erased, a tombstone is written first: from that instant no new revisions are recorded and restore/import of that tenant’s data is refused — including via disaster-recovery paths. Then every revision artifact is permanently deleted, both the catalog exports and the embedding-store artifacts. The operation is idempotent, and the tombstone-first ordering means a crash mid-erasure can never leave a window where the erased tenant is restorable.