← API v2 Overview

Appointments

Create, update, organise, notify, and complete bookings across MotorDesk calendars.

Checking Availability

Before offering a customer a time, use GET /appointments/availability to get the dates and times that can actually be booked. It uses the same engine as the dealer's own booking pages, so it accounts for opening hours, appointment duration, how many bookings are allowed per hour and per day, the minimum notice required, existing appointments, and any holiday closures.

Pass ?booking= for a single booking type (ids come from /reference/booking-types), or omit it to get every enabled type. Add ?date=YYYY-MM-DD to check one date - including a date beyond the type's normal booking window, up to 400 days ahead.

GET /2.0/appointments/availability?booking=vehicle_test_drive&date=2026-09-01

{
  "success": true,
  "data": [
    {
      "booking": "vehicle_test_drive",
      "name": "Test Drive",
      "calendar": "Primary Calendar",
      "duration_minutes": 30,
      "dates": [ { "date": "2026-09-01", "slots": ["09:00", "10:00", "14:00"] } ]
    }
  ]
}

Slot times are returned as HH:MM and can be passed straight to POST /appointments as time. Dates with nothing available are omitted, so an empty dates array simply means there is nothing bookable in the window.

Two things are worth knowing:

  • Capacity is shared per calendar, not per booking type. Two booking types that use the same calendar compete for the same slots, so booking one reduces availability for the other.
  • Availability is advisory. POST /appointments does not require a free slot - staff can book any time from the dashboard, and the API behaves the same way. Check availability when you are offering times to a customer; skip it when you are recording an appointment a member of staff has already arranged.

The Appointment Object

Generated from the OpenAPI schema. Always matches the live API.

Fields (17)
AttributeTypeRequiredDescriptionExample
idinteger-Appointment id.
leadinteger or null-Associated lead id, or null.
contactinteger or null-Associated contact id, or null.
typeenum-Appointment type.
calendarstring-Calendar name the appointment is on.
bookingstring-Booking type id (see /reference/booking-types).
datestring (date)-Appointment date (YYYY-MM-DD).
timestring-24-hour start time (HH:MM:SS).
purposestring-Short purpose/title.
durationinteger or null-Duration in the unit given by duration_type, or null.
duration_typeenum-Duration unit: m=minutes, h=hours.
notestring-Free-text note.
userarray of integer-Staff user ids the appointment is assigned to.
vehicleinteger or null-Stock vehicle id associated as the vehicle of interest (via the lead), or null.
persistboolean-Whether the appointment stays visible after its date passes.
doneboolean-Whether the appointment is marked done.
createdinteger-Unix timestamp the appointment was created.
Example object
{
    "id": 0,
    "lead": 0,
    "contact": 0,
    "type": "appointment",
    "calendar": "",
    "booking": "",
    "date": "2026-01-01",
    "time": "",
    "purpose": "",
    "duration": 0,
    "duration_type": "m",
    "note": "",
    "user": [
        0
    ],
    "vehicle": 0,
    "persist": false,
    "done": false,
    "created": 0
}

Endpoints

GET List Appointments

GET /2.0/appointments · scope appointments:read

List appointments with pagination and optional filters (date range, calendar, booking type, etc.).

Parameters (16)
ParameterInTypeRequiredDescriptionExample
pagequeryinteger-Page number, starting at 1 (offset pagination).
per_pagequeryinteger-Results per page (maximum 500).
leadqueryinteger-Filter by lead id.
contactqueryinteger-Filter by contact id.
vehiclequeryinteger-Filter to appointments associated with this vehicle id, directly or via a vehicle-associated lead.
typequeryenum-Filter by type.
calendarquerystring-Filter by calendar name, as GET /appointments/calendars lists them. "Primary Calendar" (case-insensitive) matches appointments on the primary calendar. Exact match, or use % for wildcard matching (requires search:wildcard).Primary Calendar
bookingquerystring-Filter by booking.
userquerystring-Filter by user.
persistqueryinteger-Filter by persistence: 0 or 1.
donequeryinteger-Filter by done state: 0 or 1.
datequerystring (date)-Only records on this date (YYYY-MM-DD).
date_fromquerystring (date)-Only records on or after this date (YYYY-MM-DD).
date_toquerystring (date)-Only records on or before this date (YYYY-MM-DD).
cursorquerystring-Keyset pagination cursor from a previous response's meta.pagination.next_cursor. When supplied, page/total are not returned.
fieldsquerystring-Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel.
Responses
StatusDescription
200 OKPaginated appointments

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": [
        {
            "id": 0,
            "lead": 0,
            "contact": 0,
            "type": "appointment",
            "calendar": "",
            "booking": "",
            "date": "2026-01-01",
            "time": "",
            "purpose": "",
            "duration": 0,
            "duration_type": "m",
            "note": "",
            "user": [
                0
            ],
            "vehicle": 0,
            "persist": false,
            "done": false,
            "created": 0
        }
    ],
    "meta": {
        "pagination": {
            "page": 0,
            "per_page": 0,
            "total": 0,
            "total_pages": 0,
            "next_cursor": ""
        }
    }
}

POST Create Appointment

POST /2.0/appointments · scope appointments:write

Create an appointment for a lead or customer. date, time, purpose and user are required. An optional vehicle (or vehicle_tag) links the vehicle of interest, so the appointment becomes queryable with the vehicle filter. A lead is not required - with one the vehicle also joins that lead's vehicles of interest.

Request body
AttributeTypeRequiredDescriptionExample
typeenum-Appointment type. Numeric 0 (appointment) and 1 (reminder) are accepted as legacy aliases.appointment
leadinteger-Lead id the appointment belongs to.4009
contactinteger-Contact id the appointment belongs to.77684
datestring (date)YesAppointment date (YYYY-MM-DD).2026-09-01
timestringYes24-hour start time (HH:MM).14:30
durationinteger-Duration, in the unit given by duration_type.30
duration_typeenum-Duration unit: m=minutes, h=hours.m
calendarstring-Primary Calendar, or a configured calendar from /appointments/calendars. Reads back exactly as the calendars endpoint names it, so the value round-trips.Primary Calendar
vehicleinteger-Create only: stock vehicle id of interest, making the appointment queryable with the vehicle filter. A lead is not required; when the appointment has one, the vehicle also joins that lead's vehicles of interest. Use this or vehicle_tag.85481
vehicle_tagstring-Create only: vehicle tag of interest, as an alternative to vehicle.HbSDTp97
bookingstring-Booking type id from /reference/booking-types, e.g. vehicle_test_drive.vehicle_test_drive
purposestringYesShort purpose/title for the appointment.Test drive
notestring-Free-text note.Customer wants to view the BMW 3 Series.
userstringYesStaff user id (or array of ids) the appointment is assigned to. Each id must exist in this business (see /reference/staff-users); unknown ids are rejected.1
persistboolean-Keep the appointment visible after its date passes.false
doneboolean-Whether the appointment is marked done.false
{
    "type": "appointment",
    "lead": 4009,
    "contact": 77684,
    "date": "2026-09-01",
    "time": "14:30",
    "duration": 30,
    "duration_type": "m",
    "calendar": "Primary Calendar",
    "vehicle": 85481,
    "vehicle_tag": "HbSDTp97",
    "booking": "vehicle_test_drive",
    "purpose": "Test drive",
    "note": "Customer wants to view the BMW 3 Series.",
    "user": 1,
    "persist": false,
    "done": false
}
Responses
StatusDescription
201 CreatedAppointment created

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "lead": 0,
        "contact": 0,
        "type": "appointment",
        "calendar": "",
        "booking": "",
        "date": "2026-01-01",
        "time": "",
        "purpose": "",
        "duration": 0,
        "duration_type": "m",
        "note": "",
        "user": [
            0
        ],
        "vehicle": 0,
        "persist": false,
        "done": false,
        "created": 0
    }
}

GET Get Appointment

GET /2.0/appointments/{id} · scope appointments:read

Retrieve a single appointment by id.

Parameters (2)
ParameterInTypeRequiredDescriptionExample
idpathstringYesResource identifier in the path.
fieldsquerystring-Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel.
Responses
StatusDescription
200 OKAppointment

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "lead": 0,
        "contact": 0,
        "type": "appointment",
        "calendar": "",
        "booking": "",
        "date": "2026-01-01",
        "time": "",
        "purpose": "",
        "duration": 0,
        "duration_type": "m",
        "note": "",
        "user": [
            0
        ],
        "vehicle": 0,
        "persist": false,
        "done": false,
        "created": 0
    }
}

PATCH Update Appointment

PATCH /2.0/appointments/{id} · scope appointments:write

Update an appointment. Only the supplied fields are changed.

Parameters (1)
ParameterInTypeRequiredDescriptionExample
idpathstringYesResource identifier in the path.
Request body
AttributeTypeRequiredDescriptionExample
typeenum-Appointment type. Numeric 0 (appointment) and 1 (reminder) are accepted as legacy aliases.appointment
leadinteger-Lead id the appointment belongs to.4009
contactinteger-Contact id the appointment belongs to.77684
datestring (date)-Appointment date (YYYY-MM-DD).2026-09-01
timestring-24-hour start time (HH:MM).14:30
durationinteger-Duration, in the unit given by duration_type.30
duration_typeenum-Duration unit: m=minutes, h=hours.m
calendarstring-Primary Calendar, or a configured calendar from /appointments/calendars. Reads back exactly as the calendars endpoint names it, so the value round-trips.Primary Calendar
vehicleinteger-Create only: stock vehicle id of interest, making the appointment queryable with the vehicle filter. A lead is not required; when the appointment has one, the vehicle also joins that lead's vehicles of interest. Use this or vehicle_tag.85481
vehicle_tagstring-Create only: vehicle tag of interest, as an alternative to vehicle.HbSDTp97
bookingstring-Booking type id from /reference/booking-types, e.g. vehicle_test_drive.vehicle_test_drive
purposestring-Short purpose/title for the appointment.Test drive
notestring-Free-text note.Customer wants to view the BMW 3 Series.
userstring-Staff user id (or array of ids) the appointment is assigned to. Each id must exist in this business (see /reference/staff-users); unknown ids are rejected.1
persistboolean-Keep the appointment visible after its date passes.false
doneboolean-Whether the appointment is marked done.false
{
    "type": "appointment",
    "lead": 4009,
    "contact": 77684,
    "date": "2026-09-01",
    "time": "14:30",
    "duration": 30,
    "duration_type": "m",
    "calendar": "Primary Calendar",
    "vehicle": 85481,
    "vehicle_tag": "HbSDTp97",
    "booking": "vehicle_test_drive",
    "purpose": "Test drive",
    "note": "Customer wants to view the BMW 3 Series.",
    "user": 1,
    "persist": false,
    "done": false
}
Responses
StatusDescription
200 OKAppointment updated

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "lead": 0,
        "contact": 0,
        "type": "appointment",
        "calendar": "",
        "booking": "",
        "date": "2026-01-01",
        "time": "",
        "purpose": "",
        "duration": 0,
        "duration_type": "m",
        "note": "",
        "user": [
            0
        ],
        "vehicle": 0,
        "persist": false,
        "done": false,
        "created": 0
    }
}

DELETE Delete Appointment

DELETE /2.0/appointments/{id} · scope appointments:delete

Delete an appointment.

Parameters (1)
ParameterInTypeRequiredDescriptionExample
idpathstringYesResource identifier in the path.
Responses
StatusDescription
200 OKAppointment deleted

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "deleted": false
    }
}

Availability

GET Check Booking Availability

GET /2.0/appointments/availability · scope appointments:read

List the bookable dates and times for each enabled booking type, from the same engine the dashboard and website booking forms use. Optional ?booking=<type> for one type and ?date=YYYY-MM-DD for one date. Slot times are returned as HH:MM and can be passed straight to POST /appointments. Availability is advisory - creating an appointment does not require a free slot, matching the dashboard.

Parameters (3)
ParameterInTypeRequiredDescriptionExample
bookingquerystring-Booking type id from GET /reference/booking-types. Omit to return every enabled booking type.vehicle_test_drive
datequerystring (date)-Restrict the result to a single date. Dates beyond the booking type's normal calendar window can be checked this way, up to 400 days ahead.2026-09-01
fieldsquerystring-Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel.
Responses
StatusDescription
200 OKBookable dates and times per booking type

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": [
        {
            "booking": "vehicle_test_drive",
            "name": "Test Drive",
            "calendar": "Primary Calendar",
            "duration_minutes": 30,
            "dates": [
                {
                    "date": "2026-09-01",
                    "slots": [
                        "09:00",
                        "10:00",
                        "14:00"
                    ]
                }
            ]
        }
    ]
}

Notify

POST Send Appointment Notification

POST /2.0/appointments/{id}/notify · scope appointments:write

Send an appointment notification to the customer. type is one of book, change, cancel, remind, or message; only the message type uses subject (optional, defaults to "Appointment Message") and message (required).

Parameters (1)
ParameterInTypeRequiredDescriptionExample
idpathstringYesResource identifier in the path.
Request body
AttributeTypeRequiredDescriptionExample
typeenumYesNotification type: book (confirmation), change (rescheduled), cancel (cancellation), remind (reminder), or message (a custom message).remind
subjectstring-Subject line, used only for type=message; defaults to "Appointment Message" when omitted.Your appointment with us
messagestring-Message body, required for type=message and ignored otherwise.Looking forward to seeing you on Saturday at 2:30pm.
{
    "type": "remind",
    "subject": "Your appointment with us",
    "message": "Looking forward to seeing you on Saturday at 2:30pm."
}
Responses
StatusDescription
200 OKAppointment notification sent

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "notified": false,
        "type": "book"
    }
}

Done

POST Mark Appointment Done

POST /2.0/appointments/{id}/done · scope appointments:write

Mark an appointment as done.

Parameters (1)
ParameterInTypeRequiredDescriptionExample
idpathstringYesResource identifier in the path.
Responses
StatusDescription
200 OKAppointment marked done

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": {
        "id": 0,
        "lead": 0,
        "contact": 0,
        "type": "appointment",
        "calendar": "",
        "booking": "",
        "date": "2026-01-01",
        "time": "",
        "purpose": "",
        "duration": 0,
        "duration_type": "m",
        "note": "",
        "user": [
            0
        ],
        "vehicle": 0,
        "persist": false,
        "done": false,
        "created": 0
    }
}

Calendars

GET List Calendars

GET /2.0/appointments/calendars · scope appointments:read

List the configured appointment calendars.

Parameters (2)
ParameterInTypeRequiredDescriptionExample
cursorquerystring-Keyset pagination cursor from a previous response's meta.pagination.next_cursor. When supplied, page/total are not returned.
fieldsquerystring-Comma-separated dot-paths to return only those fields, e.g. id,data.stock.price_channel.
Responses
StatusDescription
200 OKAppointment calendars

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

{
    "success": true,
    "data": [
        {
            "name": "",
            "colour": "success",
            "primary": false
        }
    ],
    "meta": {
        "pagination": {
            "page": 0,
            "per_page": 0,
            "total": 0,
            "total_pages": 0,
            "next_cursor": ""
        }
    }
}

PUT Replace Calendars

PUT /2.0/appointments/calendars · scope appointments:write

Replace the entire appointment calendar set. Calendars not included are removed and their appointments unassigned. Use PATCH to merge instead.

Request body
AttributeTypeRequiredDescriptionExample
calendarsarray of objectYesThe calendars to apply.
{
    "calendars": [
        {
            "name": "Sales",
            "colour": "primary",
            "previous_name": "Showroom",
            "previous_key": ""
        }
    ]
}
Responses
StatusDescription
200 OKAppointment calendars updated

Failures use the standard error responses (4xx/5xx) with the shared error envelope.

PATCH Merge Calendars

PATCH /2.0/appointments/calendars · scope appointments:write

Merge the supplied calendars into the existing set, adding, updating, or renaming them without removing the others. Use PUT to replace the whole set.

Request body
AttributeTypeRequiredDescriptionExample
calendarsarray of objectYesThe calendars to apply.
{
    "calendars": [
        {
            "name": "Sales",
            "colour": "primary",
            "previous_name": "Showroom",
            "previous_key": ""
        }
    ]
}
Responses
StatusDescription
200 OKAppointment calendars updated

Failures use the standard error responses (4xx/5xx) with the shared error envelope.