Integration·HTML & CSS·beginner
Automated blog posts for HTML sites
Automated blog posts for HTML sites: write body_html files from a verified Crontent webhook into a static HTML and CSS blog.
How-to: Integrate Crontent with HTML & CSS. Official docs: HTML & CSS. Shared API contract: Publishing API and SDK.
Verify the Crontent webhook, write a static .html file from body_html (or a template partial), and deploy only after review.
Field mapping
| Crontent field | HTML output |
|---|---|
title | <h1> in page body |
seo_title | <title>, <meta property="og:title"> (fallback: title) |
slug | Filename: {slug}.html |
excerpt | <meta name="description"> |
body_html | Main article content (preferred) |
body_mdx | Convert to HTML first if body_html is null |
tags | <meta name="keywords"> or tag list markup |
hero_image_url | <meta property="og:image">, hero <img> |
canonical_url | <link rel="canonical"> |
sources[] | <footer> citations list |
Webhook handler
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! });
const TEMPLATE = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{seoTitle}}</title>
<meta name="description" content="{{excerpt}}">
<link rel="canonical" href="{{canonical}}">
</head>
<body>
<article>
<h1>{{title}}</h1>
{{body}}
</article>
</body>
</html>`;
export async function handle(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 html = TEMPLATE
.replace("{{seoTitle}}", post.seo_title ?? post.title ?? "")
.replace("{{title}}", post.title ?? "")
.replace("{{excerpt}}", post.excerpt ?? "")
.replace("{{canonical}}", post.canonical_url ?? "")
.replace("{{body}}", post.body_html ?? "");
const draftDir = join(process.cwd(), "drafts");
await mkdir(draftDir, { recursive: true });
await writeFile(join(draftDir, `${post.slug}.html`), html);
return new Response("ok");
}Write to a drafts/ folder first. Copy to public/ or your web root only after manual review.
Static hosting
Works with any host that serves files: S3, Cloudflare R2, nginx, GitHub Pages. Pair with CI to upload approved drafts.
Backfill
Loop posts.list and generate HTML for each missing slug. Store in drafts/ and review in batch.