Skip to main content

Webhooks

Webhooks allow Bundleport to send real-time notifications to your application when events occur, eliminating the need to poll for updates.

Overview

Instead of polling the API repeatedly to check for booking status changes, webhooks deliver events directly to your server as they happen:

  • ✅ Real-time notifications
  • ✅ Reduced API calls (saves rate limit quota)
  • ✅ Better user experience (instant updates)
  • ✅ Lower server load

Supported Events

Event TypeDescriptionWhen It Fires
booking.confirmedBooking was successfully confirmedAfter successful booking creation
booking.cancelledBooking was cancelledWhen a booking is cancelled
booking.modifiedBooking details were modifiedWhen booking information changes
booking.failedBooking attempt failedWhen booking creation fails
quote.price_changedPrice changed during quoteWhen rechecking reveals price change
payment.requiredPayment action requiredWhen payment needs attention

Setting Up Webhooks

1. Create a Webhook Endpoint

Create an HTTP endpoint in your application that can receive POST requests:

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

app.use(express.json());

// Your webhook secret (from Bundleport dashboard)
const WEBHOOK_SECRET = process.env.BUNDLEPORT_WEBHOOK_SECRET;

// Verify webhook signature
function verifySignature(payload, signature) {
const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}

app.post('/webhooks/bundleport', (req, res) => {
const signature = req.headers['x-bundleport-signature'];
const payload = JSON.stringify(req.body);

// Verify signature
if (!verifySignature(payload, signature)) {
return res.status(401).send('Invalid signature');
}

const { event, data, timestamp } = req.body;

// Process the event
switch (event) {
case 'booking.confirmed':
handleBookingConfirmed(data);
break;
case 'booking.cancelled':
handleBookingCancelled(data);
break;
// ... handle other events
}

res.status(200).send('OK');
});

function handleBookingConfirmed(data) {
console.log('Booking confirmed:', data.bookingId);
// Update your database, send notifications, etc.
}

function handleBookingCancelled(data) {
console.log('Booking cancelled:', data.bookingId);
// Update your database, process refunds, etc.
}

app.listen(3000);

2. Register Your Webhook URL

  1. Log in to app.bundleport.com
  2. Navigate to SettingsWebhooks
  3. Click Add Webhook
  4. Enter your webhook URL (must be HTTPS in production)
  5. Select the events you want to receive
  6. Save your webhook secret (you'll need it to verify signatures)

3. Test Your Webhook

Use the dashboard's "Test Webhook" feature to send a test event to your endpoint.

Webhook Payload Format

All webhook payloads follow this structure:

{
"event": "booking.confirmed",
"data": {
"bookingId": "BP-123456789",
"clientReference": "CUST-001",
"providerReference": "PROV-987654321",
"status": "CONFIRMED",
"hotel": {
"code": "12345",
"name": "Example Hotel"
},
"checkIn": "2025-12-15",
"checkOut": "2025-12-17",
"price": {
"currency": "EUR",
"net": 300.00,
"suggested": 360.00
}
},
"timestamp": "2025-12-08T10:30:00Z",
"id": "evt_abc123def456"
}

Security: Verifying Signatures

Always verify webhook signatures to ensure requests are from Bundleport:

  1. Bundleport signs each webhook with HMAC-SHA256
  2. Signature is sent in the X-Bundleport-Signature header
  3. Compute HMAC-SHA256 of the raw request body using your webhook secret
  4. Compare with the signature header
Always Verify Signatures

Never process webhooks without verifying signatures. This prevents unauthorized requests.

Retry Logic

Bundleport will retry failed webhook deliveries:

  • Initial attempt: Immediate
  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 15 minutes
  • Retry 4: After 1 hour
  • Retry 5: After 6 hours

If all retries fail, the event is logged in the dashboard for manual review.

Best Practices for Retries

  1. Return 200 OK quickly: Process events asynchronously
  2. Idempotency: Handle duplicate events gracefully
  3. Logging: Log all webhook events for debugging
app.post('/webhooks/bundleport', async (req, res) => {
// Verify signature
if (!verifySignature(req.body, req.headers['x-bundleport-signature'])) {
return res.status(401).send('Invalid signature');
}

// Return 200 immediately
res.status(200).send('OK');

// Process asynchronously
processWebhookEvent(req.body).catch(error => {
console.error('Webhook processing failed:', error);
// Queue for retry or alert
});
});

Event Reference

booking.confirmed

Fired when a booking is successfully confirmed.

{
"event": "booking.confirmed",
"data": {
"bookingId": "BP-123456789",
"clientReference": "CUST-001",
"providerReference": "PROV-987654321",
"status": "CONFIRMED",
"hotel": {
"code": "12345",
"name": "Example Hotel Barcelona"
},
"checkIn": "2025-12-15",
"checkOut": "2025-12-17",
"price": {
"currency": "EUR",
"net": 300.00,
"suggested": 360.00
},
"rooms": [
{
"description": "Standard Double Room",
"confirmationReference": "ROOM-001"
}
]
}
}

booking.cancelled

Fired when a booking is cancelled.

{
"event": "booking.cancelled",
"data": {
"bookingId": "BP-123456789",
"clientReference": "CUST-001",
"status": "CANCELLED",
"cancellationFee": {
"currency": "EUR",
"amount": 50.00
},
"cancelledAt": "2025-12-10T14:30:00Z"
}
}

booking.modified

Fired when booking details are modified.

{
"event": "booking.modified",
"data": {
"bookingId": "BP-123456789",
"changes": {
"checkIn": {
"old": "2025-12-15",
"new": "2025-12-16"
},
"price": {
"old": 300.00,
"new": 320.00
}
}
}
}

Testing Webhooks Locally

For local development, use a tunneling service:

  1. ngrok: ngrok http 3000
  2. localtunnel: lt --port 3000
  3. Cloudflare Tunnel: cloudflared tunnel --url http://localhost:3000

Update your webhook URL in the dashboard to point to the tunnel URL.

Monitoring Webhooks

Monitor webhook delivery in the dashboard:

  • Delivery status: Success/failure for each event
  • Response times: How long your endpoint takes to respond
  • Retry history: Failed deliveries and retry attempts
  • Event logs: All events sent to your webhook

Next Steps