revision (and its ETag). Changes add revisions and never edit old ones: only redaction, the plan’s retention and purges of deleted data remove anything from history.
In the web app, open a record’s History tab. Through the API, reading history needs records:read, and reverting records:write (and records:read to preview a revert).
What a revision records
Each revision has:operation:create,update,publish,unpublish,delete,restore,revert,migrateormergeactor: the person or API key that made the change (typeisuser,api_keyorsystem), with its namevia: the client it came through:ui,api,cli,sdk,mcp,systemorschedulernote: the change note, if one was sentchanged_fields: the keys of the fields (andpublished_atordeleted_at) it changeddraft: for amerge, the draft it merged, with who started it and who edited itreverted_to_revision: for arevert, the revision it went back to
data, the record’s values as of that revision, and changes, each with before and after. Changes name a field by its current key and its stable field_id, so history stays readable after a field is renamed.
List and read revisions
Lists are newest first. Filter byoperation, by actor_id (a usr_ or key_ ID) or by a created_at range:
rev_ ID (revision_id); GET /v1/revisions/{revision_id} reads it. History stays readable while a record is deleted.
Compare two revisions
GET /v1/records/{record_id}/diff?from=3&to=7 returns the changes between any two revisions, field values and published_at alike. In the web app, open a revision and pick Compare with #7.
Revert
Reverting writes a newrevert revision with an earlier revision’s values; nothing in between is lost. In the web app, open a revision, choose Revert to this revision, check the changes and choose Revert record. Through the API, preview first with dry_run, which writes nothing:
- A field whose type changed since gets its old value converted, as a field migration would. A value that can’t be converted fails the revert (
422), and a field whose old and new types have no conversion keeps its current value. - Fields added since, and fields whose old value was redacted, keep their current values. Deleted fields don’t come back.
- The result is validated like any update, and whether the record is published doesn’t change. Reverting a live record changes what is live.
409). Send If-Match to revert only if nobody changed the record since you looked. To review an old version before it goes live, start a draft from it with from_revision instead.
Change notes and attribution
Every change is attributed to the person or API key that made it. Give each tool, agent and CI job a key of its own, so history tells them apart. To say why, sendNohead-Change-Note when you create, update, publish, unpublish, delete, restore or revert a record, change records in a batch, merge a draft, or change a schema (fields, collections, field migrations). The note is percent-encoded UTF-8, as encodeURIComponent encodes it, at most 500 characters once decoded; plain ASCII can be sent as it is.
Nohead-Client, so their changes show via: sdk. Changes made by a schedule show actor.type: system and via: scheduler. The MCP server requires a note on every revision it makes.
Redact a field’s past values
When a value should never have been stored, such as personal data or a secret, owners and admins can remove it from history in the web app. On the record’s History tab, choose Redact…, pick the Field and the Values to remove (every past value, or the value set in one revision), check the preview, type the field’s key and choose Redact permanently.- Each removed value reads
{"$redacted": true}wherever it appears in revision snapshots and changes, and those revisions list the field inredacted_fields. - Copies in stored webhook payloads are removed too.
- The current value is never touched. If it is the problem, change the record first, then redact.
- Reverting to a redacted revision keeps the field’s current value.
- Redactions are recorded in the activity log, and sent to webhooks, as
record.redacted.
How long history is kept
How long revisions are kept depends on the organization’s plan. Once a day, older revisions are removed, but every record keeps its first revision and its last 10. A record’shistory_pruned_through is the highest revision number removed (null while nothing was), and the History tab says which revisions were removed. For 30 days after a move to a plan that keeps less, nothing is removed.
History doesn’t outlive deletion: when a deleted record is purged, its revisions go with it, and when a deleted field is purged, its values are removed from every revision.
403), since it holds unpublished content.
Activity log
Revisions hold what a record’s content was. The activity log holds everything else that changed in the organization, and who changed it, through what: every record, draft, schema, asset, webhook, API key and search token change in its projects, and changes to the projects, the organization, its members, invitations and plan. Each event names its type (such asrecord.published or member.role_changed), its actor and its via, with small references such as a revision_id, but never content. Reads aren’t recorded.
Admins and owners read it on the organization’s Activity page. Filter it by project, by what changed (Show: Records, Drafts, Schema, Members and so on) and by Who: a member, All API keys or System; choosing a name in the list shows only their activity. Client shows what each change came through: Web app, API, CLI, SDK, MCP, System or Scheduler. A record’s or collection’s event links to it.
An API key with Read the activity log (audit:read) reads its own project’s events with GET /v1/projects/{project_id}/audit-events, newest first. Filter by type, exactly or by prefix (filter[type]=record.*), by resource_type and resource_id, by actor_type and actor_id, and by a created_at range. $PROJECT_ID is your key’s project: GET /v1/me returns it as api_key.project_id.