Agent skill
Ventrata Webhooks Skill
Receive and authenticate Ventrata OCTO webhooks. Use when setting up a Ventrata webhook endpoint, checking the secret header you set when creating the webhook, debugging 401s, or handling booking_update, order_update, availability_update and product_update events.
Install this skill
npx skills add hookdeck/webhook-skills --skill ventrata-webhooks
Ventrata (tours and attractions ticketing) delivers OCTO webhooks when orders, bookings, availability or products change. You create the webhook through the OCTO API with the octo/webhooks capability, and Ventrata POSTs a JSON payload to your URL.
Ventrata webhooks are not signed. Add a secret header when you create the webhook through the OCTO API and enter it here.
There is no HMAC and no timestamp. You authenticate requests with a secret header that you choose when you create the webhook (the headers map). Ventrata sends it on every delivery, and your endpoint compares it with the value you stored.
When to Use This Skill
- How do I receive Ventrata webhooks?
- Does Ventrata sign webhooks? (No: use a secret header from the webhook's
headersmap) - How do I authenticate a Ventrata OCTO webhook request?
- How do I handle
booking_update,order_update,availability_updateorproduct_update? - Why does my Ventrata webhook endpoint return 401?
Verification (core)
const crypto = require('crypto');
// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
const ha = crypto.createHash('sha256').update(String(a)).digest();
const hb = crypto.createHash('sha256').update(String(b)).digest();
return crypto.timingSafeEqual(ha, hb);
}
function verifyVentrataRequest(headers, secret = process.env.VENTRATA_WEBHOOK_SECRET,
headerName = process.env.VENTRATA_WEBHOOK_HEADER || 'X-Webhook-Secret') {
if (!secret) return false; // never accept when unconfigured
const received = headers[headerName.toLowerCase()]; // node lowercases header names
return Boolean(received) && safeEqual(received, secret); // compare the WHOLE value
}
- The header name is whatever you put in
headerswhen creating the webhook. Read it fromVENTRATA_WEBHOOK_HEADER(defaultX-Webhook-Secret) and look it up case-insensitively. VENTRATA_WEBHOOK_SECRETis the exact, whole header value, including anyBearerprefix you typed. Compare the whole value; do not strip or parse a prefix.- Python uses
hmac.compare_digeston bytes (encode both sides; passingstrraises on non-ASCII). - Missing header or mismatch:
401. A body that isn't a JSON object:400. Handled:200. - Read the body raw (
express.raw({ type: '*/*' })) so any content type works, and parse it only after the check. - A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential if it leaks.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Event Types
The body is {event, webhook, ...} plus the objects the event concerns: order, booking, product, availability, supplier and diff (the last for order, booking and product updates). The capability documents these four events, while the payload examples write booking.updated (the docs show no dotted form for the other three). Handlers should accept both spellings and dispatch on event.
| Event | Sent When |
|---|---|
order_update | An order changes |
booking_update (payload examples: booking.updated) | A booking changes |
availability_update | Availability changes |
product_update | A product changes |
Delivery is always an HTTP POST. Retries are per webhook (retryOnError).
Environment Variables
VENTRATA_WEBHOOK_HEADER=X-Webhook-Secret # the header name you chose in the webhook's headers map
VENTRATA_WEBHOOK_SECRET=your-secret-value # the exact, whole header value (including any "Bearer " prefix)
Local Development
Ventrata can't reach localhost. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 ventrata --path /webhooks/ventrata
Use the printed URL as the webhook url when you create the webhook.
Resources
- references/overview.md - What Ventrata webhooks are, events and payload shape
- references/setup.md - Create the webhook through the OCTO API with a secret header
- references/verification.md - Header check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests