News Malta Budget 2027 announced an eco tax of €10 per adult per night for short lets (capped at €80 per person) from 1 January 2027 — not yet law. Read more
Guestbud

Developers · for developers

Webhooks

Receive signed real-time events — bookings, check-ins, payments, unlocked stays, extras and guest messages — and verify the HMAC-SHA256 signature.

Webhooks push events to your system the moment they happen, so you don't need to poll the API.

Events #

EventWhen
booking.createdA booking was created (dashboard, channel import or API)
booking.updatedDates, guests or contact details changed
booking.cancelledA booking was cancelled
checkin.completedThe guest completed (or updated) the online check-in
payment.completedTourist tax paid or deposit held/paid
booking.unlockedAll steps are done — the stay guide is unlocked
extras.orderedThe guest paid for extras
message.sentA message was sent to the guest through the booking channel (Airbnb, Booking.com … via Hostaway or Channex)
pingTest event from the Test button

Add a webhook #

Settings → Webhooks: enter an https:// URL, tick the events you want (none = all) and click Add webhook. The signing secret is shown once — store it with your receiver.

Settings → Webhooks
Settings → Webhooks

The request #

POST with a JSON body:

{
  "id": "evt_…",
  "event": "payment.completed",
  "createdAt": "2026-11-18T14:03:11.000Z",
  "tenant": "t_…",
  "data": { "bookingId": "b_…", "leg": "eco", "status": "paid" }
}

Headers:

HeaderContent
X-GP-Eventthe event name
X-GP-Deliveryunique delivery ID
X-GP-TimestampUnix time in seconds
X-GP-Signaturev1= + hex HMAC-SHA256 of <timestamp>.<raw body> with your signing secret

Verify the signature (Node.js) #

const crypto = require('crypto');
function verify(rawBody, headers, secret) {
  const ts = headers['x-gp-timestamp'];
  const expected = 'v1=' + crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
  const given = String(headers['x-gp-signature'] || '');
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}

Always verify against the raw request body, and reject old timestamps to block replays.

Delivery and retries #

Answer with any 2xx status within 10 seconds. Failed deliveries are retried after 30 seconds and after 5 minutes. Recent deliveries shows the last 200 attempts with their result. Use id to ignore duplicates.

Frequently asked questions #

Do I get the full booking in the event? #

The event carries IDs and the change. Fetch the full booking with GET /api/v1/bookings/{id} using an API key — that way no personal data travels in webhook bodies.

Are webhooks sent from the demo account? #

No.