AI blog content for Payload CMS
AI blog content for Payload CMS: verify Crontent webhooks and create draft posts with the Local API or REST endpoints.
How-to: Integrate Crontent with Payload CMS. Official docs: Payload. Shared API contract: Publishing API and SDK.
Verify Crontent's webhook, fetch the post, and create a Payload draft with the Local API or REST so review happens before publish.
What you need
- Crontent credentials:
CRONTENT_API_KEY,CRONTENT_WEBHOOK_SECRET - Payload CMS with a
postscollection (title, slug, rich text body, etc.) - Either a Payload Local API hook inside your Next.js app, or REST with an API key
@crontent/sdkin your webhook handler
Register the webhook endpoint:
await crontent.webhooks.register({
projectId: process.env.CRONTENT_PROJECT_ID!,
url: "https://your-app.com/api/crontent/webhook",
events: ["post.ready"],
});Map Crontent fields to Payload
| Crontent field | Payload field | Notes |
|---|---|---|
title | title | Text, display H1 |
seo_title | meta.title | SEO plugin or custom group; fall back to title |
slug | slug | Auto-generated or from Crontent |
excerpt | excerpt | Textarea |
body_mdx / body_html | content | Rich text or Lexical field |
tags | tags | Array or relationship |
hero_image_url | heroImage | Upload or URL |
canonical_url | meta.canonicalURL | SEO group |
sources[] | sources | Array or JSON field |
ready_at | crontentReadyAt | Date field |
Crontent runs research and produces a first draft from your brief. You steer angle in the brief; Payload drafts await human or AI-assisted review before _status moves to published.
Receive and verify webhooks
Use X-Crontent-Delivery for idempotency. Failed deliveries retry at 1m, 5m, 30m, 2h, 12h.
import { Crontent } from "@crontent/sdk";
const crontent = new Crontent({ apiKey: process.env.CRONTENT_API_KEY! });
export async function POST(req: Request) {
const rawBody = await req.text();
const deliveryId = req.headers.get("x-crontent-delivery")!;
if (await processed(deliveryId)) return new Response("OK");
const valid = await crontent.webhooks.verify({
rawBody,
signature: req.headers.get("x-crontent-signature"),
timestamp: req.headers.get("x-crontent-timestamp"),
secret: process.env.CRONTENT_WEBHOOK_SECRET!,
});
if (!valid) return new Response("Unauthorized", { status: 401 });
const { post_id } = JSON.parse(rawBody);
const post = await crontent.posts.get(post_id);
await createPayloadDraft(post);
await markProcessed(deliveryId);
return new Response("OK");
}Create a draft in Payload
Prefer the Local API inside a Payload route handler for type safety. Set _status: 'draft' explicitly.
import { getPayload } from "payload";
import config from "@payload-config";
async function createPayloadDraft(post: Post) {
const payload = await getPayload({ config });
await payload.create({
collection: "posts",
draft: true,
data: {
title: post.title,
slug: post.slug,
excerpt: post.excerpt,
content: post.body_html ?? post.body_mdx,
tags: post.tags.map((t) => ({ tag: t })),
meta: {
title: post.seo_title ?? post.title,
description: post.excerpt,
canonicalURL: post.canonical_url,
},
crontentPostId: post.id,
_status: "draft",
},
});
}REST alternative: POST /api/posts?draft=true with a Payload API key and the same JSON body.
Backfill and polling
Call crontent.posts.list({ projectId, since, cursor }) and filter out posts already stored by crontentPostId. Run as a cron job or one-off script.