---
title: "Versions — Overview"
aliases: [payload-versions, payload-version-history, payload-audit-log]
tags: [payloadcms, versions, drafts, autosave, audit-log]
sources: [raw/versions__overview.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Payload Versions track the full change history of Collections and Globals. When enabled, every save creates a new version record in a separate `_slug_versions` collection. The Admin UI gains diff views, version browsing, and one-click restore.

Versions are **opt-in and non-destructive** — they don't change the shape of your data.

---

## Three Modes

| Mode | What happens on save | Use case |
|------|----------------------|----------|
| **Versions only** | New version created, always treated as "published" | Audit log / change history (e.g. user records) |
| **Versions + Drafts** | Control which version is published; drafts can be previewed before going live | Content publishing workflows |
| **Versions + Drafts + Autosave** | Admin UI auto-saves a draft as you type; published version unaffected | Editorial workflows — never lose changes |

---

## Collection Config

```js
// Simple — enable with defaults (max 100 versions per doc)
versions: true

// Custom
versions: {
  maxPerDoc: 50,   // 0 = unlimited; default 100
  drafts: true,    // or { autosave: true }
}
```

## Global Config

```js
versions: {
  max: 20,       // versions per global (no per-doc concept)
  drafts: true,
}
```

---

## Database Storage

A separate collection is created per versioned collection/global:

```
_posts_versions      ← for 'posts' collection
_header_versions     ← for 'header' global
```

Each version document structure:

```json
{
  "_id": "...",
  "parent": "...",       // parent doc ID (collections only)
  "autosave": false,
  "version": { /* full copy of the document */ },
  "createdAt": "...",
  "updatedAt": "..."
}
```

All fields inside `version` are set to **not required** — partial versions are valid.

---

## Version Operations (REST / GraphQL / Local API)

### REST — Collections

| Method | Path | Description |
|--------|------|-------------|
| `GET` | `/api/{slug}/versions` | List versions (paginated) |
| `GET` | `/api/{slug}/versions/:id` | Get single version |
| `POST` | `/api/{slug}/versions/:id` | Restore version |

### REST — Globals

| Method | Path |
|--------|------|
| `GET` | `/api/globals/{slug}/versions` |
| `GET` | `/api/globals/{slug}/versions/:id` |
| `POST` | `/api/globals/{slug}/versions/:id` |

### Local API — Collections

```js
// Find versions
await payload.findVersions({ collection: 'posts', sort: '-createdAt', limit: 10 })

// Find single version
await payload.findVersionByID({ collection: 'posts', id: '...' })

// Restore
await payload.restoreVersion({ collection: 'posts', id: '...' })
```

### Local API — Globals

```js
await payload.findGlobalVersions({ slug: 'header' })
await payload.findGlobalVersionByID({ slug: 'header', id: '...' })
await payload.restoreGlobalVersion({ slug: 'header', id: '...' })
```

### GraphQL

| Operation | Query Name |
|-----------|------------|
| Find versions | `versions{CollectionPlural}` |
| Find by ID | `version{CollectionSingular}` |
| Restore | `restoreVersion{CollectionSingular}` |

---

## Access Control

Add `readVersions` to your collection/global access control to gate who can view version history:

```js
access: {
  readVersions: ({ req: { user } }) => !!user?.isAdmin,
}
```

This automatically restricts the Admin UI version tab as well.

---

## Key Takeaways

- **Separate collection** — versions live in `_slug_versions`, never polluting your main data
- **Three levels** — versions-only (audit log), +drafts (publish control), +autosave (live editing safety)
- **maxPerDoc** — defaults to 100; set to `0` for unlimited; tune for storage budget
- **Global versions** use `max` (not `maxPerDoc`) and have no `parent` field
- **All versions store a full copy** of the document — fields are non-required to allow partial saves
- **readVersions** access function controls version history visibility independently of document read access
- **Restore via POST** — any version can be promoted back to current
- **Autosave is safe** — it only writes drafts, never touching the published version until you explicitly publish

---

## Related

- [[wiki/payloadcms/versions-drafts|Versions — Drafts]] — draft vs published, access control, scheduled publish
- [[wiki/payloadcms/versions-autosave|Versions — Autosave]] — autosave interval config, storage strategy
- [[wiki/payloadcms/access-control|Access Control]] — readVersions and publish access
- [[wiki/payloadcms/local-api|Local API]] — payload.findVersions / restoreVersion
- [[wiki/payloadcms/hooks-collections|Collection Hooks]] — afterChange hooks fire on version saves too

---

## Sources

- `raw/versions__overview.md` — https://payloadcms.com/docs/versions/overview
