What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To publish a rich-text blog post to Payload CMS without opening the Admin UI, call payload.create() with your posts collection’s slug and a data object whose rich-text field holds a serialized Lexical editor state. Run that call on the server, either from a seed script or from a server function in your frontend. Use the REST API or the Payload SDK only when the code runs outside the Payload application.
The steps below cover how to confirm your schema, what the rich-text value must look like, which API fits which caller, and how permissions apply to each route.
Confirm the collection before writing any code
Payload does not ship a universal posts collection. Each project defines its own collections in its config, and each collection generates the Local, REST, and GraphQL APIs used to manage its documents. Collection settings are described in the Payload collection configuration docs.
Before you write a script, open the project’s collection config and record four things:
- The collection slug, which is the value you pass as
collection. It may not beposts. - Required fields, because a create call without them fails validation.
- The rich-text field name, which might be
content,body, or something else. - Access and workflow settings, including who may create documents and whether the collection uses a draft or publish step.
Choose the API by where your code runs
Payload offers two programmatic routes. The Local API runs directly inside Node in the same project, which makes it the natural choice for seed scripts and server-side jobs. The REST API is an HTTP interface; external processes call it under the configured route prefix, which is /api by default. The official SDK also exposes create operations and can be used as a typed client for REST calls.
| Concern | Local API | REST API or Payload SDK |
|---|---|---|
| Where the caller runs | Server-side Node inside the Payload application, such as a seed script or server function | An external process, a separate service, or a browser (with credentials handled safely) |
| Transport | Direct function call, payload.create() |
HTTP request to /api/<collection-slug> under the configured prefix |
| Default permission behavior | Bypasses access control unless you pass a user and set overrideAccess: false |
Requests are evaluated against the authenticated user and the collection’s access rules |
| Type hints | Generated types from your project’s payload-types.ts where available |
Typed through the SDK where you use it; plain REST clients receive no generated types from Payload |
Overview pages for each route are in the Local API documentation and the REST API documentation.
Publish from a seed script or server code with the Local API
The Local API is the shortest path when your script or server code can access the Payload instance. The core call takes a required collection and a required data object:
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →const post = await payload.create({
collection: 'posts', // replace with your collection slug
data: {
title: 'Example post',
content: editorState, // Lexical state matching your field's features
},
})
Treat this as a shape to adapt, not a drop-in file. The slug, field names, and editor value must match your project. If your project generates types, the data object will be checked against them, which catches misspelled field names before the script runs.
Build the rich-text value as Lexical state
Payload’s rich-text editor is built on Lexical, installed through @payloadcms/richtext-lexical and enabled with lexicalEditor() in the field configuration. Lexical stores content as a tree of typed nodes rather than as an HTML string, so the value you pass must be serialized Lexical state. Writing raw HTML into the field is not a supported shortcut.
The set of valid node types depends on the features enabled for that field. If your field enables lists, links, or uploads, those node types become valid; if it does not, content using them will be rejected or will not render as intended. Generated types can express this: a TypedEditorState can be parameterized with the node types your editor enables.
A minimal single-paragraph value looks like this. Check it against an existing document in your database before relying on it, because exact node properties depend on your Lexical and Payload versions:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11const editorState = {
root: {
type: 'root',
direction: 'ltr',
format: '',
indent: 0,
version: 1,
children: [
{
type: 'paragraph',
direction: 'ltr',
format: '',
indent: 0,
version: 1,
children: [
{ type: 'text', detail: 0, format: 0, mode: 'normal', style: '', text: 'First paragraph.', version: 1 },
],
},
],
},
}
A practical way to avoid guessing is to fetch an existing post through the REST or Local API, copy the structure of its rich-text field, and change only the text you need.
Send documents from an external client
When the caller is outside the Payload application, POST the document data to the collection endpoint under the configured API prefix. For a collection with the slug posts and the default prefix, that is /api/posts on your installation’s own origin. Payload’s REST documentation covers the CRUD operations; the SDK documents create({ collection, data }), and it also documents file uploads for upload-enabled collections.
Authentication and access must be set up for your installation. Do not put administrator credentials in browser code. For server-to-server publishing, create a dedicated user or other credential that your collection’s auth setup accepts, with only the create permission the job needs, and keep that credential in server environment variables.
Control permissions explicitly
The Local API bypasses access control by default. A seed script that runs with full privileges is fine, but a server function that acts on behalf of a visitor is not. In that case, pass the authenticated user and turn off the override:
const post = await payload.create({
collection: 'posts',
data: { title: 'Submitted by a signed-in author', content: editorState },
user: authenticatedUser,
overrideAccess: false,
})
Setting overrideAccess: false does not make a server-side call equivalent to a logged-in REST request. You still need to decide which checks run in your code and which are enforced by the collection’s access rules. The Payload access control guide for Local API operations describes the behavior in detail.
Rank #4
Call Payload from a Next.js server function
In a Next.js frontend, keep the privileged operation on the server. Payload’s server functions guide shows obtaining a Payload instance with getPayload({ config }), calling payload.create(), and returning the created document, inside a function marked 'use server' with error handling in a try/catch.
Expose only the action the frontend needs, such as “submit a draft post for this author,” and validate the input inside the function before it reaches payload.create(). The server functions guide has the reference pattern.
Handle media as a separate step
Images are optional. If the posts collection is upload-enabled, a seed script can pass a local absolute path through filePath to payload.create(), but that is a separate upload operation. Publish the text first, confirm the document, and then add media, so a file problem does not block the article. Upload configuration is covered in the Payload uploads documentation.
Version and environment checks
Payload’s official documentation describes the API shape and the editor data model, but the package APIs and generated types change between releases. Match your code to the installed payload and @payloadcms/richtext-lexical versions, and regenerate your types after changing the collection config.
Database adapter, route prefix, locales, and draft settings are project-specific. A successful create call does not automatically mean the post is public. Check the document’s status and the collection’s read access before announcing it.
Quick Recap
Troubleshooting checklist
- Unknown collection or 404: the slug is wrong, or the REST route is not under the configured prefix.
- Validation error: a required field is missing or a value has the wrong type. Compare against the collection config.
- Rich text rejected or not rendering: the Lexical state contains a node type that the field’s enabled features do not allow. Copy the structure from an existing document.
- Post created but not visible: it may be a draft, or read access may hide it from the public. Check status and access rules.
- Unexpected permissions: the Local API was called without
overrideAccess: falseand a user, so it ran with full privileges.
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

