Plugin Config
Configure payloadMarkdownDocs for the dedicated docs workflow.
Plugin Config
payloadMarkdownDocs() is a Payload plugin factory.
1import { payloadMarkdownDocs } from '@valkyrianlabs/payload-markdown-docs'2 3payloadMarkdownDocs({4 enabled: true,5})
An enabled plugin injects the default docs infrastructure and registers the sync endpoint. A disabled plugin is an exact no-op.
Recommended Config
1import { payloadMarkdownDocs } from '@valkyrianlabs/payload-markdown-docs'2import { buildConfig } from 'payload'3 4export default buildConfig({5 plugins: [6 payloadMarkdownDocs({7 auth: {8 githubOidc: true,9 },10 target: {11 type: 'docsCollection',12 enableDrafts: true,13 },14 sync: {15 allowWrites: true,16 allowPublish: true,17 allowHardDelete: false,18 deleteBehavior: 'archive',19 },20 }),21 ],22})
Main Sections
authenables sync request verification modes. UsegithubOidc,ed25519, or both on the same endpoint.targetconfigures the dedicated generated docs collection.synccontrols write, publish, and delete authority.routingconfigures route collision checks.collectionscustomizes infrastructure collection slugs.blocksoptionally installs docs marketing blocks into existing layout block fields.heroesoptionally wraps or installs docs-set hero fields on the pages collection. See Docs Heroes.seocontrols docs set SEO fields. It defaults totrue; setfalseto omit them.
GitHub OIDC trust records and Ed25519 public keys both belong in
Docs Globals > Access. Docs packages belong in Docs Globals > Sets.
See sync config and routing config for the safety gates.
Real App Pattern
Most apps keep the plugin config small and move package-specific decisions to Payload Admin:
- register the plugin once in
payload.config.ts - create one docs set per docs package in
Docs Globals > Sets - use docs groups for route namespaces such as
/plugins - add GitHub OIDC owners in
Docs Globals > Access - add Ed25519 public keys in
Docs Globals > Accessonly for local or non-GitHub CI - render docs from a Next route with the
/nextadapter - serve raw
.mdexports from explicit Next route handlers
Do not add one Payload Page per Markdown file. The generated docs collection is
internal storage for synced content, route resolution, search, and admin review.
Minimal Local Config
For local validation or admin-only experiments, this is enough to register the collections and sync endpoint:
1payloadMarkdownDocs({2 enabled: true,3})
That does not make sync writes publicly usable. Authentication is disabled until
you enable auth.githubOidc, auth.ed25519, or both. Sync writes are rejected
until sync.allowWrites: true.
Auth Patterns
GitHub Actions publishing usually uses OIDC:
1payloadMarkdownDocs({2 auth: {3 githubOidc: true,4 },5})
Local machines and non-GitHub CI can use Ed25519 request signatures:
1payloadMarkdownDocs({2 auth: {3 ed25519: true,4 },5})
Both modes can be enabled on the same endpoint. A bearer token is treated as a GitHub OIDC request; Ed25519 headers are treated as a signed request.
Target Collection
The implemented target is the dedicated generated docs collection:
1payloadMarkdownDocs({2 target: {3 type: 'docsCollection',4 enableDrafts: true,5 markdownField: 'content',6 },7})
target.type currently only accepts docsCollection. Existing collection and
block targets are intentionally not implemented.
target.markdownField renames the generated Markdown field. If you customize it,
pass the same field name to the /next helpers:
1await resolvePayloadMarkdownDocsRoute({2 payload,3 slug,4 markdownField: 'body',5})
Collection Slugs
Infrastructure collection slugs can be customized when an app already reserves the defaults:
1payloadMarkdownDocs({2 collections: {3 docs: { slug: 'generated-docs' },4 docsAccess: { slug: 'docs-access' },5 docsGroups: { slug: 'docs-groups' },6 docsSets: { slug: 'docs-sets' },7 syncRuns: { slug: 'docs-sync-runs' },8 nonces: { slug: 'docs-sync-nonces' },9 },10})
The plugin rejects duplicate requested slugs and slugs that already exist in the
incoming Payload config. If both target.slug and collections.docs.slug are
provided, they must match.
Disabling infrastructure collections is an advanced integration path. Normal apps should leave the defaults enabled; the sync endpoint needs docs sets for source resolution and needs audit/nonces for applied sync.
Public URLs In Generated Files
Generated llms.txt and llms-full.txt files contain absolute URLs. The
origin comes from NEXT_PUBLIC_SERVER_URL, NEXT_PUBLIC_SITE_URL, SITE_URL,
the Vercel URL variables, or Payload serverURL, in that order. Request
headers are used only when none of these is set: X-Forwarded-Host and
X-Forwarded-Proto only with endpoint.trustForwardedHeaders: true (set it
only behind a proxy that overwrites them), otherwise Host. Configure
serverURL in production so a client-supplied header can never change the
URLs.
Collection Access
Plugin collections are admin-only by default. Users of the Payload admin user
collection (config.admin.user) can create, read, update, and delete docs,
docs sets, docs groups, docs assets, and Access records. Sync runs and sync
nonces are read-only for admins; only the sync endpoint writes them. Users of
any other auth collection (customers, members) get no access through REST,
GraphQL, or the admin panel.
The sync endpoint and the /next read helpers use the Local API with
overrideAccess: true, so these rules do not affect syncing or public pages.
Change who counts as a docs admin, or override single operations per collection:
1payloadMarkdownDocs({2 access: {3 admin: ({ req }) => req.user?.collection === 'users' && req.user.role === 'admin',4 },5 collections: {6 syncRuns: {7 access: {8 delete: ({ req }) => req.user?.role === 'admin',9 },10 },11 },12})
Hero Images
Generated docs records include an optional heroImage upload field. It uses the
media collection by default.
Add extra upload collections when your app stores docs imagery elsewhere:
1payloadMarkdownDocs({2 target: {3 heroImage: {4 additionalMediaCollections: ['docs-media'],5 },6 },7})
The docs-set SEO meta image, the docs heroImage field, and the media fields
inside installed heroes and docsCTA blocks reference the media upload
collection. If the app defines no media collection, the plugin omits those
fields and logs one warning instead of failing Payload config validation. Hero
image collections listed in additionalMediaCollections that do not exist are
omitted the same way. The check sees collections defined before the plugin
runs, so define media in your own config rather than in a later plugin.
Set target.heroImage: false to omit the field.
SEO Fields
Docs sets include a meta group by default, powered by
@payloadcms/plugin-seo field components:
1payloadMarkdownDocs({2 seo: true,3})
Set seo: false when an app wants to manage docs set metadata outside this
plugin:
1payloadMarkdownDocs({2 seo: false,3})
Endpoint
The sync endpoint defaults to /api/documentation/sync because Payload
mounts plugin endpoints under /api.
1payloadMarkdownDocs({2 endpoint: {3 path: '/documentation/sync',4 maxBodyBytes: 5_000_000,5 },6})
Set enabled: false only for environments where the plugin should be a complete
no-op.