---
title: History and AS OF
description: Every change kept, with who, when and why; any collection read as it stood.
section: Data
order: 10
---

# History and AS OF

<p class="lead">With history on, every change is logged in the same transaction as the write: the new version of the record, who changed it, when, and why.</p>

## Turn it on

Enable it in the admin UI or with `"history": true` in the schema. Existing rows are logged once as a baseline, so the history starts complete.

## Read the past

```ts title="Browser"
// the collection as it stood at the end of June
await sluurp.collection("grades").list({ asOf: "2026-06-30" });

// one record's versions, newest first, and restoring one
await sluurp.collection("grades").history(id);
await sluurp.collection("grades").restore(id, seq, { reason: "Undo" });
```

```http title="HTTP"
GET /api/collections/grades/aggregate?op=avg&field=value&group=subject&asOf=2026-06-30

// every change, in order
GET /api/collections/grades/changes?since=0
```

Each past version is checked against the collection's view rule as it applied to that version, so history never shows anything the reader couldn't have seen.

A past snapshot is rebuilt once and cached, so paging through it or querying it again is fast. The server caches a few snapshots per collection. If too many new snapshots are requested at once, the extra requests get `429 Too Many Requests` and can retry a second later, so time travel never slows down everyone else.

## Reasons

```ts title="Browser"
await sluurp.collection("grades").update(id, { value: 7 }, { reason: "Re-marked after appeal" });
```

Over HTTP, use the `X-Sluurp-Reason` header. A [batch](/docs/batch) applies one reason to all its changes.

## In SQL

The admin UI's SQL console supports SQL:2011 temporal syntax. Plain queries still read the latest rows.

```sql title="SQL"
SELECT subject, avg(value)
FROM grades FOR SYSTEM_TIME AS OF '2026-06-30'
GROUP BY subject;
```

Before the query reaches SQLite, each such table is swapped for its snapshot, rebuilt from the change log under the same name. This also works in the browser on rows [synced into SQLite](/docs/sync).

## Pages

Pages' version history uses the same log: who changed a page, when, and why. Restoring a page restores one of its versions.
