BookMe API Documentation
The BookMe API provides comprehensive access to meeting booking and scheduling functionality. This documentation covers API Version 3.0.0 (latest) and includes V2 features. For migration from V2 to V3, see the V2 to V3 Migration Guide.
Base URL
Production (V3): https://apim-public-api-prod.azure-api.net/api/v3/bookme
Production (V2): https://apim-public-api-prod.azure-api.net/api/v2/bookme
Test (V3): https://apim-public-api-test.azure-api.net/api/v3/bookme
Test (V2): 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 using JSON Patch operations.
PATCH /meetings/{id}
Request Body: Array of JSON Patch operations
[
{ "op": "replace", "path": "/title", "value": "Updated Meeting Title" },
{ "op": "replace", "path": "/description", "value": "Updated description" }
]
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 |
isCustomerInitiated |
boolean | No | If true, enforces customer-specific booking constraints such as minimum booking notice and working time restrictions. Defaults to false if omitted. |
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
Employee Types (V2)
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
- Daily capacity limits: Time slots are filtered based on each advisor’s remaining daily meeting capacity (see below)
Daily Meeting Time Limits
The system enforces duration-based daily meeting time limits to prevent advisor overbooking:
How It Works:
- Each advisor has a
MaxMeetingTimePerDaylimit (configured at organization, service group, or advisor level) - The system calculates total booked meeting time per advisor per day
- Advisors are excluded from time slot results when:
existingMeetingDuration + requestedMeetingDuration > dailyLimit - If the requested meeting duration alone exceeds the daily limit, the advisor is excluded for all days in the search range
Example Scenarios:
// Scenario 1: Normal booking
// Advisor limit: 04:00:00 (4 hours/day)
// Already booked: 2 hours
// Requested meeting: 1 hour
// Result: ✅ Advisor appears in time slots (2h + 1h = 3h < 4h limit)
// Scenario 2: Near capacity
// Advisor limit: 04:00:00 (4 hours/day)
// Already booked: 3.5 hours
// Requested meeting: 1 hour
// Result: ❌ Advisor excluded (3.5h + 1h = 4.5h > 4h limit)
// Scenario 3: Meeting too long
// Advisor limit: 04:00:00 (4 hours/day)
// Already booked: 0 hours
// Requested meeting: 5 hours
// Result: ❌ Advisor excluded for all days (5h > 4h limit)
API Behavior:
- Advisors at capacity are automatically filtered out
- No explicit error is returned - they simply don’t appear in results
- Limits reset at midnight (per day calculation)
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
Labels API (V3)
The Labels API provides centralized management for labels used to categorize resources across the platform.
List Labels
GET /config/labels
Query Parameters:
pageNumber(int, optional): Page number (0-indexed, default: 0)pageSize(int, optional): Items per page (default: 20)activeFilter(string, optional): Filter by status:All,Active, orInactive
Response: Paginated list of LabelRegistration objects
Get Label
GET /config/labels/{id}
Response: LabelRegistration object
Create Label
POST /config/labels
Request Body:
{
"labelName": "Premium",
"color": "#FFD700",
"description": "Premium tier services"
}
Response: 201 Created with LabelRegistration object
Update Label
PATCH /config/labels/{id}
Request Body:
{
"color": "#FF5733",
"description": "Updated description"
}
Response: Updated LabelRegistration object
Delete Label
DELETE /config/labels/{id}
Note: Labels can only be deleted when they have zero usages.
Response: 204 No Content
Search Labels
Fuzzy search labels by name.
GET /config/labels/search
Query Parameters:
q(string): Search querythreshold(float, optional): Similarity threshold 0.0-1.0 (default: 0.3)limit(int, optional): Maximum results (default: 20)activeFilter(string, optional): Filter by status
Response: Array of LabelSearchResult objects with similarity scores
Label Suggestions
Get autocomplete suggestions.
GET /config/labels/suggest
Query Parameters:
prefix(string): Prefix to search forlimit(int, optional): Maximum suggestions (default: 10)
Response: Array of LabelSuggestion objects
Competence Groups API (V3)
Competence Groups define expertise areas and associate employees, topics, and customer types.
List Competence Groups
GET /bookme/config/competence-groups
Query Parameters:
labels(array, optional): Filter by labels
Response: Array of CompetenceGroup objects
Get Competence Group
GET /bookme/config/competence-groups/{id}
Response: CompetenceGroup object
Create Competence Group
POST /bookme/config/competence-groups
Request Body:
{
"name": "Mortgage Specialists",
"employeeIds": ["uuid1", "uuid2"],
"customerTypeIds": ["uuid3"],
"topicIds": ["uuid4"],
"labels": ["Finance", "Premium"]
}
Response: 201 Created with CompetenceGroup object
Update Competence Group
PATCH /bookme/config/competence-groups/{id}
Request Body: Array of JSON Patch operations
[
{ "op": "replace", "path": "/name", "value": "Updated Name" },
{ "op": "replace", "path": "/labels", "value": ["Finance", "Updated"] }
]
Response: 200 OK
Delete Competence Group
DELETE /bookme/config/competence-groups/{id}
Response: 204 No Content
Get Employee’s Competence Groups
Get all competence groups that an employee belongs to.
GET /bookme/config/competence-groups/employee/{employeeId}
Response: Array of EmployeeCompetenceGroup objects
[
{
"id": "uuid",
"name": "Mortgage Specialists",
"dateAssigned": "2025-01-15T10:30:00Z"
}
]
Add Employee to Competence Groups
Add an employee to one or more competence groups.
PATCH /bookme/config/competence-groups/employee/{employeeId}/add
Request Body: Array of competence group IDs
["uuid1", "uuid2", "uuid3"]
Response: 200 OK
Remove Employee from Competence Groups
Remove an employee from one or more competence groups.
PATCH /bookme/config/competence-groups/employee/{employeeId}/remove
Request Body: Array of competence group IDs
["uuid1", "uuid2"]
Response: 200 OK
Service Groups API (V3)
Service Groups organize employees into logical groups for scheduling.
List Service Groups
GET /bookme/config/service-groups
Query Parameters:
labels(array, optional): Filter by labels
Response: Array of ServiceGroup objects
Get Service Group
GET /bookme/config/service-groups/{id}
Response: ServiceGroup object
Create Service Group
POST /bookme/config/service-groups
Request Body:
{
"name": "Customer Service Team",
"membershipEmail": "team@example.com",
"employeeIds": ["uuid1", "uuid2"],
"competenceGroupIds": ["uuid3"],
"labels": ["Support"]
}
Response: 201 Created with ServiceGroup object
Update Service Group
PATCH /bookme/config/service-groups/{id}
Request Body: Array of JSON Patch operations
Response: 200 OK
Delete Service Group
DELETE /bookme/config/service-groups/{id}
Response: 204 No Content
Get Service Group Advisors
Get advisor membership showing advisors from different sources.
GET /bookme/config/service-groups/{id}/advisors
Response:
{
"skillBased": ["uuid1", "uuid2"],
"assigned": ["uuid3"],
"competenceGroupAdvisors": ["uuid4", "uuid5"]
}
Update Service Trigger
Update service trigger configuration for a service group.
PATCH /bookme/config/service-groups/{id}/service-trigger
Request Body: Array of JSON Patch operations
Response: 200 OK
Update Service Level
Update service level configuration for a service group.
PATCH /bookme/config/service-groups/{id}/service-level
Request Body: Array of JSON Patch operations
Response: 200 OK
Get Employee’s Service Groups
Get all service groups that an employee belongs to.
GET /bookme/config/service-groups/employee/{employeeId}
Response: Array of EmployeeServiceGroup objects
[
{
"id": "uuid",
"name": "Customer Service Team",
"membershipEmail": "team@example.com",
"dateAssigned": "2025-01-15T10:30:00Z"
}
]
Add Employee to Service Groups
Add an employee to one or more service groups.
PATCH /bookme/config/service-groups/employee/{employeeId}/add
Request Body: Array of service group IDs
["uuid1", "uuid2", "uuid3"]
Response: 200 OK
Remove Employee from Service Groups
Remove an employee from one or more service groups.
PATCH /bookme/config/service-groups/employee/{employeeId}/remove
Request Body: Array of service group IDs
["uuid1", "uuid2"]
Response: 200 OK
Portals API (V3)
Portals represent booking interfaces that can be customized with fields, styling, and authentication.
List Portals
GET /bookme/config/portals
Query Parameters:
includeHiddenFields(boolean, optional): Include hidden form fieldslabels(array, optional): Filter by labels
Response: Array of Portal objects
Get Portal
GET /bookme/config/portals/{id}
Query Parameters:
includeHiddenFields(boolean, optional): Include hidden form fields
Response: Portal object
Create Portal
POST /bookme/config/portals
Request Body:
{
"name": "Customer Booking Portal",
"location": "Copenhagen",
"authenticationType": "AzureAD",
"fields": [
{
"name": "phone",
"label": "Phone Number",
"type": "Phone",
"validation": {
"required": true
}
}
],
"logoUrl": "https://example.com/logo.png",
"styling": "/* Custom CSS */",
"customerTypeId": "uuid",
"topicId": "uuid",
"icalEnabled": true,
"labels": ["Public"]
}
Response: 201 Created with Portal object
Update Portal
PATCH /bookme/config/portals/{id}
Request Body: Array of JSON Patch operations
Response: 200 OK
Delete Portal
DELETE /bookme/config/portals/{id}
Response: 204 No Content
Employee Schedules API (V3)
Employee Schedules define working hours, availability, and meeting time limits for individual employees.
List Employee Schedules
GET /bookme/config/employee-schedules
Response: Array of EmployeeSchedule objects
Create Employee Schedule
POST /bookme/config/employee-schedules
Request Body:
{
"employeeId": "uuid",
"isOnlyExplicitlyAvailable": false,
"canBeBooked": true,
"workdays": [
{
"isAvailable": true,
"dayOfWeek": "Monday",
"from": "08:00:00",
"to": "17:00:00",
"availableMeetingTypes": [
{ "type": "Physical" },
{ "type": "Online" }
],
"location": "Copenhagen"
}
],
"maxMeetingTimePerDay": "04:00:00"
}
Response: 201 Created with EmployeeSchedule object
Warning: This endpoint is not intended for direct consumption. Employee schedules are automatically configured for employees and using this endpoint is advised against. Requires elevated permissions.
Get Employee Schedule
GET /bookme/config/employee-schedules/{id}
Response: EmployeeSchedule object
Get Actual Employee Schedule
Get employee schedule with populated locations on workdays.
GET /bookme/config/employee-schedules/{id}/actual
Response: EmployeeSchedule object with resolved location data
Update Employee Schedule
PATCH /bookme/config/employee-schedules/{id}
Request Body: Array of JSON Patch operations
[
{ "op": "replace", "path": "/canBeBooked", "value": false },
{ "op": "replace", "path": "/maxMeetingTimePerDay", "value": "06:00:00" }
]
Response: 200 OK
Delete Employee Schedule
DELETE /bookme/config/employee-schedules/{id}
Response: 204 No Content
Warning: This endpoint is not intended for direct consumption. Employee schedules are automatically managed and using this endpoint is advised against. Requires elevated permissions.
Organization Settings API (V3)
Organization Settings configure organization-wide defaults for booking behavior, operating hours, and display preferences.
Get Organization Settings
Get organization settings. A default configuration is created if none exists.
GET /bookme/config/organization-settings
Response: OrganizationSetting object
Update Organization Settings
PATCH /bookme/config/organization-settings/{id}
Request Body: Array of JSON Patch operations
[
{ "op": "replace", "path": "/openingTime", "value": "09:00:00" },
{ "op": "replace", "path": "/closingTime", "value": "18:00:00" }
]
Response: 200 OK
Delete Organization Settings
DELETE /bookme/config/organization-settings/{id}
Response: 204 No Content
Warning: This endpoint is not intended for direct consumption. Organization settings are automatically configured and using this endpoint is advised against. Requires elevated permissions.
Add Closing Days
Add dates when the organization is closed.
POST /bookme/config/organization-settings/{id}/closing-days
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
closingDays |
array of datetime | Yes | Dates to add as closing days (ISO 8601 format) |
Response: 200 OK
Remove Closing Days
Remove dates from the organization’s closing days.
DELETE /bookme/config/organization-settings/{id}/closing-days
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
closingDays |
array of datetime | Yes | Dates to remove from closing days (ISO 8601 format) |
Response: 204 No Content
Locations API (V3)
Locations represent physical locations where meetings can be held, with room booking settings.
List Locations
GET /bookme/config/locations
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
location |
string | No | Filter by location identifier |
search |
string | No | Search term for filtering locations |
Response: Array of Location objects
Create Location
POST /bookme/config/locations
Request Body:
{
"location": "CPH-HQ",
"displayName": "Copenhagen Headquarters",
"addRoomsToBookedMeetings": true,
"requireAvailableRoom": false
}
Response: 201 Created with Location object
Get Location
GET /bookme/config/locations/{id}
Response: Location object
Update Location
PATCH /bookme/config/locations/{id}
Request Body: Array of JSON Patch operations
[
{ "op": "replace", "path": "/displayName", "value": "Updated Location Name" },
{ "op": "replace", "path": "/requireAvailableRoom", "value": true }
]
Response: 200 OK
Delete Location
DELETE /bookme/config/locations/{id}
Response: 204 No Content
List Supported Timezones
Retrieve all available timezones that can be configured for a bank.
GET /bookme/config/timezones
Response: Array of Timezone objects
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
}
LabelRegistration Object (V3)
{
"id": "uuid",
"labelName": "Premium",
"color": "#FFD700",
"description": "Premium tier services",
"usageCount": 5,
"isActive": true,
"firstSeen": "2025-01-15T10:30:00Z",
"lastUpdated": "2025-03-20T14:45:00Z"
}
CompetenceGroup Object (V3)
{
"id": "uuid",
"name": "Mortgage Specialists",
"childGroupIds": ["uuid"],
"parentGroupIds": ["uuid"],
"customerTypes": [{ "id": "uuid", "name": "Business" }],
"employeeIds": ["uuid"],
"topics": [{ "id": "uuid", "name": "Mortgage" }],
"serviceGroupIds": ["uuid"],
"labels": ["Finance", "Premium"]
}
ServiceGroup Object (V3)
{
"id": "uuid",
"name": "Customer Service Team",
"membershipEmail": "team@example.com",
"employeeIds": ["uuid"],
"competenceGroupIds": ["uuid"],
"labels": ["Support"],
"maxMeetingTimePerDay": "04:00:00"
}
EmployeeCompetenceGroup Object (V3)
Represents a competence group membership for an employee.
{
"id": "uuid",
"name": "Mortgage Specialists",
"dateAssigned": "2025-01-15T10:30:00Z"
}
EmployeeServiceGroup Object (V3)
Represents a service group membership for an employee.
{
"id": "uuid",
"name": "Customer Service Team",
"membershipEmail": "team@example.com",
"dateAssigned": "2025-01-15T10:30:00Z"
}
Portal Object (V3)
{
"id": "uuid",
"name": "Customer Booking Portal",
"location": "Copenhagen",
"fields": [
{
"id": "uuid",
"name": "phone",
"label": "Phone Number",
"type": "Phone",
"placeholder": "+45...",
"hidden": false,
"validation": {
"required": true,
"pattern": "^\\+?[0-9]{8,}$"
}
}
],
"logoUrl": "https://example.com/logo.png",
"logoHeight": 60,
"styling": "/* Custom CSS */",
"authenticationType": "AzureAD",
"customerTypeId": "uuid",
"topicId": "uuid",
"crmMappingConfigurationId": "uuid",
"icalEnabled": true,
"labels": ["Public"]
}
Portal Field Types (V3)
Text: Text input fieldPhone: Phone number fieldEmail: Email fieldCheckbox: Checkbox fieldDropdown: Dropdown selection field
Portal Authentication Types (V3)
AzureAD: Standard Azure AD authenticationMitID: Danish MitID national identity authentication
EmployeeSchedule Object (V3)
{
"id": "uuid",
"employeeId": "uuid",
"isOnlyExplicitlyAvailable": false,
"canBeBooked": true,
"workdays": [
{
"isAvailable": true,
"dayOfWeek": "Monday",
"from": "08:00:00",
"to": "17:00:00",
"availableMeetingTypes": [
{ "type": "Physical" },
{ "type": "Online" }
],
"location": "Copenhagen"
}
],
"maxMeetingTimePerDay": "04:00:00"
}
EmployeeWorkday Object (V3)
{
"isAvailable": true,
"dayOfWeek": "Monday",
"from": "08:00:00",
"to": "17:00:00",
"availableMeetingTypes": [
{ "type": "Physical" },
{ "type": "Online" },
{ "type": "Telephone" }
],
"location": "Copenhagen"
}
OrganizationSetting Object (V3)
{
"id": "uuid",
"openingTime": "08:00:00",
"closingTime": "17:00:00",
"maxTimeFromBookingToMeeting": "30.00:00:00",
"maxMeetingTimePerDay": "04:00:00",
"isFilterShowTimesAsCustomerEnabled": true,
"meetingTypeLabels": [
{ "name": "Physical", "label": "In-Person Meeting", "order": 1 },
{ "name": "Online", "label": "Video Call", "order": 2 }
],
"employeeLabels": [
{ "name": "Advisor", "label": "Financial Advisor", "order": 1 }
],
"extendedOpeningHoursEmail": "support@example.com",
"closingDays": ["2025-12-25T00:00:00Z", "2025-12-26T00:00:00Z"],
"iCalMeetingTitle": "Meeting with ",
"iCalMeetingDescription": "Your scheduled meeting",
"timeZoneId": "uuid",
"timeZone": {
"id": "uuid",
"identifier": "Europe/Copenhagen",
"displayName": "Central European Time"
}
}
Timezone Object (V3)
{
"id": "uuid",
"identifier": "Greenland Standard Time",
"displayName": "(UTC-02:00) Greenland"
}
Location Object (V3)
{
"id": "uuid",
"location": "CPH-HQ",
"displayName": "Copenhagen Headquarters",
"addRoomsToBookedMeetings": true,
"requireAvailableRoom": false
}
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-prod.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-prod.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-prod.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-prod.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-prod.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:
- V3 OpenAPI Specification (YAML) - Latest
- V2 OpenAPI Specification (YAML)
- V1 OpenAPI Specification (YAML)
What’s New in V3
V3 is fully backward compatible with V2. All new features are additive.
New APIs
- Labels API: Centralized label management for categorizing resources with fuzzy search and autocomplete
- Competence Groups API: Manage expertise-based employee groupings with hierarchical relationships
- Service Groups API: Organize employees into logical service groups with advisor membership tracking
- Portals API: Configure booking portals with custom form fields, styling, and authentication
- Employee Schedules API: Manage employee work schedules, availability, and meeting time limits
- Organization Settings API: Configure organization-wide booking defaults, operating hours, and closing days
- Locations API: Manage physical locations with room booking settings
Key Features
- Label Filtering: All new resources support labels for categorization and filtering
- JSON Patch Operations: V3 PATCH endpoints use RFC 6902 JSON Patch format
- Service Group Advisors: Get advisor membership from multiple sources (skill-based, assigned, competence groups)
- Portal Configuration: Custom fields with validation, authentication types (AzureAD, MitID), CSS styling
- maxMeetingTimePerDay: New field on Service Groups, Employee Schedules, and Organization Settings to limit daily meeting duration per advisor
For detailed migration instructions, see our V2 to V3 Migration Guide.
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.