← Blog/
Tutorial

Connecting Your Next.js Blog to Blogree in 10 Minutes: Full Integration Guide

Step-by-step tutorial for installing the Blogree adapter in your Next.js App Router project, setting up webhook delivery, and publishing your first AI-generated post automatically.

M
Muhammad Mohsin
Content Writer, Blogree
March 15, 2026
· 5 min read
Connecting Your Next.js Blog to Blogree in 10 Minutes: Full Integration Guide

What You Are Building

By the end of this guide you will have a Next.js blog that receives AI-generated posts from Blogree automatically. When you publish a post in the Blogree dashboard — or when the content planner auto-publishes on schedule — the post is delivered to your Next.js site via webhook, your blog cache is revalidated, and the post appears live without you touching a single file.

This is what full blog automation looks like in practice. Write once in Blogree, publish everywhere automatically.

Prerequisites

Before starting, make sure you have:

  • A Blogree account — sign up free at blogree.com

  • A Next.js 14+ project using App Router (Pages Router instructions are at the bottom of this guide)

  • Node.js 18+ installed

  • Your project deployed or running locally with a publicly accessible URL for webhook testing (use ngrok for local development)

How the Blogree + Next.js Integration Works

Before diving into the code it helps to understand the flow. Blogree uses a webhook-based delivery system rather than a polling approach. Here is what happens end to end:

  1. You publish a post in Blogree (manually or via the content planner)

  2. Blogree sends an HMAC-signed POST request to your webhook endpoint

  3. Your Next.js API route validates the signature, processes the payload, and stores or renders the post

  4. Next.js revalidates the affected pages using revalidatePath

  5. Your blog shows the new post — usually within 2–3 seconds of hitting publish

HMAC signature validation means only Blogree can trigger your webhook — not random bots or bad actors. Your webhook secret is used to sign every request, and your handler verifies that signature before processing anything. This is the same security pattern used by Stripe, GitHub, and every serious webhook-based system.

Step 1 — Install the Adapter

Open your terminal in your Next.js project root and run:

bash
npm install @blogree/nextjs-adapter#

The adapter handles HMAC signature verification, payload parsing, and typed response interfaces so you do not have to build any of that yourself.

Step 2 — Get Your API Keys

Log into your Blogree dashboard, navigate to Sites → Add Site. Enter your website URL and click Generate Keys. You will receive three values:

  • BLOGREE_API_KEY — authenticates your server-side requests to the Blogree API

  • BLOGREE_API_URL — the base URL for all API requests

  • BLOGREE_WEBHOOK_SECRET — used to verify incoming webhook payloads

Keep these values private. Never commit them to Git or expose them in client-side code.

Step 3 — Set Up Environment Variables

Create or update your .env.local file in the project root:

bash
BLOGREE_API_KEY=bk_live_xxxxxxxxxxxx
BLOGREE_API_URL=https://api.blogree.com
BLOGREE_WEBHOOK_SECRET=whs_xxxxxxxxxxxx

For production deployments on Vercel, add these same variables in your Vercel Dashboard → Project Settings → Environment Variables. If you are using a different hosting provider, consult their documentation for setting environment variables — Railway, Render, and Fly.io all support .env style variables in their dashboards.

Step 4 — Create the Webhook Handler

Create the file app/api/blogree/route.ts in your Next.js project. This is the endpoint Blogree will POST to when delivering new content.

typescript
// app/api/blogree/route.ts

import { createWebhookHandler } from "@blogree/nextjs-adapter";
import { revalidatePath } from "next/cache";
import { NextResponse } from "next/server";

const handler = createWebhookHandler({
  apiKey: process.env.BLOGREE_API_KEY!,
  webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET!,
});

export async function POST(request: Request) {
  try {
    const payload = await handler(request);

    if (payload.event === "post.published") {
      // Revalidate your blog listing page
      revalidatePath("/blog");

      // Revalidate the specific post page
      revalidatePath(`/blog/${payload.post.slug}`);
    }

    return NextResponse.json({ received: true }, { status: 200 });
  } catch (error) {
    // Return 400 if signature validation fails
    return NextResponse.json(
      { error: "Invalid webhook signature" },
      { status: 400 }
    );
  }
}

The createWebhookHandler function handles HMAC signature verification automatically. If the signature does not match — meaning the request did not genuinely come from Blogree — it throws an error and your catch block returns a 400. Blogree will retry failed deliveries up to 3 times with exponential backoff.

Step 5 — Fetch and Display Posts

Now set up your blog pages to fetch and render Blogree posts.

Blog listing page (app/blog/page.tsx):

typescript
// app/blog/page.tsx

import { getBlogreePosts } from "@blogree/nextjs-adapter";

export default async function BlogPage() {
  const posts = await getBlogreePosts({
    apiKey: process.env.BLOGREE_API_KEY!,
    apiUrl: process.env.BLOGREE_API_URL!,
  });

  return (
    <main>
      <h1>Blog</h1>

      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <a href={`/blog/${post.slug}`}>
              <h2>{post.title}</h2>
              <p>{post.excerpt}</p>
              <time>
                {new Date(post.publishedAt).toLocaleDateString()}
              </time>
            </a>
          </li>
        ))}
      </ul>
    </main>
  );
}

Individual post page (app/blog/[slug]/page.tsx):

typescript
// app/blog/[slug]/page.tsx

import { getBlogreePost, getBlogreePosts } from "@blogree/nextjs-adapter";
import { notFound } from "next/navigation";

interface Props {
  params: {
    slug: string;
  };
}

export default async function PostPage({ params }: Props) {
  const post = await getBlogreePost({
    slug: params.slug,
    apiKey: process.env.BLOGREE_API_KEY || "",
    apiUrl: process.env.BLOGREE_API_URL || "",
  });

  if (!post) return notFound();

  return (
    <article>
      <h1>{post.title}</h1>

      <time>
        {new Date(post.publishedAt).toLocaleDateString()}
      </time>

      <div dangerouslySetInnerHTML={{ __html: post.content }} />
    </article>
  );
}

// ✅ Generate static params for SSG
export async function generateStaticParams() {
  const posts = await getBlogreePosts({
    apiKey: process.env.BLOGREE_API_KEY || "",
    apiUrl: process.env.BLOGREE_API_URL || "",
  });

  return posts.map((post) => ({
    slug: post.slug,
  }));
}

Step 6 — Configure Your Webhook URL in Blogree

Go to your Blogree dashboard → Sites → your site → Webhook Settings. Enter your webhook URL:

https://yoursite.com/api/blogree

For local development, use ngrok to expose your local server:

bash
ngrok http 3000 # Use the generated URL: https://abc123.ngrok.io/api/blogree   

Click Verify Webhook in the Blogree dashboard. Blogree will send a test payload to your endpoint. If verification succeeds you will see a green confirmation. If it fails, check the troubleshooting section below.

Step 7 — Publish Your First AI Post

You are ready. Go to Blogree dashboard → Posts → New Post. Blogree will suggest trending topics based on your site's niche. Select one or enter your own title.

Configure your post settings:

  • Add your focus keyword

  • Generate LSI keywords with one click

  • Add internal links to existing posts

  • Select or generate a featured image

  • Run the SEO audit

Click Publish Now. Within 2–3 seconds, the webhook fires, your Next.js pages revalidate, and the post appears live on your site. Check /blog — your first AI-generated post is live.

Pages Router Setup (Alternative)

If your project uses the Next.js Pages Router instead of App Router, create the webhook handler at pages/api/blogree.ts:

typescript
// pages/api/blogree.ts

import { createWebhookHandler } from "@blogree/nextjs-adapter";
import type { NextApiRequest, NextApiResponse } from "next";

const handler = createWebhookHandler({
  apiKey: process.env.BLOGREE_API_KEY || "",
  webhookSecret: process.env.BLOGREE_WEBHOOK_SECRET || "",
});

export default async function blogreeWebhook(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // ❌ Only allow POST
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  try {
    const payload = await handler(req);

    // ✅ Handle publish event
    if (payload.event === "post.published") {
      // Revalidate blog listing page
      await res.revalidate("/blog");

      // Revalidate specific post
      await res.revalidate(`/blog/${payload.post.slug}`);
    }

    return res.status(200).json({ received: true });
  } catch (error) {
    console.error("Webhook error:", error); // ✅ helpful for debugging

    return res.status(400).json({
      error: "Invalid webhook signature",
    });
  }
}

For fetching posts in Pages Router, use getStaticProps with revalidate for ISR:

typescript
// pages/blog/index.tsx

import { getBlogreePosts } from "@blogree/nextjs-adapter";
import type { GetStaticProps } from "next";

interface Post {
  slug: string;
  title: string;
  excerpt: string;
  publishedAt: string;
}

interface Props {
  posts: Post[];
}

export default function BlogPage({ posts }: Props) {
  return (
    <main>
      <h1>Blog</h1>

      <ul>
        {posts.map((post) => (
          <li key={post.slug}>
            <a href={`/blog/${post.slug}`}>
              <h2>{post.title}</h2>
              <p>{post.excerpt}</p>
              <time>
                {new Date(post.publishedAt).toLocaleDateString()}
              </time>
            </a>
          </li>
        ))}
      </ul>
    </main>
  );
}

export const getStaticProps: GetStaticProps = async () => {
  const posts = await getBlogreePosts({
    apiKey: process.env.BLOGREE_API_KEY || "",
    apiUrl: process.env.BLOGREE_API_URL || "",
  });

  return {
    props: {
      posts,
    },
    revalidate: 60, // fallback ISR if webhook fails
  };
};

Troubleshooting Common Errors

Webhook not receiving requests Check that your deployment platform allows incoming POST requests to /api/blogree. Vercel allows this by default. If using a custom server or Docker setup, ensure port 443 is open and the path is not blocked by middleware. Confirm your webhook URL in the Blogree dashboard does not have a trailing slash.

HMAC signature validation failing This almost always means your BLOGREE_WEBHOOK_SECRET does not match what is in the Blogree dashboard. Copy the secret character by character — do not retype it manually. Check for invisible whitespace characters, especially if you copied from a PDF or email. Redeploy after adding environment variables — changes to .env.local require a server restart.

revalidatePath not working Ensure you are using Next.js 13.4+ which introduced stable revalidatePath support. If you are on an older version, upgrade or use res.revalidate in the Pages Router pattern. Also confirm the path you are revalidating exactly matches the URL structure of your blog — /blog not /blog/.

Posts not appearing after publish Check your Blogree dashboard → Sites → your site → Delivery Logs. Blogree logs every webhook attempt with the response code your endpoint returned. A 200 means delivery succeeded. A 400 or 500 means your handler rejected or errored on the payload. The error detail in delivery logs will tell you exactly what went wrong.

getBlogreePosts returning empty array Confirm your BLOGREE_API_KEY and BLOGREE_API_URL environment variables are set correctly in your deployment environment, not just .env.local. On Vercel, environment variables added after your last deployment require a redeploy to take effect.

TypeScript errors on the payload object Import the BlogreePayload type from the adapter and use it to type your handler response. The adapter exports full TypeScript interfaces for all webhook events and post objects.

FAQ

Does this integration work with Vercel Edge Functions? The standard App Router setup works with Vercel's Node.js runtime. Edge Functions have limited Node.js API support — stick with the Node.js runtime for the webhook handler by adding export const runtime = 'nodejs' to your route file.

Can I use Blogree with a Next.js site that already has existing blog posts? Yes. Blogree adds to your existing content — it does not replace it. Your existing posts remain in your codebase or CMS. Blogree-delivered posts appear alongside them. You control the rendering entirely.

Does Blogree support Next.js Image optimisation for post images? Yes. Post images delivered by Blogree include a full URL that you can pass directly to Next.js's component. Add api.blogree.com to your next.config.js image domains list.

How do I handle Blogree posts in my existing sitemap? Fetch your Blogree posts in your sitemap.ts file using getBlogreePosts and include them alongside your static pages. This ensures Google discovers AI-published posts through your sitemap immediately after they go live. See the Blogree docs for a full sitemap example.

What happens if my site is down when Blogree tries to deliver a post? Blogree retries failed webhook deliveries up to 3 times with exponential backoff — at 1 minute, 5 minutes, and 30 minutes after the initial failure. If all three attempts fail, the post is marked as delivery-failed and you receive an alert email. You can manually retry delivery from the Blogree dashboard.

Can I customise the post data before it renders? Yes. The webhook payload gives you full access to the post object before you render it. You can transform the content, add custom fields, filter by category, or apply any business logic in your handler before revalidating.

Final Thoughts

The Blogree Next.js integration is designed to be genuinely fast to set up — most developers have it running in under 15 minutes — and completely hands-off once it is live. Posts flow from the Blogree content planner to your Next.js site automatically, with no manual steps, no copy-pasting, and no formatting fixes.

The result is a blog that publishes consistently, maintains proper SEO structure on every post, and scales as your content operation grows — without adding to your workload.

Ready to automate your blog?

Start publishing AI-generated, SEO-optimized posts to any platform.

Start Automating Today →