MotorDesk API v2
MotorDesk provides a REST API for car dealerships to manage vehicle stock, leads and CRM, sales, invoicing, purchases, appointments, documents, reviews and webhooks. JSON over HTTPS, OpenAPI 3.1, and scoped bearer tokens.
Use it to connect dealer websites, CRMs, stock feeds, lead-generation tools, accounting, reporting, and custom or AI-driven automotive applications. Common integrations: sync vehicle inventory to a website, push and manage leads, automate sales and invoicing, build a valuation or part-exchange tool, and receive real-time events (a vehicle sold, a new lead) over webhooks.
Built for developers: predictable resources, a consistent JSON envelope, clear errors, and a complete OpenAPI 3.1 specification you can point a code generator or AI agent at. An official PHP SDK is available.
Create, search, update, and organise customer records, including contact notes and business-specific tags.
Capture enquiries, update lead status, associate vehicles, record messages, and send replies through supported channels.
Look up vehicle data, create and update stock, manage media, generate advert text, and review market pricing.
Publish live for-sale stock to your website with the advert-only listings feed.
Manage calendar bookings, calendars, reminders, customer notifications, and appointment completion.
List document templates, send documents to customers, and check simple signature status.
Read checkout/deal records for reporting, integrations, and status checks.
Read and write invoice records: create drafts, issue, record payments, raise credit notes, and email documents.
Read and write orders and purchases: create, issue, pay, convert orders to invoices, and upload supplier invoices.
The base URL is https://api.motordesk.com/2.0/. Send JSON request bodies with Content-Type: application/json, and authenticate protected endpoints with a Bearer token. The same call to list contacts looks like this in a few common languages:
curl "https://api.motordesk.com/2.0/contacts?per_page=10" \
-H "Authorization: Bearer key_id.secret" \
-H "Accept: application/json"
// composer require motordesk/api2
use MotorDesk\Api2\Client;
$client = new Client(apiKey: 'key_id.secret');
$contacts = $client->contacts()->list(['per_page' => 10]);
foreach ($contacts['data'] as $contact) {
echo $contact['email'] . "\n";
}
import requests
resp = requests.get(
"https://api.motordesk.com/2.0/contacts",
headers={"Authorization": "Bearer key_id.secret"},
params={"per_page": 10},
)
for contact in resp.json()["data"]:
print(contact["email"])
const res = await fetch("https://api.motordesk.com/2.0/contacts?per_page=10", {
headers: { Authorization: "Bearer key_id.secret" },
});
const body = await res.json();
body.data.forEach((contact) => console.log(contact.email));
API v2 uses scoped Bearer tokens. Each key has its own scopes and is issued as a single value in the form key_id.secret. Keep the full token private and send it in the Authorization header.
Authorization: Bearer key_id.secret
Access is controlled by scopes, so each key can be limited to the resources and actions it needs.
Successful responses include success: true. Most endpoints return their main payload in data; list endpoints may also include pagination information in meta.
{
"success": true,
"data": {
"id": 123,
"email": "customer@example.com"
}
}
Errors include a stable code and a clear message. Validation errors may include field-level details.
{
"success": false,
"error": {
"code": "validation_failed",
"message": "The email address is invalid.",
"details": {
"field": "email"
}
}
}
Resource reference pages are organised around the objects and endpoints your integration uses. Each resource page should start with the object shape, list the available endpoints, then document each endpoint with its parameters, return value, and complete request and response examples.
| Section | Purpose |
|---|---|
Object | Defines every returned attribute, its type, and what it means. |
Endpoints | Shows the method, path, required scope, and short description. |
Parameters | Lists every path, query, and JSON body parameter with type, required status, options, and description. |
Returns | Explains the success payload before the JSON example. |
Examples | Shows a complete request and a complete successful response. |
| Area | Examples | Docs |
|---|---|---|
Reference Data | Tags, vehicle statuses, vehicle stock types. | Reference |
Contacts | Customer search, create/update, notes, tagging. | Contacts |
Leads | Enquiry capture, status control, messages, sending replies, vehicle links. | Leads |
Appointments | Calendar lists, bookings, updates, reminders, mark done. | Appointments |
Calls | VOIP call logs: read, and record from providers/bridges. | Calls |
Blog | Create, update and publish blog articles. | Blog |
Reviews | Create, update and manage customer reviews. | Reviews |
Vehicles | Lookups, recognition, stock CRUD, media, pricing, advert text. | Vehicles |
Listings | Advert-only stock feed for websites; snapshot, delta sync and reconciliation. | Listings |
Deals | Checkout/deal records and stage tracking. | Deals |
Invoices | Create, issue, pay and credit invoices; line items, totals and payment state. | Invoices |
Orders | Create, issue and pay orders; convert an order to an invoice. | Orders |
Purchases | Create, issue and pay purchases; upload supplier invoices to the pending queue. | Purchases |
Documents | Document templates, sending, and signatures. | Documents |
List endpoints accept page and per_page (maximum 500). Responses include a next_cursor.
{
"meta": {
"pagination": {
"page": 1,
"per_page": 50,
"total": 125,
"total_pages": 3,
"next_cursor": "WyIxNzE3..."
}
}
}
For large or frequently-changing result sets, pass next_cursor back as ?cursor= to page by keyset, fast at any depth and stable while rows change. In cursor mode page/total are omitted; follow next_cursor until it is null.
Several resources support view=simple and view=full. Lists default to compact responses; single-resource responses usually default to full detail.
GET /2.0/vehicles?view=simple
GET /2.0/vehicles/123?view=full
On any GET request, pass fields with a comma-separated list of dot-paths to return only those fields, useful for trimming large objects such as vehicles.
GET /2.0/vehicles/123?fields=id,registration,data.stock.price_channel
All timestamps (e.g. created, updated) are Unix epoch seconds in UTC. Date-only fields use YYYY-MM-DD.
Text filters that support wildcards accept % as the wildcard. Matching is case-insensitive and requires at least 3 characters alongside the %. Endpoints that support it note it on the parameter (scope search:wildcard).
view and fieldsfields takes precedence: it returns exactly the dot-paths you list, selected from the full resource regardless of view. Use view for preset shapes and fields for precise selection: a field named in fields is returned even if it isn't part of view=simple.
Files (media, documents, purchase invoices) are sent base64-encoded in the JSON body as a file field (a raw base64 string or a data: URL), up to 25 MB. Vehicle media and test-drive photos also accept a multipart/form-data upload (file part named file), recommended for large media such as video to avoid base64 inflation. Accepted types vary by endpoint and are listed on each (e.g. documents accept PDF/JPG/PNG, plus video on job stages; purchase invoices accept PDF/JPG/PNG).
Send an Idempotency-Key header (a unique string you generate, up to 255 characters) on any POST, PATCH, PUT or DELETE to make retries safe: the first response is stored for 24 hours and any later request with the same key replays that stored response instead of repeating the operation, returning X-Idempotent-Replay: true. Reusing a key with a different request body returns 422; server errors (5xx) are never stored, so they stay retryable. To detect an existing contact before creating one, call POST /contacts/duplicate-check.
Webhooks are the recommended way to stay in sync. Subscribe once to the events you care about, such as vehicle.sold, lead.created and invoice.paid, and MotorDesk delivers a signed HTTP POST the moment each one happens. You get changes in near real time, only for what actually changed, without repeatedly calling the API or burning your rate-limit budget polling for events that may never come. This is the right model for active data such as leads, messages, appointments and sales. See the Webhooks guide to get started.
Polling is a fallback for cases where webhooks do not fit, for example when you cannot expose a public HTTPS endpoint to receive deliveries, or for a periodic reconciliation sweep alongside webhooks. To poll efficiently, request list endpoints such as /2.0/leads, /2.0/leads/{id}/messages, and /2.0/appointments with the created/updated range filters (where supported) and the keyset cursor, so you fetch only records that are new or changed since your last sync rather than re-reading everything.
If you must poll, poll as infrequently as your use case allows and stay well within the rate limits (300 requests/minute and 10,000/day per business): slow-moving data such as stock needs only an occasional sweep, and even active data rarely needs tighter than a minute. Stagger multiple pollers and back off on 429. For anything time-sensitive, prefer a webhook subscription over a short polling interval.
Use the OpenAPI schema for code generation, schema-aware clients, automated testing, or documentation tooling. Import the document into Postman or Insomnia, or generate a typed client with a tool such as openapi-generator.
GET https://api.motordesk.com/2.0/openapi.json
Authenticated clients can also request /2.0/meta to see the endpoint catalogue available to their credential.
GET /2.0/meta
These docs are published as Markdown for large language models and AI coding tools. The site's root llms.txt indexes the whole website and links to the API docs below. Every page is also available as Markdown by appending .md to its URL (for example /api-docs/v2/vehicles.md), and each HTML page links its Markdown version with <link rel="alternate" type="text/markdown">.
GET https://motordesk.com/llms.txt # site index (links to the API docs)
GET https://motordesk.com/api-docs/v2/llms.md # API docs index (Markdown)
GET https://motordesk.com/api-docs/v2/llms-full.md # every API page as one Markdown file
GET https://motordesk.com/api-docs/v2/vehicles.md # a single API page as Markdown
| Endpoint | Accepted inputs |
|---|---|
GET /2.0/meta | No path, query, or body inputs. Requires Authorization: Bearer key_id.secret with meta:read. |
GET /2.0/openapi.json | No path, query, or body inputs. Returns the OpenAPI document for API v2. |
| Name | In | Type | Required | Options | Description |
|---|---|---|---|---|---|
| The overview endpoints do not accept path, query, or body parameters. | |||||
GET /2.0/meta{
"success": true,
"data": {
"version": "2.0",
"endpoints": [
{
"method": "GET",
"path": "/2.0/contacts",
"scopes": [
"contacts:read"
]
}
]
}
}GET /2.0/openapi.json{
"openapi": "3.1.0",
"info": {
"title": "MotorDesk API",
"version": "2.0"
},
"paths": {
"/contacts": {
"get": {
"summary": "List contacts"
}
}
}
}