Bookings Webhooks

This guide explains how to receive real-time notifications for booking events on the Plum Guide platform.

Overview

Webhooks allow you to receive instant notifications when booking events occur, such as new booking requests, confirmations, or cancellations. This eliminates the need to poll the API for booking updates.


Quick Start

1. Configure Bookings Webhook Endpoint

POST /v2/webhooks/config
Authorization: Bearer {your_token}
Content-Type: application/json

{
  "isActive": true,
  "endpoints": {
    "bookings": "https://your-domain.com/webhooks/bookings"
  }
}

2. Receive Booking Events

When a booking event occurs, your endpoint receives:

{
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:00Z",
  "bookingCode": "PG-ABC123",
  "listingId": null
}

3. Fetch Booking Details

Use the booking code to get full details:

GET /v2/bookings/{bookingCode}
Authorization: Bearer {your_token}

Booking Events

EventDescriptionAction Required
Booking RequestedThe guest requested a bookingApprove or decline within 24 hours
Booking ConfirmedBooking confirmed via Instant BookPrepare for guest arrival
Booking ApprovedHost approved a booking requestPrepare for guest arrival
Booking DeclinedThe host declined a booking requestNo action needed
Booking CancelledThe booking was cancelledUpdate availability

Webhook Payload

{
  "eventId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-01-15T10:30:00Z",
  "bookingCode": "PG-ABC123",
  "listingId": null
}
FieldTypeDescription
eventIdUUIDUnique identifier for this event (use for idempotency)
timestampISO 8601When the event occurred
bookingCodestringThe booking's unique code
listingIdnullAlways null for booking events

Configuration API

Create/Update Configuration

POST /v2/webhooks/config
Content-Type: application/json
Authorization: Bearer {token}

{
  "isActive": true,
  "endpoints": {
    "bookings": "https://your-domain.com/webhooks/bookings"
  }
}

Get Configuration

GET /v2/webhooks/config
Authorization: Bearer {token}

Toggle On/Off

PATCH /v2/webhooks/config/toggle?isActive=false
Authorization: Bearer {token}

Delete Configuration

DELETE /v2/webhooks/config
Authorization: Bearer {token}

Requirements:

  • Endpoint URL must use HTTPS
  • Your endpoint must return 200 OK within 30 seconds

Handling Booking Events

Booking Requested

A guest has requested to book. You must approve or decline:

app.post('/webhooks/bookings', async (req, res) => {
  res.status(200).send('OK');

  const { bookingCode } = req.body;
  const booking = await plumApi.getBooking(bookingCode);

  if (booking.status === 'Request') {
    // Review booking and approve/decline
    await notifyHostOfNewRequest(booking);
  }
});

Booking Confirmed (Instant Book)

The booking was automatically confirmed:

if (booking.status === 'Confirmed') {
  // Update your calendar
  await blockDates(booking.listingId, booking.checkIn, booking.checkOut);

  // Send confirmation to your system
  await createReservation(booking);
}

Booking Cancelled

Update availability when a booking is cancelled:

if (booking.status === 'Cancelled') {
  // Release the dates
  await unblockDates(booking.listingId, booking.checkIn, booking.checkOut);

  // Update your records
  await cancelReservation(booking.bookingCode);
}

Complete Example Handler

const processedEvents = new Set();

app.post('/webhooks/bookings', async (req, res) => {
  const { eventId, bookingCode } = req.body;

  // Idempotency check
  if (processedEvents.has(eventId)) {
    return res.status(200).send('Already processed');
  }

  // Acknowledge receipt immediately
  res.status(200).send('OK');
  processedEvents.add(eventId);

  try {
    // Fetch full booking details
    const booking = await plumApi.getBooking(bookingCode);

    switch (booking.status) {
      case 'Request':
        await handleNewRequest(booking);
        break;
      case 'Confirmed':
        await handleConfirmation(booking);
        break;
      case 'Cancelled':
        await handleCancellation(booking);
        break;
      case 'Declined':
        await handleDecline(booking);
        break;
    }
  } catch (error) {
    console.error('Failed to process booking webhook:', error);
  }
});

Best Practices

  1. Respond quickly - Return 200 OK immediately, process asynchronously
  2. Implement idempotency - Use eventId to avoid processing duplicates
  3. Fetch full details - The webhook payload only contains bookingCode
  4. Handle all statuses - Process each booking status appropriately
  5. Log events - Keep records for debugging and auditing

Booking Statuses

When you fetch booking details, the status field indicates the current state:

StatusDescription
InitiatedBooking started but not completed
RequestGuest requested booking, awaiting approval
ConfirmedBooking is confirmed
DeclinedHost declined the booking
CancelledBooking was cancelled

Troubleshooting

Not receiving webhooks?

  1. Verify configuration: GET /v2/webhooks/config
  2. Check isActive is true
  3. Ensure endpoints.bookings is set
  4. Confirm URL is HTTPS and publicly accessible

Missing booking details?

The webhook only contains bookingCode. Fetch full details:

GET /v2/bookings/{bookingCode}

Receiving duplicates?

This is expected. Use eventId for deduplication:

const processedEvents = new Set();

if (processedEvents.has(eventId)) {
  return res.status(200).send('Already processed');
}
processedEvents.add(eventId);

Testing

Local Development

Use ngrok to expose your local server:

ngrok http 3000

Then configure the webhook:

POST /v2/webhooks/config
{
  "isActive": true,
  "endpoints": {
    "bookings": "https://abc123.ngrok.io/webhooks/bookings"
  }
}

Testing Checklist

  • Endpoint returns 200 OK within 30 seconds
  • Handles duplicate events (same eventId)
  • Fetches full booking details via API
  • Processes all booking statuses correctly
  • Updates your system appropriately