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](https://motordesk.com/api-docs/v2/webhooks/).

Built for developers: predictable resources, a consistent JSON envelope, clear errors, and a complete [OpenAPI 3.1 specification](https://api.motordesk.com/2.0/openapi.json) you can point a code generator or AI agent at. An official [PHP SDK](https://github.com/MotorDesk/api2-php) is available.

[Quick Start](https://motordesk.com/api-docs/v2/#quick-start)

[OpenAPI JSON](https://api.motordesk.com/2.0/openapi.json)

[PHP SDK](https://github.com/MotorDesk/api2-php)

[Explore Endpoints](https://motordesk.com/api-docs/v2/#capabilities)

## 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:

```bash
curl "https://api.motordesk.com/2.0/contacts?per_page=10" \
  -H "Authorization: Bearer key_id.secret" \
  -H "Accept: application/json"
```

```php
// 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";
}
```

```python
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"])
```

```javascript
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`.

```json
{
  "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.

```json
{
  "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.

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

## What Can You Build?

| Area | Examples | Docs |
| --- | --- | --- |
| `Reference Data` | Tags, vehicle statuses, vehicle stock types. | [Reference](https://motordesk.com/api-docs/v2/reference/) |
| `Contacts` | Customer search, create/update, notes, tagging. | [Contacts](https://motordesk.com/api-docs/v2/contacts/) |
| `Leads` | Enquiry capture, status control, messages, sending replies, vehicle links. | [Leads](https://motordesk.com/api-docs/v2/leads/) |
| `Appointments` | Calendar lists, bookings, updates, reminders, mark done. | [Appointments](https://motordesk.com/api-docs/v2/appointments/) |
| `Calls` | VOIP call logs: read, and record from providers/bridges. | [Calls](https://motordesk.com/api-docs/v2/calls/) |
| `Blog` | Create, update and publish blog articles. | [Blog](https://motordesk.com/api-docs/v2/blogs/) |
| `Reviews` | Create, update and manage customer reviews. | [Reviews](https://motordesk.com/api-docs/v2/reviews/) |
| `Vehicles` | Lookups, recognition, stock CRUD, media, pricing, advert text. | [Vehicles](https://motordesk.com/api-docs/v2/vehicles/) |
| `Listings` | Advert-only stock feed for websites; snapshot, delta sync and reconciliation. | [Listings](https://motordesk.com/api-docs/v2/listings/) |
| `Deals` | Checkout/deal records and stage tracking. | [Deals](https://motordesk.com/api-docs/v2/deals/) |
| `Invoices` | Create, issue, pay and credit invoices; line items, totals and payment state. | [Invoices](https://motordesk.com/api-docs/v2/invoices/) |
| `Orders` | Create, issue and pay orders; convert an order to an invoice. | [Orders](https://motordesk.com/api-docs/v2/orders/) |
| `Purchases` | Create, issue and pay purchases; upload supplier invoices to the pending queue. | [Purchases](https://motordesk.com/api-docs/v2/purchases/) |
| `Documents` | Document templates, sending, and signatures. | [Documents](https://motordesk.com/api-docs/v2/documents/) |

## Core Concepts

### Pagination

List endpoints accept `page` and `per_page` (maximum `500`). Responses include a `next_cursor`.

```json
{
  "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`](https://motordesk.com/api-docs/v2/contacts/#ep-post-contacts-duplicate-check).

## Webhooks & Polling

**[Webhooks](https://motordesk.com/api-docs/v2/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](https://motordesk.com/api-docs/v2/webhooks/) 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](https://motordesk.com/api-docs/v2/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](https://llmstxt.org/) 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

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

### Parameter Reference

| Name | In | Type | Required | Options | Description |
| --- | --- | --- | --- | --- | --- |
| The overview endpoints do not accept path, query, or body parameters. |  |  |  |  |  |

### GET Endpoint catalogue

Request

```
GET /2.0/meta
```

Response

```json
{
  "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

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