Webhooks
Signed notifications Atlas sends to your website when content is published, so you can refresh exactly the affected pages.
Create webhooks in Studio under Integrations → Webhooks. Each one has an HTTPS URL, the events it receives and a signing secret starting with whsec_, shown once.
Atlas only delivers to public HTTPS addresses. Private network addresses are refused; plain HTTP is allowed only to localhost while you develop.
Request
Section titled “Request”POST https://your-site.example/api/atlas/webhookContent-Type: application/jsonX-Atlas-Event: content.page.publishedX-Atlas-Delivery: <unique delivery id>X-Atlas-Signature: t=1761210000,v1=5f2b…Verify every request
Section titled “Verify every request”import { createHmac, timingSafeEqual } from 'node:crypto';import { revalidatePath, revalidateTag } from 'next/cache';
const seen = new Set<string>(); // use a shared store (Redis, KV) when you run several instances
export async function POST(request: Request) { const body = await request.text(); // the raw body, before any JSON parsing const header = request.headers.get('x-atlas-signature') ?? ''; const parts = Object.fromEntries(header.split(',').map((part) => part.split('=', 2))); const timestamp = Number(parts.t);
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return new Response(null, { status: 400 });
const expected = createHmac('sha256', process.env.ATLAS_WEBHOOK_SECRET!) .update(`${timestamp}.${body}`) .digest(); const given = Buffer.from(String(parts.v1 ?? ''), 'hex'); if (given.length !== expected.length || !timingSafeEqual(given, expected)) { return new Response(null, { status: 401 }); }
const delivery = request.headers.get('x-atlas-delivery') ?? ''; if (seen.has(delivery)) return new Response(null, { status: 200 }); // already handled seen.add(delivery);
const payload = JSON.parse(body); // e.g. { pageId, route, revision } for content.page.published if (typeof payload.route === 'string') revalidatePath(payload.route); else revalidateTag('atlas'); // entries, globals, translations: refresh everything tagged 'atlas' return new Response(null, { status: 200 });}The checks, in order:
- Use the raw request body.
- Reject timestamps more than 5 minutes old.
- Compute
HMAC-SHA256("<t>.<raw body>")with your signing secret and compare in constant time. - Ignore repeated
X-Atlas-Deliveryids.
Log the event type and delivery id, never the body or the secret.
Events
Section titled “Events”The request body is the event’s payload. For content.page.published it is { "pageId", "route", "revision" }. The event type is in the X-Atlas-Event header.
Webhooks are set up once, in Development, and also receive events from the live website. Each payload has environment: "development" or "production", so you can tell them apart.
| Event | When |
|---|---|
content.page.published |
A page is published or rolled back |
content.page.unpublished |
A page is taken off the website |
content.page.route.changed |
A page moves to a new address (a redirect is added) |
content.entry.published |
A collection entry is published |
content.entry.unpublished |
A collection entry is taken off the website |
content.global.published |
The header, footer or another global is published |
content.translation.published |
A translation is published |
contract.activated |
A new site contract is activated |
seo.redirects.changed |
Redirects change |
media.asset.uploaded |
A media file is uploaded |
form.published |
A form is published |
form.submission.accepted |
A form submission is accepted |
lead.created |
A new lead arrives (ids only, no personal data) |
lead.merged |
A lead is merged into another (ids only) |
bot.published |
A chatbot is published |
Atlas waits up to 10 seconds for your response and does not follow redirects. Use Studio’s delivery history to inspect payloads and failures.