Skip to content

Webhook Payload Schemas

Payload structure, example payloads, and HMAC signature verification for Vivreal webhooks

intermediate8 min readFor developers

Webhook Payload Schemas

Every webhook delivery from Vivreal follows a consistent JSON structure. This guide documents the general payload format and provides examples for key event types.

General Payload Structure

All webhook payloads share this top-level structure:

{
  "id": "evt_abc123def456",
  "event": "collection.object.created",
  "timestamp": "2026-03-15T14:30:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    // Event-specific data
  }
}
FieldTypeDescription
idstringUnique event identifier. Use for deduplication.
eventstringThe event type (see Event Types).
timestampstringISO 8601 timestamp of when the event occurred.
groupIdstringThe group where the event originated.
dataobjectEvent-specific payload. Structure varies by event type.

HTTP Headers

Each webhook request includes these headers:

HeaderDescription
Content-Typeapplication/json
x-vivreal-signatureHMAC-SHA256 hex digest of the raw request body
x-vivreal-eventThe event type (same as event in the body)
x-vivreal-delivery-idUnique delivery ID (differs from event ID on retries)

Example Payloads

collection.object.created

Fired when a new object is added to a collection.

{
  "id": "evt_co_created_001",
  "event": "collection.object.created",
  "timestamp": "2026-03-15T14:30:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "objectId": "obj_abc123",
    "collectionId": "col_def456",
    "collectionName": "Products",
    "fields": {
      "name": "Wireless Headphones",
      "price": 79.99,
      "currency": "USD"
    },
    "author": "user@example.com",
    "publishDate": null,
    "archived": false
  }
}

collection.object.updated

Fired when an existing object is modified. Includes both the changed fields and a summary of what changed.

{
  "id": "evt_co_updated_002",
  "event": "collection.object.updated",
  "timestamp": "2026-03-15T15:00:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "objectId": "obj_abc123",
    "collectionId": "col_def456",
    "collectionName": "Products",
    "fields": {
      "name": "Wireless Headphones Pro",
      "price": 99.99,
      "currency": "USD"
    },
    "changedFields": ["name", "price"],
    "author": "user@example.com",
    "version": 3
  }
}

collection.object.deleted

{
  "id": "evt_co_deleted_003",
  "event": "collection.object.deleted",
  "timestamp": "2026-03-15T15:30:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "objectId": "obj_abc123",
    "collectionId": "col_def456",
    "collectionName": "Products",
    "deletedBy": "admin@example.com"
  }
}

integration.connected

{
  "id": "evt_int_connected_004",
  "event": "integration.connected",
  "timestamp": "2026-03-15T16:00:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "integrationId": "int_ghi789",
    "provider": "stripe",
    "authMethod": "api_key",
    "connectedBy": "admin@example.com"
  }
}

site.deployed

{
  "id": "evt_site_deployed_005",
  "event": "site.deployed",
  "timestamp": "2026-03-15T17:00:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "siteId": "site_jkl012",
    "siteName": "My Store",
    "url": "https://mystore.vivreal.app",
    "customDomain": "www.mystore.com",
    "template": "ecommerce",
    "deployDuration": 145
  }
}

site.failed

{
  "id": "evt_site_failed_006",
  "event": "site.failed",
  "timestamp": "2026-03-15T17:05:00.000Z",
  "groupId": "grp_789xyz",
  "data": {
    "siteId": "site_jkl012",
    "siteName": "My Store",
    "error": "Build failed: missing required environment variable",
    "stage": "build"
  }
}

Signature Verification

Always verify the x-vivreal-signature header before trusting the payload. Here is a complete example in Node.js:

import crypto from 'crypto';
import express from 'express';

const app = express();
const WEBHOOK_SECRET = process.env.VIVREAL_WEBHOOK_SECRET;

app.post(
  '/webhooks/vivreal',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-vivreal-signature'];
    const rawBody = req.body.toString('utf8');

    // Compute expected signature
    const expected = crypto
      .createHmac('sha256', WEBHOOK_SECRET)
      .update(rawBody)
      .digest('hex');

    // Constant-time comparison to prevent timing attacks
    const isValid = crypto.timingSafeEqual(
      Buffer.from(signature, 'utf8'),
      Buffer.from(expected, 'utf8')
    );

    if (!isValid) {
      console.error('Webhook signature verification failed');
      return res.status(401).json({ error: 'Invalid signature' });
    }

    // Parse and process the event
    const event = JSON.parse(rawBody);
    console.log(`Received ${event.event} (${event.id})`);

    // Acknowledge receipt immediately
    res.status(200).json({ received: true });

    // Process asynchronously if needed
    processEventAsync(event).catch(console.error);
  }
);

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.

Python Verification Example

import hmac
import hashlib
import json

def verify_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Handling Large Payloads

Most webhook payloads are small (under 10 KB). However, events for objects with rich text or many fields can be larger. Your endpoint should accept payloads up to 256 KB. If a payload exceeds this limit, Vivreal truncates the data.fields object and includes a truncated: true flag, along with the objectId so you can fetch the full object via the API.