Webhooks & data

Every booking, sent where your team works

Microsoft Bookings has no webhooks. Switch on Store bookings for a widget and BookingsXP sends each booking to Zapier, Make, n8n, Power Automate, your CRM, a Teams channel or Slack, with the campaign that brought it.

Off by default. Nothing personal is stored until you turn it on

Teams · #front-deskIllustrative

Workflows11:02 AM

New booking: Alex Rivera · Initial assessment · Wed, Oct 14, 10:15 AM EDT

Service
Initial assessment
When
Wed, Oct 14, 10:15 AM EDT
With
Dr. Maya Chen
Source
google / cpc · spring-physio
Customer
Alex Rivera (alex.rivera@example.com)
Reference
BXP-7K3M9Q

via Physio page · Northwind Wellness

Slack · #bookingsIllustrative

BookingsXPApp11:02 AM

New booking: Alex Rivera · Initial assessment · Wed, Oct 14, 10:15 AM EDT

Service
Initial assessment
When
Wed, Oct 14, 10:15 AM EDT
With
Dr. Maya Chen
Source
google / cpc · spring-physio
Customer
Alex Rivera alex.rivera@example.com
Ref
BXP-7K3M9Q

via Physio page · Northwind Wellness

Your data

Off by default, on when you need it

Out of the box, BookingsXP keeps no names, emails or answers. The booking lives in your Microsoft 365 tenant and the dashboard counts anonymous visits. “Store bookings in BookingsXP” is a per-widget switch, for the widgets where you want the data in other tools.
What BookingsXP stores with Store bookings off and on
DataStore bookings offDefaultStore bookings on
Funnel eventsAnonymous: the step, time, page, source, medium, campaign, device, country and a random per-visit IDThe same anonymous events
Name, email and phoneNot stored. They go to Microsoft Bookings and nowhere elseStored with the booking
Notes and answers to your questionsNot storedStored with the booking
Campaign detailSource, medium, campaign and channel, with no link to a personAlso the full UTMs, click IDs, first page and referrer, attached to the booking
IP addressesNever storedNever stored
Bookings list, search and CSVNot availableAvailable (CSV export on Pro and Business)
Webhooks, Teams and Slack alertsNot availableAvailable on Pro and Business
How long it is keptEvents: 30 days on Free, 12 months on Pro, 24 months on BusinessBookings: 30 days on Free, 12 months on Pro, 24 months on Business

Bookings list

Search, export and erase

With Store bookings on, every booking made through the widget appears in one list across all your widgets and locations, with the source that brought it.
Search
Find a booking by name, email, reference, service or campaign. Useful when a customer quotes a BXP reference, or when sales asks which bookings came from one campaign.
CSV export
Every stored booking with its customer, answers and attribution, for spreadsheets and CRMs that import files (Pro and Business).
Erase by email
Delete every stored booking for one email address in one step, for data-subject requests. Microsoft Bookings is not changed.
Delete one, or stop collecting
Delete a single booking at any time. Switching Store bookings off stops new copies; existing ones are deleted when they pass your plan's retention period.

Webhooks

Signed JSON for anything that accepts a URL

Each booking is POSTed as JSON to the endpoints you add, signed with HMAC-SHA256 so you can prove it came from BookingsXP. The full reference is in the webhooks docs.

booking.created payload

JSON
{
  "id": "evt_4f1c9b2e7a6d4e0f8b3a5c7d9e1f2a4b",
  "type": "booking.created",
  "created": "2026-09-29T15:02:14.318Z",
  "apiVersion": "2026-09-01",
  "data": {
    "booking": {
      "reference": "BXP-7K3M9Q",
      "id": "AAMkADk2ZTJhNzQwLWE3YjEtNGQ1Mi05ZjE0LTgyNmQ0ZDhjOWUxMwBGAAA=",
      "status": "confirmed",
      "start": "2026-10-14T14:15:00.000Z",
      "end": "2026-10-14T15:00:00.000Z",
      "timeZone": "America/New_York",
      "service": {
        "id": "8b3c2f4e-1d7a-4c59-9e0b-6a2f71d4c8e3",
        "name": "Initial assessment"
      },
      "staff": [
        {
          "id": "f41e9a27-3c80-4b6d-a5e2-0d9c7b18e664",
          "name": "Dr. Maya Chen"
        }
      ],
      "customer": {
        "name": "Alex Rivera",
        "email": "alex.rivera@example.com",
        "phone": "+1 555 0142",
        "notes": "Left knee, running injury."
      },
      "answers": [
        {
          "questionId": "2c7d51a9-86e4-4f03-b1da-5e9f0c3a7b42",
          "question": "How did you hear about us?",
          "answer": "google / cpc / spring-physio"
        }
      ],
      "manageUrl": "https://outlook.office.com/book/NorthwindWellness@northwind.example/id/3f9a6c1e-72d4-4b8e-a015-c6e2d9b7f840?isAnonymous=true",
      "joinUrl": null
    },
    "business": {
      "id": "NorthwindWellness@northwind.example",
      "name": "Northwind Wellness"
    },
    "widget": {
      "id": "w_8fk2m1qz",
      "name": "Physio page"
    },
    "attribution": {
      "source": "google",
      "medium": "cpc",
      "campaign": "spring-physio",
      "channel": "Paid search",
      "landingPage": "/physio?area=knee&utm_source=google&utm_medium=cpc&utm_campaign=spring-physio&utm_term=sports+physio+near+me&gclid=Cj0KCQjw8p2Z",
      "referrer": "https://www.google.com/",
      "pageUrl": "https://northwind.example/book",
      "utm": {
        "source": "google",
        "medium": "cpc",
        "campaign": "spring-physio",
        "term": "sports physio near me"
      },
      "clickIds": {
        "gclid": "Cj0KCQjw8p2Z"
      },
      "gaClientId": "1843021957.1760190322"
    }
  }
}

Request headers

HTTP
POST /your-endpoint HTTP/1.1
Content-Type: application/json
User-Agent: BookingsXP-Webhooks/1.0 (+https://bookingsxp.com/docs/webhooks)
BookingsXP-Event: booking.created
BookingsXP-Delivery: 5b0e1c7a-…
BookingsXP-Signature: t=1790694134,v1=5f2c8e…

Verify the signature (Node)

The signature is the hex HMAC-SHA256 of <t>.<raw body> with your endpoint's whsec_… secret. Reject timestamps more than five minutes old.

JavaScript
import crypto from "node:crypto";
import express from "express";

// The endpoint's signing secret (whsec_…), shown once when you add it.
const SECRET = process.env.BOOKINGSXP_WEBHOOK_SECRET;

function verify(rawBody, header, toleranceSec = 300) {
  const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto
    .createHmac("sha256", SECRET)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

// Sign-checking needs the raw body, exactly as it was sent.
app.post("/bookingsxp", express.raw({ type: "application/json" }), (req, res) => {
  const body = req.body.toString("utf8");
  if (!verify(body, req.get("BookingsXP-Signature") ?? "")) return res.sendStatus(400);

  const event = JSON.parse(body);
  if (event.type === "booking.created") {
    const { booking, attribution } = event.data;
    // booking.customer.email, booking.service.name, attribution.campaign …
  }
  res.sendStatus(200);
});

Teams and Slack

An alert in the channel, not another inbox

Add a Microsoft Teams or Slack endpoint and each booking arrives as a formatted card: who booked, what, when, with whom, and the source and campaign. Teams uses the Workflows app's webhook; Slack uses an incoming webhook.

Microsoft Teams, as an Adaptive Card

Teams · #front-deskIllustrative

Workflows11:02 AM

New booking: Alex Rivera · Initial assessment · Wed, Oct 14, 10:15 AM EDT

Service
Initial assessment
When
Wed, Oct 14, 10:15 AM EDT
With
Dr. Maya Chen
Source
google / cpc · spring-physio
Customer
Alex Rivera (alex.rivera@example.com)
Reference
BXP-7K3M9Q

via Physio page · Northwind Wellness

Slack

Slack · #bookingsIllustrative

BookingsXPApp11:02 AM

New booking: Alex Rivera · Initial assessment · Wed, Oct 14, 10:15 AM EDT

Service
Initial assessment
When
Wed, Oct 14, 10:15 AM EDT
With
Dr. Maya Chen
Source
google / cpc · spring-physio
Customer
Alex Rivera alex.rivera@example.com
Ref
BXP-7K3M9Q

via Physio page · Northwind Wellness

Delivery

Retries you can see

If an endpoint fails, BookingsXP tries again up to 8 times with exponential backoff, from 30 seconds up to about an hour apart, over roughly two hours. Every attempt is logged with its status code and duration, and you can redeliver by hand.
Recent deliveriesIllustrative
booking.created for BXP-7K3M9Q, sent to four endpoints
TimeEndpointResultAttemptDuration
11:02:14Zapier: new leadsWebhookHTTP 2001312 ms
11:02:14#front-deskTeamsHTTP 2001486 ms
11:02:14#bookingsSlackHTTP 2001205 ms
11:02:15CRM intakeWebhookHTTP 500Retrying in 30 s11,204 ms
11:02:45CRM intakeWebhookHTTP 200Delivered on retry2388 ms

A webhook problem never fails a booking. Microsoft Bookings has already confirmed it by the time the webhook is queued. Delivery logs are kept for 30 days.

REST API · Business

Pull bookings and analytics on your schedule

On Business, create API keys in the dashboard and read your widgets, stored bookings and analytics from your own systems. Details in the REST API docs.
GET /api/v1/widgets
Your widgets: ID, name, Bookings link, template, status, whether bookings are stored.
GET /api/v1/bookings
Stored bookings, newest first, with customer, answers and attribution. Filter by since, until and widget; page with cursor.
GET /api/v1/analytics
Totals, daily series, funnel, channels, sources and campaigns for up to 730 days.
Shell
curl "https://bookingsxp.com/api/v1/bookings?since=2026-09-01T00:00:00Z&limit=50" \
  -H "Authorization: Bearer bxp_live_…"
Response (abridged)
{
  "data": [
    {
      "reference": "BXP-7K3M9Q",
      "status": "confirmed",
      "createdAt": "2026-09-29T15:02:14.318Z",
      "start": "2026-10-14T14:15:00.000Z",
      "service": { "id": "8b3c2f4e-…", "name": "Initial assessment" },
      "customer": { "name": "Alex Rivera", "email": "alex.rivera@example.com", … },
      "attribution": { "source": "google", "medium": "cpc", "campaign": "spring-physio", … }
    }
  ],
  "nextCursor": null
}

FAQ

Questions about webhooks and data

Bookings, where you work

Start free, keep Microsoft Bookings as it is, and switch on Store bookings for the widgets you want to automate.