API reference
Publishing webhooks let ContioReach notify your application when content changes.
This is especially useful when your frontend uses cached or statically generated pages. Instead of repeatedly checking whether content changed, your application can refresh the relevant pages when ContioReach sends a webhook.
A typical publishing flow looks like this:
Inside your workspace:
Store the generated token in your application:
REVALIDATION_SECRET=your_generated_secret_tokenContioReach sends one POST request to your webhook URL when:
Saving a draft does not send a webhook.
POST https://yoursite.com/api/revalidate
Content-Type: application/json
X-API-Key: your_generated_secret_tokenThe secret is sent twice: in the X-API-Key header and as secret in the body. Check either one before acting on the request.
{
"secret": "your_generated_secret_token",
"post": {
"id": "b7e1f2a3-0000-4000-8000-000000000001",
"title": "Getting Started With Your First Blog Post",
"metaTitle": "Getting Started With Your First Blog Post | Your Blog",
"slug": "getting-started-first-blog-post",
"description": "The meta description used for search engines.",
"coverImage": "https://contiocdn.com/images/getting-started-cover.webp",
"coverImageAlt": "Laptop open on a desk with a blank blog draft",
"excerpt": "A short summary of the post, shown on listing pages.",
"content": "<h2>Introduction</h2><p>Your post content as HTML…</p>",
"publishedAt": "2026-01-15T09:30:00.000Z",
"createdAt": "2026-01-14T16:05:00.000Z",
"updatedAt": "2026-01-20T11:12:00.000Z",
"workspaceId": "e5f6a7b8-0000-4000-8000-000000000001",
"userId": "f6a7b8c9-0000-4000-8000-000000000001",
"primaryKeyword": "first blog post",
"secondaryKeywords": ["how to start a blog", "blog post template"]
}
}| Field | Type | Description |
|---|---|---|
| secret | string | Your revalidation secret. Reject the request if it does not match. |
| post.id | string | Unique identifier of the post. |
| post.title | string | Article title. |
| post.metaTitle | string | SEO title for <title> and og:title. Falls back to title when not set. |
| post.slug | string | URL-safe identifier. Use it to revalidate the article route. |
| post.description | string | null | Meta description. |
| post.coverImage | string | null | Absolute URL of the cover image. |
| post.coverImageAlt | string | Alt text for the cover image. Falls back to title when not set. |
| post.excerpt | string | null | Short summary for cards and listings. |
| post.content | string | null | Article body as HTML. |
| post.publishedAt | string | null | ISO 8601 publication timestamp. |
| post.createdAt | string | ISO 8601 creation timestamp. |
| post.updatedAt | string | ISO 8601 last-modified timestamp. |
| post.workspaceId | string | The workspace the post belongs to. |
| post.userId | string | The user who created the post. |
| post.primaryKeyword | string | null | Target keyword for the article. |
| post.secondaryKeywords | string[] | Supporting keywords. Empty array when none are set. |
The payload does not say which event happened. On delete, post is the post as it was just before removal. Revalidating post.slug handles every case, because the Content API only serves published posts.
Respond with HTTP 200 within 15 seconds. Any other status — including 201, 202, or 204 — is recorded as a failed delivery.
Create:
app/api/revalidate/route.tsimport { revalidatePath } from "next/cache";
export async function POST(request: Request) {
const { secret, post } = await request.json();
if (
secret !==
process.env.REVALIDATION_SECRET
) {
return Response.json(
{ error: "Invalid token" },
{ status: 401 }
);
}
revalidatePath("/blog");
if (post?.slug) {
revalidatePath(`/blog/${post.slug}`);
}
return Response.json({
revalidated: true
});
}When ContioReach sends the webhook, your application verifies the secret before revalidating content.
The example refreshes:
/blogand, when the payload includes a post slug:
/blog/[slug]This keeps both the blog listing and the individual article current.
The webhook secret should be treated like a credential. Do not expose it in browser code or commit it to a public repository.
Always verify the secret before performing revalidation.
Was this page helpful?