Alerts & automation
Microsoft Bookings webhooks
Microsoft Bookings has no webhooks. To get a new appointment into a CRM you would normally poll Microsoft Graph or parse notification emails. BookingsXP sends a signed JSON request to your endpoint the moment a widget booking is confirmed, including the marketing attribution Microsoft never stores.
- Plan
- Plan: ProNeeds Pro ($19 a month) or Business ($49 a month).
- In short
- Signed JSON (HMAC-SHA256) to any HTTPS endpoint when a booking is created, with retries and a delivery log.
- Microsoft Bookings
- Stays your system of record. Bookings land in Outlook and Teams and Microsoft sends the invites and reminders.
How it works
What happens with Webhooks
- Each delivery is an HTTPS POST with Content-Type application/json and a User-Agent of BookingsXP-Webhooks/1.0.
- The body is an envelope: id (evt_…), type (booking.created or booking.failed), created, apiVersion (2026-09-01) and data with booking, business, widget and attribution.
- The BookingsXP-Signature header is t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" keyed with your endpoint's whsec_… secret. BookingsXP-Event and BookingsXP-Delivery headers carry the event type and delivery ID.
- A 2xx response within 10 seconds counts as delivered. Redirects are not followed. Anything else is retried with exponential backoff, about 30 s, 1, 2, 4, 8, 16, 32 and 64 minutes: 8 retries over roughly two hours.
- booking.failed is sent when Microsoft rejects a booking for an upstream reason, not when a slot was simply taken or a field was invalid.
Setup
Set up Webhooks
Step 1: Turn on Store bookings
Dashboard → widget → Data & access → "Store bookings in BookingsXP". Webhooks carry customer data, so they only fire for widgets that keep a copy.
Step 2: Add a Webhook (JSON) endpoint
Dashboard → Integrations → Add an endpoint → Webhook (JSON). Enter a name and your HTTPS URL, and choose All widgets or one widget. Copy the signing secret shown after saving: it is displayed once.
Step 3: Verify the signature on your server
Compute the HMAC over the raw request body, before any JSON parsing, and compare in constant time. Reject timestamps more than five minutes old.
JavaScript // Node.js: verify the BookingsXP-Signature header import crypto from "node:crypto"; export function verify(rawBody, header, secret) { // header looks like: t=1759312443,v1=5f2c… const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("="))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5-minute replay window const mac = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex"); return mac.length === v1.length && crypto.timingSafeEqual(Buffer.from(mac), Buffer.from(v1)); }Step 4: Handle the payload idempotently
Retries resend the same envelope with the same id. Store the id (or data.booking.reference) and ignore repeats. Respond 2xx quickly and do slow work in the background.
booking.created { "id": "evt_4c1f0d9a7b2e4f6a8c3d5e7f9a1b2c3d", "type": "booking.created", "created": "2026-10-01T09:14:03.512Z", "apiVersion": "2026-09-01", "data": { "booking": { "reference": "BXP-7K2M9Q", "id": "AAMkAGI2…", "status": "confirmed", "start": "2026-10-02T08:30:00.000Z", "end": "2026-10-02T09:15:00.000Z", "timeZone": "Europe/London", "service": { "id": "a1b2…", "name": "Initial consultation" }, "staff": [{ "id": "c3d4…", "name": "Front desk" }], "customer": { "name": "Sam Example", "email": "sam@example.com", "phone": null, "notes": null }, "answers": [ { "questionId": "q1…", "question": "Company size", "answer": "11-50" } ], "manageUrl": "https://outlook.office.com/book/…", "joinUrl": null }, "business": { "id": "b5e6…", "name": "Example Consulting" }, "widget": { "id": "w_8fk2m1qz", "name": "Homepage" }, "attribution": { "source": "google", "medium": "cpc", "campaign": "autumn-audit", "channel": "Paid search", "landingPage": "/audit?utm_source=google&utm_medium=cpc&utm_campaign=autumn-audit", "referrer": "https://www.google.com/", "pageUrl": "https://www.example.com/book", "utm": { "source": "google", "medium": "cpc", "campaign": "autumn-audit" }, "clickIds": { "gclid": "Cj0KCQjw…" }, "gaClientId": "1234567890.1759312443" } } }Step 5: Test, then watch the delivery log
The Test button sends a ping event ({ "message": "Test notification from BookingsXP" }) so you can check connectivity and signatures. Recent deliveries show status, HTTP code and attempts, with Retry for failures.
Outcomes and caveats
What you get
The result
- A push the moment a booking is confirmed, instead of polling Microsoft Graph.
- Customer, answers to your Bookings questions, service, staff, time and time zone in one payload.
- Source, medium, campaign, channel, UTM values, click IDs (gclid, fbclid, msclkid, li_fat_id and more) and the GA client ID.
- Signed requests, automatic retries for about two hours, and a delivery log with manual redelivery.
Good to know
Limits and caveats
- Pro: 5 endpoints across webhooks, Slack and Teams. Business: 25, plus the REST API for reading bookings and analytics.
- Requires "Store bookings" on the widget.
- Only bookings made through a BookingsXP widget produce events. Bookings made elsewhere in Microsoft Bookings, and later cancellations or reschedules, do not.
- Endpoints must be public HTTPS or HTTP URLs; private and local network addresses are refused.
- Endpoints added from the dashboard subscribe to booking.created.
FAQ
Webhooks questions
Start free, no card
Start on Free with the widget and GA4 events, then move to Pro when you want ad conversions, webhooks and chat alerts.