Gareth Wilson Gareth Wilson

Guide to WeTravel Webhooks: Features and Best Practices

Published


WeTravel webhooks tell tour operators and their software partners when something happens on WeTravel: a traveller books, a payment succeeds or fails, money moves, a trip is published, or a prospective traveller downloads a brochure. If you run your CRM, accounting or operations outside WeTravel, webhooks keep them up to date without polling.

This guide covers how to switch WeTravel webhooks on, the nine event types and what triggers them, how to verify deliveries, and the best practices for running a WeTravel receiver in production. WeTravel's public documentation is short, so this guide also points out what it leaves unsaid.

What are WeTravel webhooks?

WeTravel offers webhooks to enterprise clients and software partners. They are disabled by default. Once enabled, you manage endpoints from a Manage Webhooks window, where each endpoint can listen to specific event types or to all of them, and where the Event Catalog lists every event with its structure and examples.

WeTravel doesn't publish the payload schemas, the signing scheme or the retry policy in its public docs. Hookdeck's WeTravel source type verifies deliveries as Svix messages, which is what the verification code in this guide follows.

WeTravel webhook features

FeatureDetails
AvailabilityEnterprise clients and software partners, disabled by default
EnablingProfile, Edit Profile, Webhooks section (requires an API key)
Endpoint managementManage Webhooks, then + Add Endpoint
Event filteringPer endpoint: specific events or all events
Multiple endpointsSupported
Payload referenceEvent Catalog inside Manage Webhooks
SignatureNot documented by WeTravel. Hookdeck verifies it as a Svix signature
Retries and timeoutNot documented

Common events

EventFired when
booking.createdA booking is made on a trip page or embedded widget, or added with Add Participant in Manage Trip
booking.updatedA booking changes: departure date, refund, cancellation, a participant edit, custom price, package switch, bulk action, eSignature, new payment, failed or disputed payment, trip details, balance due, or a rebooking that cancels the original
payment.createdA new payment is initiated
payment.updatedA payment moves from pending to successful or failed, from successful to refunded or disputed, or to a new trip on rebooking
transaction.createdA payment is received, or a refund, dispute, payout, instant payout, card transfer, vendor payment or wire transfer is initiated, or a top-up is received, or WeTravel adjusts the balance
transaction.updatedA rebooking moves the transaction to a new trip
trip.publishedA trip is published in the trip builder or through the Trip Builder API
lead.createdSomeone downloads a brochure or asks a question on a trip or profile page
lead.updatedThe same lead downloads another brochure or asks another question

booking.updated covers many different changes, so the event name alone won't tell you what happened. Compare the booking with your stored copy, or check the fields shown for it in the Event Catalog.

Setting up WeTravel webhooks

  1. Generate a WeTravel API key. The Webhooks section of your profile only appears once you have one.
  2. Go to Profile, then Edit Profile, find Webhooks and click Yes.
  3. Click Manage Webhooks, then + Add Endpoint. Enter your HTTPS URL and choose specific events or all events.
  4. Copy the endpoint's signing secret and store it server-side, for example as WETRAVEL_WEBHOOK_SECRET.
  5. Open the Event Catalog to see the structure of each event before writing your handler.

You can add several endpoints, and edit, delete or disable each one from the endpoint manager.

Securing WeTravel webhooks

WeTravel doesn't document how its webhooks are signed. Hookdeck verifies WeTravel deliveries with the Svix scheme: svix-id, svix-timestamp and svix-signature headers, and a base64 HMAC-SHA256 over {id}.{timestamp}.{raw body}, keyed with the base64-decoded part of a whsec_ secret. Check the headers on your first delivery to confirm the names before you rely on them.

const crypto = require('crypto');
const express = require('express');

function verifyWeTravelSignature(rawBody, headers, secret) {
  const msg_id = headers['svix-id'];
  const msg_timestamp = headers['svix-timestamp'];
  const signature_header = headers['svix-signature'];
  if (!msg_id || !msg_timestamp || !signature_header) return false;
  if (typeof secret !== 'string' || !secret.startsWith('whsec_')) return false;

  const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
  const signed_content = Buffer.concat([Buffer.from(`${msg_id}.${msg_timestamp}.`, 'utf8'), rawBody]);
  const expected = Buffer.from(
    crypto.createHmac('sha256', key).update(signed_content).digest('base64'),
    'utf8'
  );

  return signature_header.split(' ').some((entry) => {
    const [version, signature] = entry.split(',');
    if (version !== 'v1' || !signature) return false;
    const received = Buffer.from(signature, 'utf8');
    return received.length === expected.length && crypto.timingSafeEqual(received, expected);
  });
}

const app = express();

app.post('/webhooks/wetravel', express.raw({ type: '*/*' }), (req, res) => {
  const secret = process.env.WETRAVEL_WEBHOOK_SECRET;
  if (!secret) return res.sendStatus(500); // fail closed

  if (!verifyWeTravelSignature(req.body, req.headers, secret)) {
    return res.sendStatus(400);
  }

  const event = JSON.parse(req.body.toString('utf8'));
  // Deduplicate on the svix-id header, queue the event, then respond.
  res.sendStatus(200);
});

The same check in Python:

import base64
import hashlib
import hmac
import os


def verify_wetravel_signature(raw_body: bytes, headers, secret: str) -> bool:
    msg_id = headers.get("svix-id")
    msg_timestamp = headers.get("svix-timestamp")
    signature_header = headers.get("svix-signature")
    if not (msg_id and msg_timestamp and signature_header):
        return False
    if not secret or not secret.startswith("whsec_"):
        return False

    key = base64.b64decode(secret[len("whsec_"):])
    signed_content = f"{msg_id}.{msg_timestamp}.".encode("utf-8") + raw_body
    expected = base64.b64encode(hmac.new(key, signed_content, hashlib.sha256).digest())

    for entry in signature_header.split(" "):
        version, _, signature = entry.partition(",")
        if version == "v1" and hmac.compare_digest(signature.encode("utf-8"), expected):
            return True
    return False


# In FastAPI: raw_body = await request.body()
# verify_wetravel_signature(raw_body, request.headers, os.environ["WETRAVEL_WEBHOOK_SECRET"])

If your deliveries carry webhook-id, webhook-timestamp and webhook-signature instead, swap the header names. The scheme is the same.

Make WeTravel webhooks production-ready. Hookdeck Event Gateway verifies WeTravel deliveries, queues every booking, payment and transaction event, and keeps them available for replay.

Hookdeck's WeTravel source type verifies the signature with the secret you enter and accepts either svix- or webhook- header names. Verification is optional on the source, so leave it off if you didn't set a secret. Hookdeck does not apply a timestamp window.

WeTravel webhook limitations and pain points

Most of the reference is behind a login

The Problem: You can't design a handler from the public docs alone.

Why It Happens: WeTravel's public page lists event names and triggers. The payload structures and examples live in the Event Catalog inside Manage Webhooks, and signing, retries and timeouts aren't documented.

Workarounds:

  • Read the Event Catalog before writing code, and keep a copy of the examples in your repository as test fixtures.
  • Log the headers and body of your first real deliveries.

How Hookdeck Can Help: Hookdeck stores every request with its headers and body, so you can inspect real WeTravel events and replay them against your handler while you build it.

Broad update events

The Problem: A booking.updated arrives, and you don't know whether it was a refund, a date change or a new eSignature.

Why It Happens: WeTravel fires booking.updated for more than a dozen kinds of change.

Workarounds:

  • Store the latest booking you have seen and diff it against the event.
  • Listen to payment.* and transaction.* for money movements rather than inferring them from booking.updated.

How Hookdeck Can Help: Hookdeck filters can route events by body fields, so payment and lead events can go to different services from booking events.

Unknown retry behaviour

The Problem: You can't tell how long WeTravel will keep trying after your endpoint goes down.

Why It Happens: WeTravel doesn't document retries or the response timeout.

Workarounds:

  • Respond with a 2xx quickly and process in the background.
  • After an outage, reconcile bookings and payments through the WeTravel API.

How Hookdeck Can Help: Hookdeck accepts WeTravel's deliveries while your service is down and holds them until you replay them, whatever WeTravel's own retry policy is.

Best practices

Verify against the raw body

Use express.raw() in Express, Buffer.from(await request.arrayBuffer()) in a Next.js route handler, or await request.body() in FastAPI, and parse only after verification.

Subscribe only to what you use

Each endpoint can listen to specific events. Separate endpoints for bookings, payments and leads make each handler simpler.

Make handlers idempotent

Deduplicate on the delivery's message ID header and make updates safe to repeat. See our guide to webhook idempotency.

Acknowledge fast and process asynchronously

Return a 2xx as soon as the event is stored. See why to process webhooks asynchronously.

Conclusion

WeTravel webhooks cover bookings, payments, transactions, trips and leads, and you switch them on from your profile once you have an API key. The public docs stop at event names and triggers, so use the Event Catalog for payloads, verify each delivery with your endpoint's whsec_ secret, and confirm the headers on your first real event.

Hookdeck Event Gateway verifies WeTravel deliveries and holds each event until your service has processed it.

Get started with Hookdeck for free and handle WeTravel webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.