Webhooks Overview
Webhooks let your application receive real-time HTTP notifications when content events occur in your Vivreal group — such as a collection object being created, updated, or deleted, or an integration object (e.g., a Stripe product) changing.
How Webhooks Work
When an event occurs, Vivreal queues the event and asynchronously sends an HTTP POST request to your registered endpoint URL with a JSON payload describing the event. Your server processes the payload and returns a 2xx response to acknowledge receipt.
Vivreal Event → SQS Queue → HTTP POST → Your Endpoint → 2xx OK
If your endpoint does not respond with a 2xx status code (or times out), Vivreal retries delivery via the queue and auto-disables the endpoint after repeated consecutive failures (see Retry Policy).
Setting Up a Webhook
Create an endpoint
Build an HTTP endpoint in your application that accepts POST requests. The endpoint should parse JSON bodies and return a 200 status on success.
// Express.js example
app.post('/webhooks/vivreal', (req, res) => {
const event = req.body;
console.log('Received event:', event.event);
// Process the event...
res.status(200).json({ received: true });
});
Register the endpoint in Vivreal
In the Vivreal portal, go to Group Settings and find the Webhooks section. Click Add Endpoint and enter your public URL (must be HTTPS).
Select event types
Choose which events should be sent to this endpoint. See Event Types for the full list of events Vivreal emits (for example, content.created or integration.updated).
Copy the signing secret
After creating the webhook, Vivreal generates a signing secret. Copy it and store it securely — you will use it to verify that incoming requests are genuinely from Vivreal.
Signature Verification
Every webhook request includes an X-Vivreal-Signature header in the form sha256=<hex> — the value of the HMAC-SHA256 signature of the raw request body. Always verify this signature before processing the event.
import crypto from 'crypto';
function verifyWebhookSignature(rawBody, signatureHeader, secret) {
// Header format: "sha256=<hex>" — strip the prefix before comparing
if (!signatureHeader || !signatureHeader.startsWith('sha256=')) {
return false;
}
const received = signatureHeader.slice('sha256='.length);
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
const a = Buffer.from(received, 'hex');
const b = Buffer.from(expected, 'hex');
// timingSafeEqual requires equal-length buffers
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
// In your handler (use express.raw — see callout below):
app.post(
'/webhooks/vivreal',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = req.headers['x-vivreal-signature']; // Node lowercases header names
const isValid = verifyWebhookSignature(
req.body,
signature,
process.env.WEBHOOK_SECRET,
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body.toString('utf8'));
// Process event...
res.status(200).json({ received: true });
},
);
Always verify signatures
Without signature verification, anyone could send fake events to your endpoint. Use crypto.timingSafeEqual() with equal-length hex buffers to prevent timing attacks during comparison.
Use express.raw() for signature verification
You must access the raw request body (not the parsed JSON) to compute the signature correctly. Using express.json() middleware before verification will alter the body and cause signature mismatches.
Delivery Timeout
Vivreal allows your endpoint up to 5 seconds to respond. If the request times out, the delivery is considered a failure and counts toward the auto-disable threshold.
If your handler needs to perform slow work (database writes, external API calls), respond 200 first and process asynchronously.
Retry Policy
Webhook delivery is fronted by an SQS queue. When your endpoint returns a non-2xx status code or times out, the queue redelivers the message according to its visibility-timeout and maxReceiveCount policy — there is no custom exponential backoff ladder.
Vivreal tracks consecutive failures per endpoint. When an endpoint has 10 consecutive failures, it is automatically disabled and no further events are delivered until you re-enable it in the portal.
Idempotency matters
Your endpoint may receive the same event more than once due to queue redelivery. Use the X-Vivreal-Delivery header as a delivery identifier and design your processing logic to be idempotent (for example, by tracking whether a given delivery ID has already been processed, or by writing in an upsert-style pattern).
Testing Webhooks Locally
During development, your local machine is not publicly accessible. Use a tunnel service to expose your local server.
Using a tunnel service
Tools like ngrok create a public URL that tunnels to your local server:
ngrok http 3001
# Gives you: https://abc123.ngrok.io
# Register this URL as your webhook endpoint
Best Practices
- Respond quickly — Return
200immediately, then process the event asynchronously. Long-running processing should happen in a background job. Your endpoint has 5 seconds before the delivery times out. - Store raw payloads — Log the raw JSON payload for debugging, even after processing.
- Handle unknown events gracefully — New event types may be added over time. Return
200for events you do not recognize rather than erroring. - Use HTTPS — Webhook endpoints must use HTTPS to protect payload data in transit.
Next Steps
- Event Types — Full list of webhook event types.
- Payload Schemas — Detailed payload structures and examples.
Source of truth
- Delivery logic, timeout, signature, retry threshold:
VR_Secure_API/src/webhookDelivery/services/deliver.js(DELIVERY_TIMEOUT_MS,AUTO_DISABLE_THRESHOLD,X-Vivreal-Signature,X-Vivreal-Delivery,X-Vivreal-Event). - SQS consumer / batch-item-failures pattern:
VR_Secure_API/src/webhookDelivery/main.js. - Event emission (what names are emitted):
VR_CMS_API/src/shared/emitWebhookEvent.jsand allemitWebhookEvent(...)call sites underVR_CMS_API/src/.