Microsoft Bookings
Microsoft Bookings integration via Microsoft Graph API for enterprise appointment scheduling and calendar management
You are an expert in integrating Microsoft Bookings via the Microsoft Graph API for enterprise scheduling and calendar management. ## Key Points - Always include `timeZone` with datetime values; Graph API datetime objects without timezone context default to UTC, which causes scheduling errors for end users. - Use delegated permissions (user-context tokens) for user-facing apps and application permissions (client credentials) for background services and daemons. - Leverage the native Teams integration by setting `isLocationOnline: true` — this auto-generates a Teams meeting link for each appointment. - Use `$select` query parameters to reduce payload size when listing appointments or services in high-volume scenarios. - Map staff members to Azure AD users so that appointments automatically appear on their Outlook calendars. - The Bookings API requires a Microsoft 365 Business Premium or higher license; Business Basic/Standard plans do not include API access. - `calendarView` requires both `start` and `end` query parameters and returns a flat list, not paginated by default — add `$top` for large date ranges. - Deleting a service that has future appointments will fail; cancel or reassign existing appointments first. - Token expiration is 60 minutes for Graph API tokens; implement token caching with refresh logic rather than acquiring a new token per request. - The `defaultDuration` field uses ISO 8601 duration format (`PT30M`, `PT1H`), not minutes as an integer — passing a number will cause a 400 error.
skilldb get scheduling-services-skills/Microsoft BookingsFull skill: 306 linesMicrosoft Bookings — Scheduling Integration
You are an expert in integrating Microsoft Bookings via the Microsoft Graph API for enterprise scheduling and calendar management.
Core Philosophy
Overview
Microsoft Bookings is an enterprise appointment scheduling service included in Microsoft 365 Business and Enterprise plans. It integrates natively with Outlook calendars, Teams meetings, and Azure AD. The Microsoft Graph API provides programmatic access to booking businesses, services, staff, customers, and appointments. Bookings is ideal when your organization already uses the Microsoft 365 ecosystem and needs scheduling that ties into existing identity, calendar, and video conferencing infrastructure.
Setup & Configuration
App Registration in Azure AD
# 1. Register an app at https://portal.azure.com > App registrations
# 2. Add the following API permissions:
# - Bookings.Read.All
# - Bookings.ReadWrite.All
# - Bookings.Manage.All (for creating booking businesses)
# - BookingsAppointment.ReadWrite.All
# 3. Set redirect URI and generate a client secret
Authentication with MSAL
import {
ConfidentialClientApplication,
Configuration,
} from "@azure/msal-node";
const msalConfig: Configuration = {
auth: {
clientId: process.env.AZURE_CLIENT_ID!,
authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
clientSecret: process.env.AZURE_CLIENT_SECRET!,
},
};
const msalClient = new ConfidentialClientApplication(msalConfig);
async function getAccessToken(): Promise<string> {
const result = await msalClient.acquireTokenByClientCredential({
scopes: ["https://graph.microsoft.com/.default"],
});
if (!result?.accessToken) {
throw new Error("Failed to acquire access token");
}
return result.accessToken;
}
Graph Client Setup
import axios, { AxiosInstance } from "axios";
async function createGraphClient(): Promise<AxiosInstance> {
const token = await getAccessToken();
return axios.create({
baseURL: "https://graph.microsoft.com/v1.0",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
});
}
Core Patterns
Managing Booking Businesses
interface BookingBusiness {
id: string;
displayName: string;
email: string;
phone: string;
businessType: string;
address: {
street: string;
city: string;
state: string;
countryOrRegion: string;
postalCode: string;
};
schedulingPolicy: {
timeSlotInterval: string; // ISO 8601 duration, e.g. "PT30M"
minimumLeadTime: string;
maximumAdvance: string;
allowStaffSelection: boolean;
};
}
async function listBookingBusinesses(): Promise<BookingBusiness[]> {
const client = await createGraphClient();
const { data } = await client.get("/solutions/bookingBusinesses");
return data.value;
}
async function getBookingBusiness(
businessId: string
): Promise<BookingBusiness> {
const client = await createGraphClient();
const { data } = await client.get(
`/solutions/bookingBusinesses/${businessId}`
);
return data;
}
Defining Services
interface BookingService {
id: string;
displayName: string;
description: string;
defaultDuration: string; // ISO 8601 duration
defaultPrice: number;
defaultPriceType: "undefined" | "fixedPrice" | "free";
isLocationOnline: boolean;
staffMemberIds: string[];
schedulingPolicy?: {
allowStaffSelection: boolean;
};
}
async function createService(
businessId: string,
service: Partial<BookingService>
): Promise<BookingService> {
const client = await createGraphClient();
const { data } = await client.post(
`/solutions/bookingBusinesses/${businessId}/services`,
{
displayName: service.displayName,
description: service.description,
defaultDuration: service.defaultDuration || "PT60M",
defaultPrice: service.defaultPrice || 0,
defaultPriceType: service.defaultPriceType || "free",
isLocationOnline: service.isLocationOnline ?? true,
}
);
return data;
}
Creating Appointments
interface BookingAppointment {
id: string;
serviceId: string;
serviceName: string;
startDateTime: {
dateTime: string;
timeZone: string;
};
endDateTime: {
dateTime: string;
timeZone: string;
};
customers: {
emailAddress: string;
name: string;
phone?: string;
}[];
staffMemberIds: string[];
isLocationOnline: boolean;
onlineMeetingUrl?: string;
}
async function createAppointment(
businessId: string,
serviceId: string,
customer: { name: string; email: string },
startDateTime: string,
timeZone: string
): Promise<BookingAppointment> {
const client = await createGraphClient();
const { data } = await client.post(
`/solutions/bookingBusinesses/${businessId}/appointments`,
{
serviceId,
startDateTime: { dateTime: startDateTime, timeZone },
customers: [
{
emailAddress: customer.email,
name: customer.name,
},
],
isLocationOnline: true,
}
);
return data;
}
Querying Availability (via Calendar View)
async function getCalendarView(
businessId: string,
startDate: string,
endDate: string
): Promise<BookingAppointment[]> {
const client = await createGraphClient();
const { data } = await client.get(
`/solutions/bookingBusinesses/${businessId}/calendarView`,
{
params: {
start: startDate,
end: endDate,
},
}
);
return data.value;
}
// Get staff availability by checking existing appointments
// and comparing against business hours
async function getStaffAvailability(
businessId: string,
staffId: string,
date: string
): Promise<{ start: string; end: string }[]> {
const client = await createGraphClient();
const { data } = await client.get(
`/solutions/bookingBusinesses/${businessId}/staffMembers/${staffId}`
);
const workingHours = data.workingHours;
const existingAppointments = await getCalendarView(
businessId,
`${date}T00:00:00`,
`${date}T23:59:59`
);
// Filter to this staff member's appointments and compute open slots
const staffAppointments = existingAppointments.filter((appt) =>
appt.staffMemberIds.includes(staffId)
);
// Return free slots (implementation depends on your slot logic)
return computeFreeSlots(workingHours, staffAppointments, date);
}
Canceling Appointments
async function cancelAppointment(
businessId: string,
appointmentId: string,
cancellationMessage?: string
): Promise<void> {
const client = await createGraphClient();
await client.post(
`/solutions/bookingBusinesses/${businessId}/appointments/${appointmentId}/cancel`,
{
cancellationMessage:
cancellationMessage || "This appointment has been canceled.",
}
);
}
Best Practices
- Always include
timeZonewith datetime values; Graph API datetime objects without timezone context default to UTC, which causes scheduling errors for end users. - Use delegated permissions (user-context tokens) for user-facing apps and application permissions (client credentials) for background services and daemons.
- Leverage the native Teams integration by setting
isLocationOnline: true— this auto-generates a Teams meeting link for each appointment. - Use
$selectquery parameters to reduce payload size when listing appointments or services in high-volume scenarios. - Map staff members to Azure AD users so that appointments automatically appear on their Outlook calendars.
Common Pitfalls
- The Bookings API requires a Microsoft 365 Business Premium or higher license; Business Basic/Standard plans do not include API access.
calendarViewrequires bothstartandendquery parameters and returns a flat list, not paginated by default — add$topfor large date ranges.- Deleting a service that has future appointments will fail; cancel or reassign existing appointments first.
- Token expiration is 60 minutes for Graph API tokens; implement token caching with refresh logic rather than acquiring a new token per request.
- The
defaultDurationfield uses ISO 8601 duration format (PT30M,PT1H), not minutes as an integer — passing a number will cause a 400 error.
Anti-Patterns
Using the service without understanding its pricing model. Cloud services bill differently — per request, per GB, per seat. Deploying without modeling expected costs leads to surprise invoices.
Hardcoding configuration instead of using environment variables. API keys, endpoints, and feature flags change between environments. Hardcoded values break deployments and leak secrets.
Ignoring the service's rate limits and quotas. Every external API has throughput limits. Failing to implement backoff, queuing, or caching results in dropped requests under load.
Treating the service as always available. External services go down. Without circuit breakers, fallbacks, or graceful degradation, a third-party outage becomes your outage.
Coupling your architecture to a single provider's API. Building directly against provider-specific interfaces makes migration painful. Wrap external services in thin adapter layers.
Install this skill directly: skilldb add scheduling-services-skills
Related Skills
Acuity Scheduling
Acuity Scheduling API integration for appointment booking, availability management, and calendar sync
Cal.com
"Cal.com: open-source scheduling, booking API, event types, availability, webhooks, embeds, self-hosted"
Calendly
"Calendly API: scheduling links, event types, invitees, webhooks, organization management, OAuth, REST API"
Cronofy
"Cronofy: calendar API, availability, scheduling, real-time sync, conferencing, UI elements, enterprise calendar integration"
Doodle
Doodle API integration for group scheduling polls, 1:1 booking pages, and meeting coordination
Nylas
"Nylas: unified calendar/email/contacts API, scheduling, calendar CRUD, email send/read, OAuth providers, Node SDK"