---
title: "Versions — Drafts"
aliases: [payload-drafts, draft-publish, payload-draft-api]
tags: [payloadcms, versions, drafts, publish, access-control]
sources: [raw/versions__drafts.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

Drafts build on [[wiki/payloadcms/versions-autosave|Versions / Autosave]] to let you save changes without publishing them immediately. Requires `versions` to be enabled first.

```ts
// Collection or Global
versions: {
  drafts: {
    autosave: true,            // or pass autosave options object
    schedulePublish: true,     // allow future publish/unpublish events
    validate: false,           // validate on draft save (default: false)
    localizeStatus: false,     // Beta: localize _status field
  },
}
```

## Database Changes

Enabling drafts injects a `_status` field (`'draft' | 'published'`) into your schema automatically.

**Admin UI statuses:**
- **Draft** — never published; only draft versions exist
- **Published** — published, no newer drafts
- **Changed** — published, but newer unpublished drafts exist

## How `draft` Param and `_status` Work Together

| Operation | `draft` param      | `_status` in data    | Result                                                        |
|-----------|-------------------|---------------------|---------------------------------------------------------------|
| Create    | `true` or `false` | omitted             | Main collection updated with `_status: 'draft'`              |
| Create    | `true` or `false` | `'published'`       | Main collection updated with `_status: 'published'`          |
| Update    | `true`            | omitted or `'draft'`| **Only versions table** updated; main collection unchanged   |
| Update    | `true`            | `'published'`       | Main collection updated with `_status: 'published'` (override)|
| Update    | `false` or omitted| omitted             | Main collection updated with `_status: 'draft'`              |
| Update    | `false` or omitted| `'published'`       | Main collection updated with `_status: 'published'`          |

**Key distinctions:**
- `draft: true` → skips required field validation + writes only to versions table
- `_status: 'published'` → overrides `draft: true` and updates the main collection
- Setting `_status: 'draft'` alone does **not** skip required field validation; use `draft: true` for that

## Draft API Examples

```ts
// Save as draft (only versions table updated)
await payload.update({
  collection: 'pages',
  id,
  data: { title: 'WIP' },
  draft: true,
})

// Publish (main collection updated)
await payload.update({
  collection: 'pages',
  id,
  data: { _status: 'published' },
})

// Read latest version (draft or published)
await payload.findByID({
  collection: 'pages',
  id,
  draft: true,   // returns newest version from versions table
})
```

REST equivalent: `POST /api/pages?draft=true`

## Controlling Who Can Read Drafts

> **Critical:** `draft: true` on read does NOT filter by `_status`. Use Access Control.

```ts
access: {
  read: ({ req }) => {
    if (req.user) return true         // logged-in: see everything
    return { _status: { equals: 'published' } }  // guests: published only
  },
}
```

**Migration gotcha:** existing documents before drafts was enabled won't have `_status`. Allow both cases:

```ts
return {
  or: [
    { _status: { equals: 'published' } },
    { _status: { exists: false } },
  ],
}
```

## Controlling Who Can Publish

Use `update` access control with a query constraint on `_status`:

```ts
access: {
  update: ({ req: { user } }) => {
    if (!user) return false
    if (user.role === 'admin') return true
    // editors can only update drafts (not published docs)
    return { _status: { equals: 'draft' } }
  },
}
```

This also suppresses the Publish/Unpublish buttons in Admin UI for non-admins.

## Scheduled Publish

Enable with `versions.drafts.schedulePublish: true`. Requires [[wiki/payloadcms/jobs-queue|Jobs Queue]] to be running — scheduled events are queued as background jobs.

## Unpublishing & Reverting

- **Unpublish** → sets `_status: 'draft'` on the main collection document
- **Revert to published** → creates a new version reflecting last published state (draft history is preserved)

## Key Takeaways

- `draft: true` controls **validation bypass** + **write location** (versions table only)
- `_status: 'published'` controls **publish state** — always overrides `draft: true`
- First document creation always writes to main collection regardless of `draft` param
- Read with `draft: true` returns the **newest version** (could be draft or published)
- Access Control is required to hide drafts from unauthenticated users
- `schedulePublish` requires the Jobs Queue to be configured and running
- Adding drafts to an existing collection → old docs have no `_status`; update AC accordingly

## Related

- [[wiki/payloadcms/versions-autosave|Versions — Autosave]]
- [[wiki/payloadcms/access-control|Access Control]]
- [[wiki/payloadcms/jobs-queue|Jobs Queue — Overview]]
- [[wiki/payloadcms/admin-preview|Admin Preview & Draft Preview]]
- [[wiki/payloadcms/live-preview|Live Preview — Overview]]

## Sources

- `raw/versions__drafts.md` — https://payloadcms.com/docs/versions/drafts
