crontent
Integration·Eleventy·beginner

Scheduled blog posts for Eleventy

Scheduled blog posts for Eleventy: write Crontent posts as markdown drafts via webhook and rebuild your static site.

How-to: Integrate Crontent with Eleventy. Official docs: Eleventy. Shared API contract: Publishing API and SDK.

Verify the Crontent webhook, write a markdown draft into your Eleventy input directory, and rebuild after review.

Field mapping

Crontent fieldEleventy frontmatter
titletitle
seo_titleseoTitle (custom layout partial)
slugPermalink via permalink: /blog/{{ slug }}/
excerptdescription
body_mdxMarkdown body
body_htmlUse with a .html template if preferred
tagstags
hero_image_urlimage
canonical_urlcanonical
sources[]sources (array in frontmatter)

Webhook endpoint

Run alongside Eleventy or as a separate serverless function:

ts
import { Crontent } from "@crontent/sdk";
import { writeFile, mkdir } from "node:fs/promises";
import { join } from "node:path";

const crontent = new Crontent({ apiKey: process.env.CRONTENT_API_KEY! });

export async function handler(req: Request) {
  const rawBody = await req.text();
  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);
  const inputDir = join(process.cwd(), "src/posts");
  await mkdir(inputDir, { recursive: true });

  const content = `---
title: ${JSON.stringify(post.title)}
description: ${JSON.stringify(post.excerpt ?? "")}
date: ${JSON.stringify(post.ready_at ?? post.created_at)}
draft: true
tags: ${JSON.stringify(post.tags)}
permalink: "/blog/${post.slug}/"
layout: post.njk
---
${post.body_mdx}
`;
  await writeFile(join(inputDir, `${post.slug}.md`), content);
  return new Response("ok");
}

In .eleventy.js, exclude drafts from collections in production:

js
eleventyConfig.addGlobalData("eleventyExcludeFromCollections", (data) => data.draft);

Remove draft: true after editorial review and rebuild.

Backfill

Import historical posts with a script calling posts.list. Write files with the same frontmatter shape.

Further reading