MotorDesk API v2

Build Car Dealership Software with the MotorDesk API

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.

Customers and CRM

Create, search, update, and organise customer records, including contact notes and business-specific tags.

Leads and Messaging

Capture enquiries, update lead status, associate vehicles, record messages, and send replies through supported channels.

Vehicle Stock

Look up vehicle data, create and update stock, manage media, generate advert text, and review market pricing.

Stock Syndication

Publish live for-sale stock to your website with the advert-only listings feed.

Appointments

Manage calendar bookings, calendars, reminders, customer notifications, and appointment completion.

Documents and Signatures

List document templates, send documents to customers, and check simple signature status.

Deals

Read checkout/deal records for reporting, integrations, and status checks.

Invoices

Read and write invoice records: create drafts, issue, record payments, raise credit notes, and email documents.

Orders and Purchases

Read and write orders and purchases: create, issue, pay, convert orders to invoices, and upload supplier invoices.

Quick Start

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));

Authentication

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

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

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"
    }
  }
}

Reference Page Format

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.

SectionPurpose
ObjectDefines every returned attribute, its type, and what it means.
EndpointsShows the method, path, required scope, and short description.
ParametersLists every path, query, and JSON body parameter with type, required status, options, and description.
ReturnsExplains the success payload before the JSON example.
ExamplesShows a complete request and a complete successful response.

What Can You Build?

AreaExamplesDocs
Reference DataTags, vehicle statuses, vehicle stock types.Reference
ContactsCustomer search, create/update, notes, tagging.Contacts
LeadsEnquiry capture, status control, messages, sending replies, vehicle links.Leads
AppointmentsCalendar lists, bookings, updates, reminders, mark done.Appointments
CallsVOIP call logs: read, and record from providers/bridges.Calls
BlogCreate, update and publish blog articles.Blog
ReviewsCreate, update and manage customer reviews.Reviews
VehiclesLookups, recognition, stock CRUD, media, pricing, advert text.Vehicles
ListingsAdvert-only stock feed for websites; snapshot, delta sync and reconciliation.Listings
DealsCheckout/deal records and stage tracking.Deals
InvoicesCreate, issue, pay and credit invoices; line items, totals and payment state.Invoices
OrdersCreate, issue and pay orders; convert an order to an invoice.Orders
PurchasesCreate, issue and pay purchases; upload supplier invoices to the pending queue.Purchases
DocumentsDocument templates, sending, and signatures.Documents

Core Concepts

Pagination

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.

Response Views

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

Sparse Fieldsets

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

Conventions

Dates & Times

All timestamps (e.g. created, updated) are Unix epoch seconds in UTC. Date-only fields use YYYY-MM-DD.

Wildcard Search

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).

Combining view and fields

fields 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.

File Uploads

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).

Idempotency & Duplicates

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 & Polling

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.

Machine-Readable Resources

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

For LLMs and AI Agents

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

Accepted Inputs

EndpointAccepted inputs
GET /2.0/metaNo path, query, or body inputs. Requires Authorization: Bearer key_id.secret with meta:read.
GET /2.0/openapi.jsonNo path, query, or body inputs. Returns the OpenAPI document for API v2.

Parameter Reference

NameInTypeRequiredOptionsDescription
The overview endpoints do not accept path, query, or body parameters.

GET Endpoint catalogue

Request
GET /2.0/meta
Response
{
  "success": true,
  "data": {
    "version": "2.0",
    "endpoints": [
      {
        "method": "GET",
        "path": "/2.0/contacts",
        "scopes": [
          "contacts:read"
        ]
      }
    ]
  }
}

GET OpenAPI schema

Request
GET /2.0/openapi.json
Response
{
  "openapi": "3.1.0",
  "info": {
    "title": "MotorDesk API",
    "version": "2.0"
  },
  "paths": {
    "/contacts": {
      "get": {
        "summary": "List contacts"
      }
    }
  }
}