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.

Off by default, on when you need it
| Data | Store bookings off (default) | Store bookings on |
|---|---|---|
| Funnel events | Anonymous: the step, time, page, source, medium, campaign, device, country and a random per-visit ID | The same anonymous events |
| Name, email and phone | Not stored. They go to Microsoft Bookings and nowhere else | Stored with the booking |
| Notes and answers to your questions | Not stored | Stored with the booking |
| Campaign detail | Source, medium, campaign and channel, with no link to a person | Also the full UTMs, click IDs, first page and referrer, attached to the booking |
| IP addresses | Never stored | Never stored |
| Bookings list, search and CSV | Not available | Available (CSV export on Pro and Business) |
| Webhooks, Teams and Slack alerts | Not available | Available on Pro and Business |
| How long it is kept | Events: 30 days on Free, 12 months on Pro, 24 months on Business | Bookings: 30 days on Free, 12 months on Pro, 24 months on Business |
Search, export and erase
- 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.
Signed JSON for anything that accepts a URL
booking.created payload
{
"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/BrightwaterClinic@brightwater.example/id/3f9a6c1e-72d4-4b8e-a015-c6e2d9b7f840?isAnonymous=true",
"joinUrl": null
},
"business": {
"id": "BrightwaterClinic@brightwater.example",
"name": "Brightwater Clinic"
},
"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://brightwater.example/book",
"utm": {
"source": "google",
"medium": "cpc",
"campaign": "spring-physio",
"term": "sports physio near me"
},
"clickIds": {
"gclid": "Cj0KCQjw8p2Z"
},
"gaClientId": "1843021957.1760190322"
}
}
}Request headers
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.
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);
});An alert in the channel, not another inbox
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 · Brightwater Clinic
BookingsXP11: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 · Brightwater Clinic
The text is what BookingsXP sends for a sample booking; Teams and Slack draw it in their own style. BookingsXP is independent and not affiliated with or endorsed by Microsoft or Slack.
Retries and a delivery log

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.
Connect it to the rest of your stack
- ZapierA “Catch Hook” trigger receives each booking; add the customer to your CRM with the campaign that brought them.
- MakeA custom webhook starts the scenario; route bookings by service or location to the right team, sheet or inbox.
- n8nA Webhook node receives the event; check the signature in a Code node, then write the booking to your own database.
- Power Automate“When an HTTP request is received” starts a flow; add a row to an Excel table in SharePoint or start an approval.
Pull bookings and analytics on your schedule
- 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.
curl "https://bookingsxp.com/api/v1/bookings?since=2026-09-01T00:00:00Z&limit=50" \
-H "Authorization: Bearer bxp_live_…"{
"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
}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.
No Microsoft sign-in or admin consent. Free plan, no card.