Webhooks
SEO Manager can send webhooks to your application when SEO data changes. This is useful for triggering on-demand revalidation (ISR) so your pages always reflect the latest SEO content without a full rebuild.
Webhook Event Types
Your webhook endpoint will receive a type field in the request body:
| Type | Description |
|---|---|
insert-seo | A new SEO page was created |
update-seo | An existing SEO page was updated |
insertorupdate-seo | An SEO page was inserted or updated (upsert) |
delete-seo | An SEO page was deleted |
Setup
- Enable webhooks in your Project Settings.
- Set the webhook URL to your API endpoint (e.g.,
https://yourdomain.com/api/nextjs-seo-manager-webhook).
Pages Router Example
Uses res.revalidate() to trigger Next.js on-demand ISR for changed pages.
// pages/api/nextjs-seo-manager-webhook.js
import SEOInit from "nextjs-seo-manager/init";
import { backendValidation } from "nextjs-seo-manager";
SEOInit({
projectId: process.env.SEO_MANAGER_PROJECT_ID,
secretKey: process.env.SEO_MANAGER_SECRET_KEY,
});
export default async function handler(req, res) {
const response = await backendValidation(req.body);
const type = req?.body?.type;
if (
type === "update-seo" ||
type === "insert-seo" ||
type === "insertorupdate-seo"
) {
const revalidations = Object.keys(response?.results || []).map(
async (idx) => {
try {
const item = response.results[idx];
await res.revalidate(item?.path || "/");
} catch (err) {
console.error("Revalidation failed for:", item?.path, err);
}
}
);
await Promise.all(revalidations);
}
return res.status(200).json({ success: true });
}
App Router Example
Uses revalidatePath() from next/cache for on-demand revalidation in the App Router.
// app/api/nextjs-seo-manager-webhook/route.ts
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";
import SEOInit from "nextjs-seo-manager/init";
import { backendValidation } from "nextjs-seo-manager";
SEOInit({
projectId: process.env.SEO_MANAGER_PROJECT_ID,
secretKey: process.env.SEO_MANAGER_SECRET_KEY,
});
export async function POST(request: NextRequest) {
const body = await request.json();
const response = await backendValidation(body);
const type = body?.type;
if (
type === "update-seo" ||
type === "insert-seo" ||
type === "insertorupdate-seo"
) {
const results = response?.results || [];
for (const key of Object.keys(results)) {
const path = results[key]?.path || "/";
try {
revalidatePath(path);
} catch (err) {
console.error("Revalidation failed for:", path, err);
}
}
}
return NextResponse.json({ success: true });
}
About ISR (Incremental Static Regeneration)
If you use getStaticProps (Pages Router) or static rendering (App Router), your pages are built once and cached. Webhooks let you invalidate that cache on-demand when SEO data changes in the dashboard, so users always see the latest metadata.
- Pages Router:
res.revalidate(path)inside an API route. - App Router:
revalidatePath(path)orrevalidateTag(tag)fromnext/cacheinside a route handler.
This is more efficient than setting a short revalidate interval, since pages are only rebuilt when data actually changes.