Webhooks
Webhooks notify your systems on every content event — rebuild a static site, invalidate a cache, sync a search index.
Configure
Settings → Webhooks in the editor (or POST /spaces/{id}/webhooks). Each
webhook has a target URL, an optional shared secret, custom headers, and
filters:
{
"name": "Production rebuild",
"url": "https://api.vercel.com/v1/integrations/deploy/…",
"secret": "whsec-…",
"events": ["entry.publish", "entry.unpublish"],
"filters": { "content_types": ["article"], "environments": ["master"] }
}
Empty events/filters match everything.
Event types
entry.create entry.update entry.publish entry.unpublish
entry.archive entry.delete
content_type.create content_type.update content_type.delete
asset.create asset.update asset.delete
environment.create
Payload
{
"event": "entry.publish",
"space_id": "…",
"environment": "master",
"resource": {
"id": "2b1e…",
"content_type": "article",
"slug": "hello-world",
"status": "published"
},
"timestamp": "2026-07-01T10:00:00Z"
}
Verify the signature
Every delivery is signed: X-CMS-Signature is the hex HMAC-SHA256 of the raw
request body using your webhook secret.
// Node/Express — use the RAW body, not the parsed JSON
import crypto from 'node:crypto';
app.post('/hooks/cms', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto
.createHmac('sha256', process.env.CMS_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const given = req.get('X-CMS-Signature') ?? '';
const ok =
given.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!ok) return res.status(401).end();
const event = JSON.parse(req.body);
// …do work…
res.status(204).end();
});
Respond quickly (2xx within a few seconds) and do heavy work asynchronously — slow endpoints show up as failures in the delivery log.
Delivery log
Every attempt is recorded (status code, duration, truncated response) and visible under the webhook's Deliveries tab — the same data platform operators see aggregated in the system-health dashboard.