Getting Started·4 min read·

How do webhooks work in VeloCMS?

Webhooks are outbound HTTP POST notifications VeloCMS sends when events happen, signed with HMAC-SHA256 so your server can verify the sender.

A VeloCMS webhook is an outbound HTTP POST request sent to a URL you register whenever a specific event happens in your blog — like a new post being published, a new subscriber joining, or an order completing. The request body is a JSON object and the request includes an HMAC-SHA256 signature header so your receiving server can verify the request came from VeloCMS.

What does the webhook JSON payload look like?

Every webhook payload follows a consistent envelope structure: id (a unique delivery ID), event (the event type like 'post.published'), version (currently 1), created (a Unix timestamp in seconds), tenant (your blog's slug), and a data object containing the event-specific fields. For a post.published event the data object contains post_id, title, slug, status, published_at, excerpt, tags (an array of tag names), and author_name. For member.subscribed it contains member_id, email, tier, and subscribed_at. The machine-readable schema for every event is published as an AsyncAPI 3.0 document at /api/v1/asyncapi on your blog.

How do I verify that a webhook came from VeloCMS?

Every webhook request carries three headers your server needs: X-VeloCMS-Event (the event type), X-VeloCMS-Timestamp (the Unix timestamp used in the signature), and X-VeloCMS-Signature in the format v1=<hex digest>. The digest is an HMAC-SHA256 of the string v1:{timestamp}:{raw request body}, computed with your webhook endpoint's secret key — signing the timestamp alongside the body lets you reject replayed old requests. To verify: read the timestamp header, rebuild the v1:{timestamp}:{body} string from the exact raw request body, compute HMAC-SHA256 with your secret, and compare the result to the digest in the signature header. If they match, the request is genuine; if they don't, reject it. The secret is shown once in a dialog when you create the endpoint in Admin → Settings → Advanced → Webhooks — store it securely, because VeloCMS can't show it again. If you lose it, delete the endpoint and create a new one to get a fresh secret.

What HTTP status should my server return?

Return any 2xx status (200, 201, 204) within 10 seconds to acknowledge receipt. VeloCMS considers the delivery successful as soon as it receives any 2xx response — it doesn't parse your response body. If your processing takes longer than 10 seconds, the correct approach is to acknowledge receipt immediately with a 200 OK, then process the payload asynchronously in the background. Any non-2xx response — 4xx or 5xx alike — or a connection timeout triggers VeloCMS's retry logic on a widening schedule: five minutes, thirty minutes, two hours, six hours, then twenty-four hours (six attempts total, including the original). After the final attempt fails, the delivery is marked failed, the endpoint is disabled, and VeloCMS emails the site owner; you re-enable the endpoint from the webhooks panel once your server is healthy again. One caveat: redirects are never followed, so a 3xx counts as a failure — register the endpoint's final URL directly.

Can I test webhook deliveries without publishing a real post?

Yes. In Admin → Settings → Advanced → Webhooks, each registered endpoint has a lightning-bolt Send test ping button. Clicking it sends a test.ping event to your endpoint URL immediately, using the same X-VeloCMS-Signature header as a real event. This lets you verify your receiving server's signature check and response handling without needing to create real content. The payload carries the webhook ID and a short message, and the event type is test.ping, so your server can recognise it and skip processing.

The delivery ID in the webhook envelope (the 'id' field) is stable across retries — the same event always gets the same delivery ID. Use this for idempotency checks in your receiving server: store received delivery IDs and skip processing if you've already handled that ID. This prevents duplicate side effects if VeloCMS retries a delivery your server already processed.