Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

## Features

- **Full spec from your config** — collections, globals, auth, versions, uploads, and jobs are documented with zero annotation. See the [endpoint list](https://payload-plugin-openapi.seshuk.im/v1/reference/endpoints).
- **Full spec from your config** — collections, globals, auth, versions, uploads, jobs, and the endpoints of official Payload plugins are documented with zero annotation. See the [endpoint list](https://payload-plugin-openapi.seshuk.im/v1/reference/endpoints).
- **Interactive docs included** — mount Scalar or Swagger UI, or both on different paths.
- **Native metadata** — document custom endpoints and refine field schemas through Payload's own `custom.openapi` key. No wrapper, no separate registry.
- **Precise filtering** — choose exactly which entities and operations end up in the spec.
Expand All @@ -31,15 +31,15 @@

## Quick start

Requires **Payload 4.0.0-canary.38**, **Next.js 16.4 or later** and **Node.js 24.15 or later**. On Payload 3, use `@seshuk/payload-plugin-openapi@0`.
Requires **Payload 4.0.0-beta.0 or later**, **Next.js 16.4 or later** and **Node.js 24.15 or later**. On Payload 3, use `@seshuk/payload-plugin-openapi@0`.

### Install

```bash
npm install @seshuk/payload-plugin-openapi@beta
```

v1 is in beta under the `beta` npm tag, and Payload 4 is published under `canary`.
v1 is in beta under the `beta` npm tag, and Payload 4 is published under `beta`.

### Configure

Expand Down Expand Up @@ -121,6 +121,10 @@ Full docs are at **<https://payload-plugin-openapi.seshuk.im/>**:
- [CLI — `openapi:generate`](https://payload-plugin-openapi.seshuk.im/v1/cli/generate)
- [Examples](https://payload-plugin-openapi.seshuk.im/v1/guides/examples)

## Official plugins

When you install an official Payload plugin, the spec documents the REST endpoints it mounts: `@payloadcms/plugin-ecommerce`, `plugin-stripe`, `plugin-mcp`, `plugin-seo`, `plugin-search`, `plugin-multi-tenant`, `plugin-import-export` and `storage-r2`. There is nothing to configure. Filter them with `excludeOperations: [{ plugin: '@payloadcms/plugin-stripe' }]` or `{ kind: 'plugin' }`. See [Official plugins](https://payload-plugin-openapi.seshuk.im/v1/reference/endpoints#official-plugins).

## For plugin authors

If you maintain a Payload plugin, attach a `custom.openapi` Operation Object to the endpoints you add and they show up in the generated spec — no dependency on this package, and nothing for your users to wire up. The same key works on fields. If this plugin isn't installed, the metadata is inert. See [For plugin authors](https://payload-plugin-openapi.seshuk.im/v1/guides/plugin-authors).
Expand Down
19 changes: 19 additions & 0 deletions docs/v1/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ description: 'Release history for the OpenAPI plugin for Payload CMS.'

Notable changes per release. For the full commit-level history, see the [GitHub releases](https://github.com/maximseshuk/payload-plugin-openapi/releases).

<Update label="v1.0.0-beta.2" description="October 11, 2026">

**Breaking changes**

- Payload 4.0.0-beta.0 or later is required.
- An operation in extension `paths` now [replaces the generated operation](/v1/configuration/extensions#your-paths-take-priority) with the same path and method. Before, the two were deep-merged.
- `OperationKind` and `OperationContext.kind` can be `'plugin'`. An exhaustive `switch` over `kind` needs a new case.
- Endpoints of official Payload plugins now appear in the spec. `includeCustom: false` does not hide them. Add `{ kind: 'plugin' }` to `excludeOperations` to drop them.

**New features**

- Endpoints of the official ecommerce, Stripe, MCP, SEO, search, multi-tenant, import-export and R2 storage plugins are documented when the plugin is installed. See [Official plugins](/v1/reference/endpoints#official-plugins).
- `excludeOperations` rules take `plugin`, the package name of the plugin that added the operation. A custom endpoint sets it with `'x-payload-plugin'` in `custom.openapi`. See [Rules](/v1/configuration/filters#rules).
- `POST /api/<slug>/{id}/rename` for upload collections, and the `version` query param on `GET /api/<slug>/file/{filename}`.
- `POST /api/reorder` when a collection or join field is `orderable`. See [System](/v1/reference/endpoints#system).
- With `includeSystem`, the `payload-preferences` routes that get, save and delete a preference by key. See [Preferences](/v1/reference/endpoints#preferences).

</Update>

<Update label="v1.0.0-beta.1" description="October 3, 2026">

**Breaking changes**
Expand Down
8 changes: 7 additions & 1 deletion docs/v1/configuration/extensions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,17 @@ openapi({

| Option | Type | Default | Description |
| ------------ | ---------------------------------------------------- | ------- | -------------------------------------------------------------- |
| `paths` | `PathsObject` | — | Extra path items merged into the document's `paths` |
| `paths` | `PathsObject` | — | Extra path items merged into `paths`. See below |
| `components` | `ComponentsObject` | — | Extra schemas, security schemes, etc. merged into `components` |
| `tags` | `TagObject[]` | — | Extra tags appended to the document's `tags` |
| `transform` | `({ doc, ...ctx }) => Document \| Promise<Document>` | — | Post-build hook over the finished document. It can be `async`. |

## Your paths take priority

An operation in `paths` replaces the generated operation with the same path and method in full. Nothing from the generated operation is kept: not its `security`, `tags` or `responses`. Other methods on the same path stay. Path-level fields such as `summary` and `parameters` merge into the generated path item.

To change one field of a generated operation, use [`custom.openapi`](/v1/guides/custom-endpoints) on the endpoint or a `transform`.

## The `transform` hook

`transform` receives one object, the `ExtensionContext`: the full built document as `doc` plus the build environment. It **must return the document** to serve. Mutate and return the same object, or return a new one — either works, but a missing `return` drops the document. It can be `async`.
Expand Down
24 changes: 21 additions & 3 deletions docs/v1/configuration/filters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ description: 'Control what ends up in the spec: which entities are documented, w

See [Endpoints](/v1/reference/endpoints) for the full list of endpoints and the option that controls each one.

`includeCustom` does not cover [official plugin](/v1/reference/endpoints#official-plugins) endpoints. To hide them, add `{ kind: 'plugin' }` to `excludeOperations`.

## Choosing entities with `include` and `exclude`

These two lists pick which collections and globals are documented. Each entry is an `EntityMatcher` — one of three shapes:
Expand Down Expand Up @@ -104,7 +106,7 @@ filters: {

## Dropping individual operations

`include` / `exclude` work at the entity level. To remove specific operations — one method on one collection, every `DELETE`, anything under a path prefix — use `excludeOperations`. It applies to every operation in the spec: collection, global, custom, jobs and system (upload instructions, `/api/access`) operations.
`include` / `exclude` work at the entity level. To remove specific operations — one method on one collection, every `DELETE`, anything under a path prefix — use `excludeOperations`. It applies to every operation in the spec: collection, global, custom, jobs, system (upload instructions, `/api/access`) and [official plugin](/v1/reference/endpoints#official-plugins) operations.

### Rules

Expand All @@ -116,8 +118,11 @@ Each rule is a set of conditions. Within one rule, every field you set must matc
| `slug` | `string \| RegExp` | The collection or global slug, exact string or pattern; omit to match any |
| `kind` | `OperationKind` | Restrict to one group; omit to match all |
| `path` | `RegExp` | Tested against the final route path (e.g. `/api/posts/{id}`) |
| `plugin` | `string \| RegExp` | The plugin that added the operation, exact string or pattern |

`kind` is one of `'collection'`, `'global'`, `'custom'` (endpoints with `custom.openapi`), `'jobs'`, `'system'` or `'plugin'` (endpoints of an official Payload plugin). Custom, jobs and system operations have no slug, so a rule with `slug` never matches them. A plugin operation mounted on a collection, such as `/api/search/reindex`, has that collection's slug. One mounted at the API root has none.

`kind` is one of `'collection'`, `'global'`, `'custom'` (endpoints with `custom.openapi`), `'jobs'` or `'system'`. Custom, jobs and system operations have no slug, so a rule with `slug` never matches them.
`plugin` is the plugin's package name, such as `'@payloadcms/plugin-search'`, for `'plugin'` operations. A rule with `plugin` never matches an operation without one.

```ts payload.config.ts
filters: {
Expand All @@ -128,13 +133,26 @@ filters: {
{ slug: 'posts', method: ['post', 'patch', 'put'] },
// Drop anything matching a path, regardless of method or entity.
{ path: /\/preview$/ },
// Drop everything the Stripe plugin adds.
{ plugin: '@payloadcms/plugin-stripe' },
],
}
```

A custom endpoint can name its plugin too. Set `'x-payload-plugin'` in its `custom.openapi`. The operation keeps the `'custom'` kind, and rules and functions see the value as `plugin`. The key is removed from the final document.

```ts my-plugin.ts
{
path: '/my-plugin/sync',
method: 'post',
handler,
custom: { openapi: { 'x-payload-plugin': 'my-plugin', summary: 'Sync now', responses: { '200': { description: 'Done' } } } },
}
```

### Functions

For logic that doesn't fit a rule, put a function in the same list. It receives the `method`, `path`, `slug` and `kind` of every operation. The operation is dropped only when the function returns `true`. An `async` function is awaited:
For logic that doesn't fit a rule, put a function in the same list. It receives the `method`, `path`, `slug`, `kind` and `plugin` of every operation. The operation is dropped only when the function returns `true`. An `async` function is awaited:

```ts payload.config.ts
filters: {
Expand Down
2 changes: 1 addition & 1 deletion docs/v1/configuration/security-marking.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ openapi({
})
```

The function receives `{ method, path, slug, kind, detected }` for each operation. `kind` is `'collection'`, `'global'`, `'custom'`, `'jobs'` or `'system'`, and `slug` is set only for collections and globals. `detected` is the marking the plugin found, `'public'` or `'secured'`. The function can be `async`.
The function receives `{ method, path, slug, kind, plugin, detected }` for each operation. `kind` is `'collection'`, `'global'`, `'custom'`, `'jobs'`, `'system'` or `'plugin'`. `slug` is set for collections and globals, and for plugin operations mounted on a collection. `plugin` is the package name of the [official plugin](/v1/reference/endpoints#official-plugins) that added the operation, or the `x-payload-plugin` value of a custom endpoint. `detected` is the marking the plugin found, `'public'` or `'secured'`. The function can be `async`.

When [interactive auth](/v1/configuration/interactive-auth) is on, every secured operation also lists the `PayloadLogin` scheme, so the docs UI sends the token it got from the login dialog.

Expand Down
2 changes: 2 additions & 0 deletions docs/v1/guides/plugin-authors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ export const myPlugin =

That is the whole integration. When a user has the OpenAPI plugin installed, your endpoint appears in their spec under its own tag, and your field carries its description and example into the generated schemas. See [Custom endpoints](/v1/guides/custom-endpoints) for the operation shape and [Field metadata](/v1/guides/field-metadata) for everything fields support.

To let users filter your endpoints by plugin, add `'x-payload-plugin': 'my-plugin'` to each `custom.openapi`. A rule such as `{ plugin: 'my-plugin' }` in [`excludeOperations`](/v1/configuration/filters#rules) then drops them all. The key is removed from the final document.

## Why plugin order does not matter

The OpenAPI plugin does not read your config when it runs. It builds the document **lazily, on request, from the fully sanitized config**. By the time a request hits `/api/openapi.json`, every plugin in the `plugins` array has already run — so your endpoints, collections, and fields are visible no matter where either plugin sits in the list. There is no ordering constraint to document and nothing for your users to get wrong.
Expand Down
2 changes: 1 addition & 1 deletion docs/v1/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The document is built lazily from the fully sanitized config, so endpoints and f

## Requirements

Payload `4.0.0-canary.38`, Next.js 16.4 or later and Node.js 24.15 or later. v1 is in beta under the `beta` npm tag, and Payload 4 is published under `canary`. For Payload 3, use the `0.x` versions.
Payload `4.0.0-beta.0` or later, Next.js 16.4 or later and Node.js 24.15 or later. v1 is in beta under the `beta` npm tag, and Payload 4 is published under `beta`. For Payload 3, use the `0.x` versions.

## Where to go next

Expand Down
4 changes: 2 additions & 2 deletions docs/v1/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ yarn add @seshuk/payload-plugin-openapi@beta
</CodeGroup>

<Info>
Requires Payload `4.0.0-canary.38`, Next.js 16.4 or later and Node.js 24.15 or later. v1 is in beta under the `beta`
npm tag, and Payload 4 is published under `canary`. For Payload 3, use the `0.x` versions.
Requires Payload `4.0.0-beta.0` or later, Next.js 16.4 or later and Node.js 24.15 or later. v1 is in beta under the
`beta` npm tag, and Payload 4 is published under `beta`. For Payload 3, use the `0.x` versions.
</Info>

</Step>
Expand Down
Loading
Loading