crontent
Integration·Hugo·intermediate

Automated blog posts for Hugo

Automated blog posts for Hugo: write markdown drafts from Crontent webhooks and trigger a Hugo rebuild.

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

Verify Crontent, fetch the post, write a markdown draft under content/, and trigger a Hugo rebuild only after human review.

Field mapping

Crontent fieldHugo frontmatter
titletitle
seo_titleCustom seo_title param for <title> partial
slugFilename or slug in frontmatter
excerptdescription or summary
body_mdxBody (convert MDX to MD if needed)
body_htmlUse with markup: html if you skip Markdown
tagstags array
hero_image_urlfeatured_image param
canonical_urlcanonicalUrl param
sources[]sources YAML list in frontmatter

Webhook handler

Deploy alongside Hugo or in CI. Example Node handler:

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

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

export async function handleWebhook(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 dir = join(process.cwd(), "content/posts");
  await mkdir(dir, { recursive: true });

  const fm = `---
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)}
featured_image: ${JSON.stringify(post.hero_image_url ?? "")}
---
`;
  await writeFile(join(dir, `${post.slug}.md`), fm + "\n" + post.body_mdx);
  await exec("hugo", ["--minify"], { cwd: process.cwd() });
  return new Response("ok");
}

Alternatively, commit new files via GitHub API and let Netlify or Cloudflare Pages rebuild. Set draft: true so Hugo excludes the post from production until you remove the flag.

Git-based workflow

  1. Webhook writes file to a branch or opens a PR.
  2. Review diff in GitHub.
  3. Merge and let your host run hugo.

Use X-Crontent-Delivery as an idempotency key to avoid duplicate commits.

Backfill

Script posts.list with pagination and write any slug not present under content/posts/. Run once after initial setup.

Further reading