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
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
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
| Data | Store bookings offDefault | 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 |
Bookings list
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.
Webhooks
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/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
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);
});Teams and Slack
An alert in the channel, not another inbox
Microsoft Teams, as an Adaptive Card
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
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
| Time | Endpoint | Result | Attempt | Duration |
|---|---|---|---|---|
| 11:02:14 | Zapier: new leadsWebhook | HTTP 200 | 1 | 312 ms |
| 11:02:14 | #front-deskTeams | HTTP 200 | 1 | 486 ms |
| 11:02:14 | #bookingsSlack | HTTP 200 | 1 | 205 ms |
| 11:02:15 | CRM intakeWebhook | HTTP 500Retrying in 30 s | 1 | 1,204 ms |
| 11:02:45 | CRM intakeWebhook | HTTP 200Delivered on retry | 2 | 388 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.
Automation recipes
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.
REST API · Business
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
}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.