> ## Documentation Index
> Fetch the complete documentation index at: https://payload-plugin-openapi.seshuk.im/llms.txt
> Use this file to discover all available pages before exploring further.

# Extensions

> Inject extra paths, components, and tags, or transform the finished document before it is served.

For anything the generator doesn't produce on its own, pass `extensions`. Each extension can add paths, components, and tags, and/or transform the finished document before it's served or written to a file:

```ts payload.config.ts theme={null}
import type { OpenApiExtension } from '@seshuk/payload-plugin-openapi'

const myExtension: OpenApiExtension = {
  paths: {
    '/webhooks/stripe': {
      post: { summary: 'Stripe webhook', responses: { '200': { description: 'OK' } } },
    },
  },
  components: {
    securitySchemes: {
      apiKey: { type: 'apiKey', in: 'header', name: 'X-API-Key' },
    },
  },
  tags: [{ name: 'Webhooks', description: 'Inbound webhooks' }],
  transform: async ({ doc, payload }) => {
    const { totalDocs } = await payload.count({ collection: 'posts' })
    doc.info['x-posts'] = totalDocs
    return doc
  },
}

openapi({
  info: { title: 'My API', version: '1.0.0' },
  extensions: [myExtension],
})
```

## The extension shape

| Option | Type | Default | Description |
| - | - | - | - |
| `paths` | `PathsObject` | — | Extra path items merged into the document's `paths` |
| `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`. |

## 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`.

`transform` fails closed. If it throws or its promise rejects, the spec request fails and the CLI stops with the error. The plugin never serves a document that skipped a transform, so a transform that removes private paths can rely on it.

The `ExtensionContext` has these properties:

| Property | Type | Description |
| - | - | - |
| `doc` | `Document` | The finished document |
| `defaultIDType` | `'text' \| 'number'` | The project's ID type |
| `locales` | `string[]` | Locale codes from the project's `localization` config |
| `apiRoute` | `string` | The Payload API route (`routes.api`, `/api` by default) |
| `docLanguages` | `string[]` | Languages the docs are available in (the project's `i18n.supportedLanguages`) |
| `i18n` | `I18n` | The active request i18n — resolves translated descriptions |
| `payload` | `BasePayload` | The Payload instance, for example to read data with the Local API |
| `options` | `ResolvedOptions` | The plugin options with every default filled in |

## Extension or `custom.openapi`?

Use the tool that keeps documentation next to what it describes:

* **`custom.openapi` on an endpoint** — documentation for one specific endpoint your config already defines. It lives on the endpoint itself and needs no plugin option. See [Custom endpoints](/v1/guides/custom-endpoints).
* **An extension** — documentation that isn't tied to a single endpoint or field: shared components, extra tags, paths served outside Payload (webhooks handled elsewhere), or a post-build transform over the whole document.

<Note>
  If you're a plugin author, prefer `custom.openapi` on the endpoints you add — it's picked up automatically without depending on this package. Extensions are an opt-in your users wire into their own `openapi({ extensions: [...] })` call. See [For plugin authors](/v1/guides/plugin-authors).
</Note>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.