Last updated on 2026-09-10 21:20
Payload CMS and webhooks
auto-post.io can publish generated articles to a Payload CMS project through its REST API. The same integration guide also covers webhook-based destinations for workflows that need a custom delivery step.
Payload CMS integration
What you need
Before connecting Payload CMS, prepare:
- A public Payload REST API base URL
- A Payload user account that can access the configured collections
- The authentication collection slug, usually
users - A posts collection slug
- Optional category and media collection slugs
Connect Payload CMS
- Open Websites, then choose Add website.
- Select Payload CMS as the platform.
- Enter the website name and public URL.
- Enter the Payload API URL, login email, password, authentication collection slug, and posts collection slug.
- Add category and media collection slugs when your project uses them.
- Select Continue. auto-post.io logs in, reads a sample post, and suggests a field mapping.
- Review the suggested mapping and submit the connection.
The connection stores the credentials needed to obtain a Payload JWT. Tokens are refreshed or recreated when required for later requests.
Field mapping
The mapping connects auto-post.io fields to fields in your Payload posts collection:
- Title
- Slug
- Content
- Publication date
- Categories
- Featured image
- Open Graph image
- SEO title, description, and keywords
- Public post URL
Nested fields are supported with dot paths such as seo.title. You can use the AI suggestion step as a starting point, then review every field before saving.
Payload rich text is sent as Lexical JSON by default. Choose the plain HTML format only when your collection expects HTML content.
Payload collections and endpoints
The following example shows the minimum collection shape expected by the integration. Field names can be different when you configure the corresponding mapping in auto-post.io.
// payload/collections/Posts.js
import { lexicalEditor } from '@payloadcms/richtext-lexical';
export const Posts = {
slug: 'posts',
access: {
create: ({ req }) => Boolean(req.user),
read: () => true,
update: ({ req }) => Boolean(req.user),
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', required: true, unique: true },
{ name: 'content', type: 'richText', editor: lexicalEditor() },
{ name: 'publishedAt', type: 'date' },
{ name: 'categories', type: 'relationship', relationTo: 'categories', hasMany: true },
{ name: 'featuredImage', type: 'upload', relationTo: 'media' },
],
};auto-post.io uses these Payload REST endpoints. Replace https://cms.example.com/api and collection slugs with your values:
# Login and receive a JWT
curl -X POST "https://cms.example.com/api/users/login" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"YOUR_PASSWORD"}'
# Create a post
curl -X POST "https://cms.example.com/api/posts" \
-H "Authorization: JWT YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"A generated article","slug":"a-generated-article","content":{"root":{"type":"root","children":[],"direction":null,"format":"","indent":0,"version":1}},"_status":"draft"}'
# Update an existing post
curl -X PATCH "https://cms.example.com/api/posts/123" \
-H "Authorization: JWT YOUR_JWT" \
-H "Content-Type: application/json" \
-d '{"title":"An updated article","_status":"published"}'For featured images, configure a media collection and make sure its upload field accepts the multipart field name configured in auto-post.io, file by default:
curl -X POST "https://cms.example.com/api/media" \
-H "Authorization: JWT YOUR_JWT" \
-F "[email protected]"Publishing behavior
When a campaign generates an article for a Payload website:
- Draft content is sent with Payload's
_statusset todraft. - Published content is sent with
_statusset topublished. - Existing Payload documents are updated instead of duplicated when their document ID is known.
- Categories are synchronized from the configured categories collection.
- Featured images are uploaded to the configured media collection when a media field is mapped.
- The generated article's public URL is taken from the configured URL field, a returned URL, or the configured path template.
See Campaign management to configure the campaigns that publish to Payload CMS.
Custom webhook workflows
Use a webhook destination when content must pass through your own endpoint or automation workflow before it reaches a CMS or another service. Keep the receiving endpoint responsible for:
- Authenticating the request
- Validating the content and required fields
- Returning a successful response only after accepting the request
- Handling retries safely without creating duplicate content
Do not expose credentials in a webhook URL. Store secrets in the receiving service and rotate them if they are exposed.
Webhook request contract
Use a JSON request with an explicit event and article fields. The receiving client can validate this contract, transform it into its CMS format, and return a 2xx response after accepting it.
{
"event": "article.published",
"idempotencyKey": "article-123-published",
"article": {
"title": "A generated article",
"slug": "a-generated-article",
"content": "<p>Article content</p>",
"status": "published",
"publishedAt": "2026-09-10T19:20:00Z",
"featuredImage": "https://cdn.example.com/articles/a-generated-article.jpg"
}
}The destination endpoint should validate the signature or secret configured for your workflow, reject malformed requests with 4xx, and make idempotencyKey unique before writing content.
// webhook-server.js
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhooks/auto-post', async (req, res) => {
const { event, idempotencyKey, article } = req.body;
if (event !== 'article.published' || !idempotencyKey || !article?.title) {
return res.status(400).json({ error: 'Invalid webhook payload' });
}
// Verify the configured secret or signature before this point.
// Ignore idempotencyKey when it has already been processed.
await publishToDestination(article, idempotencyKey);
return res.status(202).json({ accepted: true });
});
app.listen(3000);Troubleshooting
Connection fails
- Confirm that the API URL points to the Payload REST API, not only to the public frontend.
- Check the authentication collection slug and login credentials.
- Confirm that the posts collection exists and is readable by the user.
- Check that the API is reachable from the internet.
Articles are rejected
- Verify that the mapped field names match the Payload collection.
- Confirm the content field type and the selected rich text format.
- Check required fields, relationship formats, and media permissions.
- Review the collection access rules for create and update operations.
Images are missing
- Configure a media collection slug.
- Check the mapped featured image or Open Graph field.
- Confirm that the media collection accepts the configured upload field.
Related documentation
Last updated on 2026-09-10 21:20