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
| Event | Description | Action Required |
|---|---|---|
| Booking Requested | The guest requested a booking | Approve or decline within 24 hours |
| Booking Confirmed | Booking confirmed via Instant Book | Prepare for guest arrival |
| Booking Approved | Host approved a booking request | Prepare for guest arrival |
| Booking Declined | The host declined a booking request | No action needed |
| Booking Cancelled | The booking was cancelled | Update availability |
Webhook Payload
{
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"timestamp": "2025-01-15T10:30:00Z",
"bookingCode": "PG-ABC123",
"listingId": null
}| Field | Type | Description |
|---|---|---|
eventId | UUID | Unique identifier for this event (use for idempotency) |
timestamp | ISO 8601 | When the event occurred |
bookingCode | string | The booking's unique code |
listingId | null | Always 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 OKwithin 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
- Respond quickly - Return
200 OKimmediately, process asynchronously - Implement idempotency - Use
eventIdto avoid processing duplicates - Fetch full details - The webhook payload only contains
bookingCode - Handle all statuses - Process each booking status appropriately
- Log events - Keep records for debugging and auditing
Booking Statuses
When you fetch booking details, the status field indicates the current state:
| Status | Description |
|---|---|
Initiated | Booking started but not completed |
Request | Guest requested booking, awaiting approval |
Confirmed | Booking is confirmed |
Declined | Host declined the booking |
Cancelled | Booking was cancelled |
Troubleshooting
Not receiving webhooks?
- Verify configuration:
GET /v2/webhooks/config - Check
isActiveistrue - Ensure
endpoints.bookingsis set - 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 3000Then configure the webhook:
POST /v2/webhooks/config
{
"isActive": true,
"endpoints": {
"bookings": "https://abc123.ngrok.io/webhooks/bookings"
}
}Testing Checklist
- Endpoint returns
200 OKwithin 30 seconds - Handles duplicate events (same
eventId) - Fetches full booking details via API
- Processes all booking statuses correctly
- Updates your system appropriately
