Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
Skip to content
World desk7 min

Publishing Rich-Text Blog Posts to Payload CMS Programmatically, Without the Admin UI

You can create a rich-text blog post in Payload CMS without opening the admin panel by calling payload.create() on the server, or by POSTing to the collection's REST endpoint from outside. This guide covers which path to pick, how the Lexical rich-text value should be built, how access control changes between the two, and where the official docs stop.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To publish a rich-text blog post to Payload CMS without 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. Use this Local API route when the code runs inside your Payload application, such as a seed script or a server function. When the caller is an external process, POST the same document data to the collection’s REST endpoint or use the official SDK instead.

Choose the path by where your code runs

Payload offers two ways to write documents programmatically, and the right one depends on where the caller lives. The Local API runs directly inside Node in the same process as your Payload application, and the official docs list seed scripts as a典型 use case. The REST API is an HTTP interface generated for each collection, so any external service can reach it.

Caller Transport Access behavior Typing
Server code inside the Payload app (seed scripts, server functions) Local API, payload.create() Bypasses access control by default. To run as a user, pass that user and set overrideAccess: false. Generated Local API types, when the project’s generated types are available
External service, script on another host, or browser-adjacent code REST API (POST to the collection endpoint), or the Payload SDK create({ collection, data }) Requests go through configured authentication and access rules. Credentials must be configured for the installation, not hardcoded in browser code. The SDK is documented as a typed option for REST clients; detailed REST type behavior is not stated in the reviewed docs

The sections below follow that split. Start with the Local API because it covers most seed and migration work, then move to the external paths.

Confirm the collection before writing any code

A generic posts collection cannot be assumed. Payload collections define the fields of each document and generate the Local, REST, and GraphQL APIs that manage them, so the real configuration in your project decides every value in your call. Before writing data, check the following in your collection config, as documented in Payload’s collection configuration reference:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Collection slug. This is the value you pass as collection. Your project may call it something other than posts.
  • Rich-text field name. Your body field might be content, body, or something else.
  • Required fields. Any field marked required must appear in data, or the create will fail.
  • Access settings. These decide who can create documents over REST and what server code needs to pass through when it runs as a user.
  • Publish workflow, locales, database adapter, and API route prefix. Draft and publish handling, localization, and the route prefix (/api by default) differ between projects. A created document is not automatically public; check whether your collection uses drafts before assuming a sample post is live.

Create the post with the Local API

On the server, get your Payload instance and call create with the collection slug and a data object. The Local API documentation at payloadcms.com/docs/local-api/overview describes this direct in-process access.

Seed script pattern

A seed script is the simplest case: it loads your Payload config, creates the instance, and runs one or more create calls. The snippet below shows the shape. Treat it as an illustration, not a drop-in file, because the slug, field names, and editor value must match your own project.

const post = await payload.create({
  collection: 'posts', // the slug from your collection config
  data: {
    title: 'Example post',
    content: editorState, // Lexical state built for this field's features
  },
})

Generated Payload types can give you type hints for data when your project has them. Regenerate them after you change the collection, or the hints will describe an older schema.

Reading the result

The call returns the created document. Use its id for follow-up writes, such as a later update or a link from another document, rather than searching by title, which may match more than one post.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Build the rich-text value as Lexical state

The rich-text field expects serialized Lexical editor data, not an arbitrary HTML string. The Payload rich-text documentation at payloadcms.com/docs/rich-text/overview describes the Lexical setup, which uses the @payloadcms/richtext-lexical package and the lexicalEditor() function in your configuration.

Match the editor configuration

Saved Lexical data is a tree of typed nodes. Payload’s TypeScript types for this data, TypedEditorState, can be parameterized with the serialized node types your editor has enabled. The features you turn on in lexicalEditor() therefore determine which node types are valid. If your script produces nodes for a feature the field does not enable, the saved content will not match the field’s model. Generate the state with the same editor configuration the field uses, and keep the feature list in one shared place so the script and the admin editor cannot drift apart.

Where the editor state comes from

The reviewed documentation establishes the data model and the Local API call, but it does not describe a specific helper for converting Markdown or HTML into Lexical state. If your content starts as HTML or Markdown, you need a conversion step that you write and test yourself, and the output must be checked against the enabled features before you save it.

Respect access control in server code

Local API operations bypass access control by default. That is convenient for a trusted seed script, but it means a server-side call does not automatically enforce the same permissions as a logged-in REST request. The access-control guidance at payloadcms.com/docs/local-api/access-control describes the pattern for running an operation under a user’s permissions: pass the authenticated user and set overrideAccess: false.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const post = await payload.create({
  collection: 'posts',
  data: { title: 'Drafted by an editor', content: editorState },
  user: authenticatedUser, // the user whose permissions should apply
  overrideAccess: false,
})

Decide this per operation. A seed script that loads initial content can keep the default bypass, while a server action triggered by a visitor or editor should pass that person’s user so that the create is checked against their rights.

Post from outside the Payload application

An external service cannot call the Local API, because the Local API only exists inside the Node process that loads your Payload config. It uses HTTP instead. The REST routes use the configured API prefix, which is /api by default, so creating a document in a collection with the slug posts means sending a POST to /api/posts on your installation’s base URL. The request body carries the same document data as the Local API data object, and the request must be authenticated and permitted by your collection’s access rules. The REST overview is at payloadcms.com/docs/rest-api/overview.

The official SDK documents a create({ collection, data }) operation and file uploads for upload-enabled collections, so it is a typed alternative to hand-written HTTP calls. Either way, store credentials on the server or in a secrets manager. Do not embed them in browser code, and do not hardcode a hostname the reviewed material does not establish for your site.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Call the server action from a Next.js frontend

In a Next.js app, the safest pattern keeps the privileged write in a server function and exposes only one narrow action to the page. The Payload guide at payloadcms.com/docs/local-api/server-functions shows the pattern: obtain the Payload instance with getPayload, call payload.create(), and return the created document. The example below follows that structure. Import your project’s own config where the comment indicates.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
'use server'

import { getPayload } from 'payload'
import config from 'your-project-payload-config' // your project's config import

export async function createPost(title: string, editorState: unknown) {
  try {
    const payload = await getPayload({ config })
    const post = await payload.create({
      collection: 'posts',
      data: { title, content: editorState as any },
    })
    return post
  } catch (error) {
    console.error(error)
    throw error
  }
}

Validate the input inside the function before calling create, and decide who may call it. The example does not check the caller, so add an authentication check and pass the user with overrideAccess: false if the action should be limited to editors.

Attach media only when you need it

Image uploads are optional. For an upload-enabled collection, the Uploads documentation at payloadcms.com/docs/upload/overview describes passing a local file path through filePath to payload.create() in a seed script. The path must be absolute.

const media = await payload.create({
  collection: 'media', // the upload-enabled collection in your project
  data: { alt: 'Cover image' },
  filePath: '/absolute/path/to/cover.jpg',
})

Keep media out of the minimum publishing path. Create the text post first, then attach an image in a separate step if the post needs one, so a file problem does not block the article.

Check the version and verify the first post

The reviewed official docs explain the API shape and the editor data model, but the passages do not pin a release number. Package APIs and generated types change between releases, so check your installed payload and @payloadcms/richtext-lexical versions against the docs you are reading. Regenerate your payload-types.ts file after any schema change, and treat the snippets above as shapes to adapt.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Before you rely on a script, create one post in a non-production database and read it back through the admin panel or the REST API. Confirm that the rich-text content renders, that the status is what you expected (draft or published), and that a request without credentials cannot write the document when access rules should block it.

Once a script has created one post correctly, the same create call can run in a loop over your content files, with one document per entry and an error log for any create that fails.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.