BookMe API Documentation
The BookMe API provides comprehensive access to meeting booking and scheduling functionality. This documentation covers API Version 2.0.0 (current). For V1 documentation, see the V1 reference.
Base URL
Production: https://apim-public-api.azure-api.net/api/v2/bookme
Test: https://apim-public-api-test.azure-api.net/api/v2/bookme
Authentication
All API requests require OAuth2 authentication using Azure AD:
Authorization: Bearer <access_token>
Ocp-Apim-Subscription-Key: <subscription_key>
Endpoints
Meetings
List Meetings
Retrieve a list of meetings based on filter criteria.
GET /meetings
Query Parameters:
timeSlotId(string, optional): Filter by time slot IDemployeeId(uuid, optional): Filter by employee IDstartDateModified(datetime, optional): Filter by modified start dateendDateModified(datetime, optional): Filter by modified end date
Response: Array of Meeting objects
Get Meeting
Retrieve a specific meeting by ID.
GET /meetings/{id}
Response: Meeting object
Create Meeting
Create a new meeting booking.
POST /meetings
Request Body:
{
"bookedBy": "Employee|Customer|PortalPartner|PortalCustomer",
"salesforceId": "string", // Required in V2
"customerTypeId": "uuid", // Optional in V2
"timeSlotId": "uuid", // Optional in V2
"portalId": "uuid", // V2 only
"cpr": "string", // V2 only
"customFields": [ // V2 only
{
"key": "string",
"value": "string"
}
],
"externalAttendees": [ // V2 only
{
"name": "string",
"email": "string"
}
],
"title": "string",
"description": "string",
"videoLink": "string", // V2 only
"additionalEmployees": ["uuid"],
"roomId": "uuid",
"type": "Physical|Online|Telephone|Offsite",
"topicId": "uuid", // Optional in V2
"employeeId": "uuid", // Required
"customerId": "string" // Required
}
Note that the fields related to portal meetings “portalId, customFields and externalAttendees” are only used during portal meeting bookings and are not used during booking of a normal BookMe meeting.
Response: Created Meeting object with ID
Update Meeting
Update an existing meeting.
PUT /meetings/{id}
Request Body: Meeting object
Response: Updated Meeting object
Delete Meeting
Cancel a meeting booking.
DELETE /meetings/{id}
Response: 204 No Content
Generate iCal (V2 Only)
Generate an iCal file for a meeting.
POST /meetings/{id}/ical
Request Body:
{
"title": "string",
"description": "string",
"location": "string"
}
Response: iCal file content
Time Slots
List Time Slots
Retrieve available time slots.
GET /time-slots
Query Parameters:
employeeId(uuid, optional): Filter by employeeroomId(uuid, optional): Filter by roomstartDate(datetime): Start of date rangeendDate(datetime): End of date rangestatus(array, optional): Filter by status
Response: Array of TimeSlot objects
List Available Time Slots
Get available time slots for booking with advanced filtering.
GET /time-slots/available
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
explicitEmployeeIds |
array of uuid | No | Specific employee IDs. Pass empty array or omit for all employees |
startDate |
datetime | No | Start date for time slot search (ISO 8601 format) |
lookForwardTime |
string | No | Duration to search ahead (format: "days.hours:minutes:seconds", e.g., "7.00:00:00" for 7 days) |
topic |
uuid | No | Meeting topic/theme ID |
customerTypeId |
uuid | No | Customer type/category ID |
employeeTypes |
array | No | V2 only: Employee selection types. At least one value required. Only relevant when requireEmployeeParticipation is false (see Employee Types) |
meetingTypes |
array | No | Types of meetings: ["physical", "online", "telephone", "offsite"] |
customerLocation |
string | No | Customer’s location (free text, used to filter local employees) |
meetingLocation |
string | No | Meeting location (free text, defaults to customerLocation if omitted) |
requireRoom |
boolean | No | If true, only return slots with available rooms |
requireEmployeeParticipation |
boolean | Yes | If true, requires all explicitly specified employees are available for the entire meeting duration. If false, returns slots where at least one specified employee is available |
Response: Array of available TimeSlot objects
employeeTypes: The employeeTypes parameter is only relevant when requireEmployeeParticipation is set to false. At least one value must be provided.
This parameter controls which pools of employees to search. Multiple types can be combined:
Explicit: Use only employees specified inexplicitEmployeeIdsLocal: Include employees qualified to serve the customer’s locationServiceGroup: Include employees from configured service groups
Note:
- When
requireEmployeeParticipationistrue, all explicitly specified employees must be available - When
requireEmployeeParticipationisfalse, at least one employee from the specified types must be available - Employee types are combined (union), not mutually exclusive
How Time Slots Are Generated
Available time slots are generated automatically based on employee availability and configuration:
- Interval: Time slots start at 30-minute intervals (
:00and:30minutes) - Duration: Default meeting duration is typically 1 hour (configurable via
meetingDurationparameter) - Multiple slots: For each available employee, slots are generated for each available meeting type they support
Example response:
[
{
"startDate": "2025-10-10T08:00:00.000Z",
"endDate": "2025-10-10T09:00:00.000Z",
"meetingType": "Physical",
"employeeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
{
"startDate": "2025-10-10T08:30:00.000Z",
"endDate": "2025-10-10T09:30:00.000Z",
"meetingType": "Physical",
"employeeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
},
{
"startDate": "2025-10-10T09:00:00.000Z",
"endDate": "2025-10-10T10:00:00.000Z",
"meetingType": "Online",
"employeeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
]
This means customers can book meetings starting at any half-hour mark within the employee’s available working hours.
Reserve Time Slot
Reserve a time slot for booking. Reservations expire after 5 minutes if not converted to a meeting.
POST /time-slots/reserve
Request Body:
{
"timeSlot": {
"startDate": "datetime",
"endDate": "datetime",
"status": "string",
"roomId": "uuid",
"employeeId": "uuid",
"serviceGroup": "string"
},
"token": "uuid",
"existingMeetingId": "uuid" // Optional
}
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
timeSlot |
object | Yes | Time slot details to reserve |
timeSlot.startDate |
datetime | Yes | Start time (ISO 8601) |
timeSlot.endDate |
datetime | Yes | End time (ISO 8601) |
timeSlot.status |
string | Yes | Should be "Reserved" |
timeSlot.employeeId |
uuid | No | Employee/advisor ID |
timeSlot.roomId |
uuid | No | Room ID if room is required |
timeSlot.serviceGroup |
string | No | Service group identifier |
token |
uuid | Yes | Unique reservation token (see Token Behavior) |
existingMeetingId |
uuid | No | Meeting ID when modifying an existing booking |
Token Behavior
The token parameter is crucial for managing reservations:
Same Token = Automatic Replacement: When you reserve a new time slot with the same token as a previous reservation, the system automatically:
- Deletes the old reservation immediately
- Creates the new reservation
- Ensures only one active reservation per token
Different Token = Separate Reservations: Using a different token creates a separate reservation. The old reservation will remain until it expires (5 minutes) or is explicitly replaced.
Response: Reserved TimeSlot object with confirmation
Rooms
List All Rooms
Retrieve all available meeting rooms.
GET /rooms
Response: Array of Room objects
Get Room Details
Get details for a specific room.
GET /rooms/{id}
Response: Room object
Check Room Vacancies
Check room availability for a specific time slot.
GET /room-vacancies
Query Parameters:
location(string, required): Room locationtimeSlotId(uuid, required): Time slot ID
Response: Array of available rooms
Employees
List All Employees
Retrieve all employees.
GET /employees
Response: Array of Employee objects
Get Employee Details
Get details for a specific employee.
GET /employees/{id}
Response: Employee object
Topics
List Topics
Retrieve all meeting topics.
GET /meeting-topics
Query Parameters:
customerTypeId(uuid, optional): Filter by customer typeisCustomer(boolean, required): Flag indicating if the request is for a customer
Response: Array of Topic objects
Configuration API
List Customer Types
Retrieve all customer types.
GET /config/customer-types
Response: Array of CustomerType objects
Get Customer Type
Get details for a specific customer type.
GET /config/customer-types/{id}
Response: CustomerType object
Create Customer Type
Create a new customer type.
POST /config/customer-types
Request Body: CustomerType object
Response: Created CustomerType object
Update Customer Type
Update an existing customer type.
PUT /config/customer-types/{id}
Request Body: CustomerType object
Response: Updated CustomerType object
Delete Customer Type
Delete a customer type.
DELETE /config/customer-types/{id}
Response: 204 No Content
Data Models
Meeting Object
{
"id": "string",
"salesforceId": "string",
"bookedBy": {},
"customerTypeId": "string",
"timeSlotId": "string",
"dateCreated": "2025-08-20T10:00:00Z",
"dateModified": "2025-08-20T10:00:00Z",
"title": "string",
"description": "string",
"videoLink": "string",
"additionalEmployees": ["string"],
"roomId": "string",
"startDate": "2025-08-20T10:00:00Z",
"endDate": "2025-08-20T11:00:00Z",
"type": {},
"topicId": "string",
"employeeId": "uuid",
"customerId": "string"
}
TimeSlot Object
{
"id": "string",
"employeeId": "uuid",
"startTime": "2025-08-20T10:00:00Z",
"endTime": "2025-08-20T11:00:00Z",
"available": true,
"roomId": "string"
}
Room Object
{
"id": "string",
"name": "string",
"capacity": 10,
"location": "string",
"features": ["string"],
"available": true
}
Code Examples
JavaScript/Node.js
const axios = require('axios');
const ACCESS_TOKEN = 'your-access-token';
const SUBSCRIPTION_KEY = 'your-subscription-key';
const BASE_URL = 'https://apim-public-api.azure-api.net/api/v2/bookme';
// List meetings
async function listMeetings() {
try {
const response = await axios.get(`${BASE_URL}/meetings`, {
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
}
});
return response.data;
} catch (error) {
console.error('Error fetching meetings:', error);
}
}
// Create a meeting with V2 features
async function createMeeting(meetingData) {
try {
const payload = {
bookedBy: "Customer",
salesforceId: meetingData.salesforceId, // Required in V2
customerTypeId: meetingData.customerTypeId || null, // Optional in V2
timeSlotId: meetingData.timeSlotId,
employeeId: meetingData.employeeId,
customerId: meetingData.customerId,
type: "Physical",
// V2 new features
portalId: meetingData.portalId || null,
customFields: meetingData.customFields || [],
externalAttendees: meetingData.externalAttendees || [],
videoLink: meetingData.videoLink || null,
...meetingData
};
const response = await axios.post(`${BASE_URL}/meetings`, payload, {
headers: {
'Authorization': `Bearer ${ACCESS_TOKEN}`,
'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
console.error('Error creating meeting:', error);
}
}
C#/.NET
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
public class BookMeApiClient
{
private readonly HttpClient _httpClient;
private const string AccessToken = "your-access-token";
private const string SubscriptionKey = "your-subscription-key";
private const string BaseUrl = "https://apim-public-api.azure-api.net/api/v2/bookme";
public BookMeApiClient()
{
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {AccessToken}");
_httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", SubscriptionKey);
}
public async Task<List<Meeting>> GetMeetingsAsync()
{
var response = await _httpClient.GetAsync($"{BaseUrl}/meetings");
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<List<Meeting>>(json);
}
public async Task<Meeting> CreateMeetingAsync(CreateMeetingRequest meetingRequest)
{
var json = JsonConvert.SerializeObject(meetingRequest);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await _httpClient.PostAsync($"{BaseUrl}/meetings", content);
response.EnsureSuccessStatusCode();
var resultJson = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<Meeting>(resultJson);
}
}
// V2 Meeting Request Model
public class CreateMeetingRequest
{
public string BookedBy { get; set; } // "Employee", "Customer", "PortalPartner", "PortalCustomer"
public string SalesforceId { get; set; } // Required in V2
public string CustomerTypeId { get; set; } // Optional in V2
public string TimeSlotId { get; set; }
public string PortalId { get; set; } // V2 only
public string Cpr { get; set; } // V2 only
public List<CustomField> CustomFields { get; set; } // V2 only
public List<ExternalAttendee> ExternalAttendees { get; set; } // V2 only
public string Title { get; set; }
public string Description { get; set; }
public string VideoLink { get; set; } // V2 only
public List<string> AdditionalEmployees { get; set; }
public string RoomId { get; set; }
public string Type { get; set; } // "Physical", "Online", "Telephone", "Offsite"
public string TopicId { get; set; } // Optional in V2
public string EmployeeId { get; set; }
public string CustomerId { get; set; }
}
public class CustomField
{
public string Key { get; set; }
public string Value { get; set; }
}
public class ExternalAttendee
{
public string Name { get; set; }
public string Email { get; set; }
}
cURL
# List meetings
curl -X GET "https://apim-public-api.azure-api.net/api/v2/bookme/meetings" \
-H "Authorization: Bearer your-access-token" \
-H "Ocp-Apim-Subscription-Key: your-subscription-key"
# Create a V2 meeting with new features
curl -X POST "https://apim-public-api.azure-api.net/api/v2/bookme/meetings" \
-H "Authorization: Bearer your-access-token" \
-H "Ocp-Apim-Subscription-Key: your-subscription-key" \
-H "Content-Type: application/json" \
-d '{
"bookedBy": "Customer",
"salesforceId": "sf-customer-123",
"timeSlotId": "timeslot-uuid",
"employeeId": "employee-uuid",
"customerId": "customer-id",
"title": "Customer Meeting",
"type": "Online",
"videoLink": "https://meet.example.com/room123",
"customFields": [
{
"key": "project_code",
"value": "PROJ-123"
}
],
"externalAttendees": [
{
"name": "John Smith",
"email": "john.smith@external.com"
}
]
}'
# Generate iCal for meeting (V2 only)
curl -X POST "https://apim-public-api.azure-api.net/api/v2/bookme/meetings/meeting-id/ical" \
-H "Authorization: Bearer your-access-token" \
-H "Ocp-Apim-Subscription-Key: your-subscription-key" \
-H "Content-Type: application/json" \
-d '{
"title": "Custom Meeting Title",
"description": "Meeting description for calendar",
"location": "Online via Teams"
}'
Full API Specification
For the complete API specification including all endpoints, parameters, and response schemas:
- V2 OpenAPI Specification (YAML) - Current version
- V1 OpenAPI Specification (YAML) - Active
What’s New in V2
Key Changes from V1
- Required Fields:
salesforceIdis now required, whilecustomerTypeId,topicId, andtimeSlotIdare now optional - Portal Integration: New
bookedBytypes:PortalPartnerandPortalCustomer - Enhanced Features: Custom fields, external attendees, video links, CPR field, and iCal generation
- Employee Types: New types
LocalandServiceGroupreplace deprecatedGlobaltype - Time Slot Status: New internal meeting statuses for better scheduling
- Portal meeting external attendees: External attendees can now optionally be added for portal meetings.
For detailed migration instructions, see our V1 to V2 Migration Guide.