For the complete documentation index, see llms.txt.

Webhooks

Subscribe to real-time events when bookings are created, updated, or cancelled.

Overview

Vennio sends HTTP POST requests to your registered URL whenever a subscribed event occurs. Webhook deliveries include a signature header for verification and are retried up to 3 times with exponential backoff.

Webhook events

Event Description
booking.created A new booking was made
booking.updated A booking was rescheduled
booking.confirmed A paid booking was confirmed after successful payment
booking.cancelled A booking was cancelled
consent.granted A user granted calendar access
consent.revoked A user revoked calendar access

Create a webhook

curl -X POST https://api.vennio.app/v1/webhooks \
  -H "Authorization: Bearer vennio_sk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/vennio",
    "events": ["booking.created", "booking.cancelled"],
    "description": "My booking notifications"
  }'
{
  "id": "wh_abc123",
  "url": "https://your-app.com/webhooks/vennio",
  "events": ["booking.created", "booking.cancelled"],
  "secret": "whsec_xxxxxxxxxxxxxxxxxxxx",
  "created_at": "2026-01-20T10:00:00Z"
}
Save your webhook secret

The webhook signing secret is only shown once at creation. Use it to verify incoming payloads.

Verify signatures

Every webhook delivery includes a X-Webhook-Signature header containing an HMAC-SHA256 signature of the raw request body. Always verify this before processing.

import crypto from 'crypto'

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex')
  if (signature.length !== expected.length) return false
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )
}

// Express handler
app.post('/webhooks/vennio', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature']
  if (!verifyWebhookSignature(req.body, sig, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' })
  }

  const event = JSON.parse(req.body)
  console.log('Event received:', event.type)

  // Process event...
  res.json({ received: true })
})

Retry schedule

If your endpoint returns a non-2xx status or times out, Vennio retries with backoff:

Attempt Delay
1st retry 1 minute
2nd retry 5 minutes
3rd retry 30 minutes
Failed Marked as failed after 4 total attempts

View delivery history at GET /v1/webhooks/{id}/deliveries.

Event payloads

All events follow a consistent envelope:

{
  "id": "evt_abc123",
  "type": "booking.created",
  "created_at": "2026-01-20T14:30:00Z",
  "data": {
    "booking": {
      "id": "booking_xyz789",
      "status": "confirmed",
      "start_time": "2026-01-21T09:00:00Z",
      "end_time": "2026-01-21T09:30:00Z",
      "customer_email": "customer@example.com",
      "customer_name": "Jane Doe"
    }
  }
}

For paid bookings, the booking.confirmed event includes payment details:

{
  "id": "evt_def456",
  "type": "booking.confirmed",
  "created_at": "2026-03-15T10:05:00Z",
  "data": {
    "booking": {
      "id": "booking_xyz789",
      "status": "confirmed",
      "start_time": "2026-03-15T10:00:00Z",
      "end_time": "2026-03-15T10:30:00Z",
      "customer_email": "customer@example.com",
      "customer_name": "Jane Doe",
      "payment_amount": 5000,
      "payment_currency": "usd"
    }
  }
}