---
title: "Payload CMS — Access Control Overview"
aliases: [payload-access-control, payload-cms-permissions]
tags: [payload-cms, access-control, rbac, permissions, cms]
sources: [raw/access-control__overview.md]
created: 2026-05-15
updated: 2026-05-15
---

# Payload CMS — Access Control Overview

Access Control in Payload determines what a user can do with any Document and what they see in the Admin Panel. Functions are scoped per **operation** and run **before** any changes are made.

## Three Types

| Type | Article | Scope |
|------|---------|-------|
| Collection | [[wiki/tech-patterns/payload-cms-collection-access-control\|Collection Access Control]] | Per-operation on collection documents |
| Global | [[wiki/tech-patterns/payload-cms-globals-access-control\|Globals Access Control]] | Per-operation on singleton globals |
| Field | [[wiki/tech-patterns/payload-cms-field-access-control\|Field Access Control]] | Per-field within a document |

## Default Access Control

Payload ships with a default that requires an authenticated user:

```ts
const defaultPayloadAccess = ({ req: { user } }) => Boolean(user)
```

- **Local API skips access control by default** — set `overrideAccess: false` to opt back in.

## Access Operation & Admin Panel Sync

- On login, Payload runs every access function at the top level (all Collections, Globals, Fields) to determine what the user can do.
- Admin Panel hides/shows UI elements dynamically based on this result.
- **When called via the Access Operation**, `id`, `data`, `siblingData`, `blockData`, and `doc` are all `undefined` — always guard against this before using them.

```ts
const access = ({ req: { user }, id }) => {
  if (!id) return Boolean(user) // Access Operation context
  return user?.role === 'admin'
}
```

## Locale-Specific Access Control

Use `req.locale` to restrict access per locale:

```ts
const access = ({ req }) => req.locale === 'en'
```

## Common Patterns

- **Public read:** return `true` from `read`
- **Published only:** return `{ status: { equals: 'published' } }` (Query constraint)
- **Owner only:** return `{ createdBy: { equals: req.user.id } }`
- **Admin role:** `return req.user?.role === 'admin'`
- **Org-scoped:** `return { organization: { equals: req.user.orgId } }`

## Key Takeaways

- Access functions execute **before** operations — they are guards, not post-filters
- Return `true`/`false` for full allow/deny; return a **Where query** for row-level security (Collections/Globals only — Fields are boolean-only)
- Local API bypasses access control unless `overrideAccess: false` is set
- Admin Panel UI reflects access control automatically via the Access Operation
- Guard `id`/`data`/`doc` for `undefined` inside access functions — they are absent when called by the Access Operation
- Locale-aware access is possible via `req.locale`

## Sources

- `raw/access-control__overview.md` — compiled 2026-05-15
- https://payloadcms.com/docs/access-control/overview
