Webhooks

Available onAvailabilityBooking

Listen for events on your Bookingmood account so your integration can automatically trigger real-time reactions.

Why to use webhooks

When building integrations with Bookingmood, you might want your applications to receive events as they occur in your Bookingmood accounts, so that your backend systems can execute actions accordingly. To enable webhook events, you need to register webhook endpoints. After you register them, Bookingmood can push real-time event data to your application’s webhook endpoint when events happen in your Bookingmood account. Bookingmood uses HTTPS to send webhook events to your app as a JSON payload.

Receiving webhook events is particularly useful for listening to asynchronous events such as when a booking is registered, or when a payment is paid.

Using webhooks

When an event occurs, Bookingmood generates a new Event object. A single action might result in the creation of multiple events. For example, if you confirm a booking, both the calendar_events.updated and calendar_events.confirmed events are emitted.

Event object

The Event object we send to your webhook endpoint provides a snapshot of the object that has been created or that has changed. The object has the following shape.

{
  "id": "00000000-0000-0000-0000-000000000000",
  "date": 1680064028,
  "event_type": "calendar_events.updated",
  "payload": {
    "new": {
      "id": "00000000-0000-0000-0000-000000000000",
      ...
    },
    "old": {
      "id": "00000000-0000-0000-0000-000000000000",
      ...
    }
  }
}

The event_type field in the Event object indicates the type of event that occurred. The payload field contains the new and old versions of the object that has been created or that has changed.

Event types

Bookingmood emits events for a variety of actions. The following table lists the event types that Bookingmood emits.

Event typeDescription
bookings.createdA booking was created
bookings.updatedA booking was updated
calendar_event_tasks.createdA calendar event task was created
calendar_event_tasks.updatedA calendar event task was updated
calendar_event_tasks.completedA calendar event task was completed
calendar_events.createdA calendar event was created
calendar_events.updatedA calendar event was updated
calendar_events.confirmedA calendar event was confirmed
calendar_events.cancelledA calendar event was cancelled
contacts.createdA contact was created
contacts.updatedA contact was updated
invoices.createdAn invoice was created
invoices.updatedAn invoice was updated
payments.createdA payment was created
payments.updatedA payment was updated
payments.paidA payment was paid
members.createdA member was created
members.updatedA member was updated
products.createdA product was created
products.updatedA product was updated
sites.createdA site was created
sites.updatedA site was updated
widgets.createdA widget was created
widgets.updatedA widget was updated

How to set up your webhook integration

To start receiving webhook events in your app, you can follow the steps below. You can register one endpoint to handle several different event types at once, or set up individual endpoints for specific events.

  1. Develop a webhook endpoint to receive events.
  2. Register your endpoint in Bookingmood in the "development" section of your organization's settings, or by using the API.
  3. Secure your webhook endpoint.

Retry mechanisms

To account for unexpected events when processing the webhooks, we retry the webhook delivery up to 3 times. Between each try we wait one minute.

1. Create a webhook endpoint function

Set up an HTTPS endpoint that can accept webhook requests with a POST method. Set up your endpoint function so that it:

  • Handles POST requests with a JSON payload consisting of an event object.
  • Quickly returns a successful status code (2xx) prior to any complex logic that could cause a timeout.

Example endpoint

This code snippet is a webhook function configured to check that the event type was received, to handle the event, and return a 200 response.

// This example uses Express to receive webhooks
const express = require("express");
const app = express();

// Match the raw body to content type application/json
// If you are using Express v4 - v4.16 you need to use body-parser, not express, to retrieve the request body
app.post(
  "/webhook",
  express.json({ type: "application/json" }),
  (request, response) => {
    const event = request.body;
    // Handle the event
    switch (event.event_type) {
      case "calendar_events.confirmed":
        const calendarEvent = event.payload.new;
        // Then define and call a method to handle the calendar event confirmation.
        // handleCalendarEventConfirmation(calendarEvent);
      break;
      case "products.created":
        const product = event.payload.new;
        // Then define and call a method to handle the product creation.
        // handleProductCreated(product);
      break;
        // ... handle other event types
      default:
        console.log(`Unhandled event type ${event.event_type}`);
    }
    // Return a response to acknowledge receipt of the event
    response.json({ received: true });
  }
);

app.listen(8000, () => console.log("Running on port 8000"));

2. Register and manage your webhook in Bookingmood

After writing your webhook endpoint, register the webhook endpoint’s accessible URL using the Webhooks section in your organization's settings or using the API so that Bookingmood knows where to deliver events. Registered webhook endpoints must be publicly accessible HTTPS URLs. To register your webhook endpoint, provide the publicly accessible HTTPS URL to your webhook endpoint, and select the type of events you’re receiving in your endpoint.

Manage a webhook endpoint configuration

You can always update or delete existing webhook endpoints using the Webhooks section on your organization's settings page or using the API.

3. Secure your webhooks

After confirming that your webhook endpoint connection works as expected, secure the connection by implementing webhook best practices.

One especially important best practice is to use webhook signatures to verify that Bookingmood generated a webhook request and that it didn’t come from a server acting like Bookingmood.

Event ordering

Bookingmood doesn’t guarantee delivery of events in the order in which they’re generated. For example, paying a payment might generate the following events:

  • payments.updated
  • payments.paid

Your endpoint shouldn’t expect delivery of these events in this order, and needs to handle delivery accordingly. You can also use the API to fetch any missing objects (for example, you can fetch the related invoice using the information from payments.paid).

Handle duplicate events

Webhook endpoints might occasionally receive the same event more than once. You can guard against duplicated event receipts by making your event processing idempotent. One way of doing this is logging the events you’ve processed, and then not processing already-logged events.

Only listen to event types your integration requires

Configure your webhook endpoints to receive only the types of events required by your integration. Listening for extra events (or all events) puts undue strain on your server and we don’t recommend it. You can change the events that a webhook endpoint receives in the Dashboard or with the API.

Verify events are sent from Bookingmood

Use webhook signatures to confirm that received events are sent from Bookingmood. Bookingmood signs webhook events by including a signature in each event’s X-Signature header. This allows you to verify that the events were sent by Bookingmood, not by a third party.

The following section describes how to verify webhook signatures:

  1. Retrieve your endpoint’s secret.
  2. Verify the signature.

Retrieving your endpoint’s secret

Bookingmood generates a unique secret key for each endpoint. To retrieve the secret, use the Webhooks section on your organization's settings page and copy the secret below the endpoint URL. You can also use the API to retrieve the secret. Additionally, if you use multiple endpoints, you must obtain a secret for each one you want to verify signatures on.

Verifying the secret

To verify the signature that Bookingmood sends with each event, perform the following steps:

  1. Prepare the signed payload by concatenating your signing_secret with the character . and then the stringified request body.
  2. Compute the expected signature by hashing the signed payload with the MD5 hash function.
  3. Compare the expected signature to the signature in the X-Signature header. If the signatures match, the event is verified.

Example of verifying signatures manually

// Set your secret key.
const signingSecret = "...";
// Make sure that the secret is never exposed to the client

// This example uses Express to receive webhooks
const express = require("express");
const crypto = require("crypto");
const app = express();

app.post(
  "/webhook",
  express.json({ type: "application/json" }),
  (request, response) => {
    const signature = request.headers["x-signature"];
    const signedPayload = `${signingSecret}.${JSON.stringify({
      id: request.body.id,
      event_type: request.body.event_type,
      date: request.body.date,
      payload: request.body.payload
    })}`;
    const expectedSignature = crypto
      .createHash("md5")
      .update(signedPayload)
      .digest("hex");

    if (signature !== expectedSignature) {
      response.status(401).send("Invalid signature");
      return;
    }
  
    // Handle the event
    switch (event.type) {
      default:
        console.log(`Received unhandled event type: ${event.type}`);
    }

    // Return a response to acknowledge receipt of the event
    response.json({ received: true });
  }
);

app.listen(8000, () => console.log("Running on port 8000"));

Preventing replay attacks

A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks, Bookingmood includes a timestamp in the X-Signature header. Because this timestamp is part of the signed payload, it’s also verified by the signature, so an attacker can’t change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you can have your application reject the payload. We suggest to tolerate at most 5 minutes between the timestamp and the current time. Bookingmood generates the timestamp and signature each time we send an event to your endpoint. If Bookingmood retries an event (for example, your endpoint previously replied with a non-2xx status code), then we generate a new signature and timestamp for the new delivery attempt.

Last modified April 19, 2024

On this page

Why to use webhooksUsing webhooksEvent objectEvent typesHow to set up your webhook integrationRetry mechanisms1. Create a webhook endpoint functionExample endpoint2. Register and manage your webhook in BookingmoodManage a webhook endpoint configuration3. Secure your webhooksEvent orderingHandle duplicate eventsOnly listen to event types your integration requiresVerify events are sent from BookingmoodRetrieving your endpoint’s secretVerifying the secretExample of verifying signatures manuallyPreventing replay attacks