crontent
Integration·Beehiiv·intermediate

AI content for Beehiiv

AI content for Beehiiv: send Crontent research drafts into Beehiiv as newsletter or blog posts via the Beehiiv API.

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

Verify the Crontent webhook, fetch the post, and create a Beehiiv draft via the API for review before send or publish.

Field mapping

Crontent fieldBeehiiv field
titletitle
seo_titleSEO title meta (if using blog posts)
slugURL slug on Beehiiv blog
excerptsubtitle or meta description
body_mdxConvert to HTML for content
body_htmlcontent (preferred)
tagsTags if supported on your plan
hero_image_urlHeader/thumbnail image
canonical_urlCustom field or link block
sources[]Footer section in HTML body

Webhook to Beehiiv draft

ts
import { Crontent } from "@crontent/sdk";

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

function appendSources(html: string, sources: { url: string; title: string | null }[]) {
  if (!sources.length) return html;
  const list = sources.map((s) => `<li><a href="${s.url}">${s.title ?? s.url}</a></li>`).join("");
  return html + `<h2>Sources</h2><ul>${list}</ul>`;
}

async function createBeehiivDraft(post: Awaited<ReturnType<typeof crontent.posts.get>>) {
  const content = appendSources(post.body_html ?? `<p>${post.excerpt}</p>`, post.sources);
  const res = await fetch(
    `https://api.beehiiv.com/v2/publications/${process.env.BEEHIIV_PUBLICATION_ID}/posts`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.BEEHIIV_API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: post.title,
        subtitle: post.excerpt,
        content,
        status: "draft",
      }),
    }
  );
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

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);
  await createBeehiivDraft(post);
  return new Response("ok");
}

Review drafts in the Beehiiv dashboard. Choose &quot;Send newsletter&quot; or &quot;Publish to web&quot; depending on your workflow.

Newsletter vs blog

Crontent posts work for both use cases. SEO-focused content maps well to Beehiiv web posts. Short-form newsletter editions may need manual trimming after import.

Backfill

Use posts.list to import historical content as drafts. Beehiiv rate limits apply; batch with delays.

Environment variables

text
CRONTENT_API_KEY=...
CRONTENT_WEBHOOK_SECRET=...
BEEHIIV_API_KEY=...
BEEHIIV_PUBLICATION_ID=...

Further reading