Quickstart
Every request is authenticated with a secret API key sent as a Bearer token. Keys are scoped to a single dealership and only ever return that dealership's active data.
- Sign in to your DealerDMS account and open Admin & Settings → API Keys.
- Create a key and copy it — the secret is shown once.
- Call the API with
Authorization: Bearer ddms_live_…
# List your active inventory
curl "https://app.car-search.ai/api/v1/inventory?limit=5" \
-H "Authorization: Bearer ddms_live_YOUR_KEY"
🔒 API keys are only available to signed-up dealerships. Reading these docs is open to everyone; creating keys requires a DealerDMS account with the admin.manage permission.
Base URL
All endpoints are served under /api/v1 on your DealerDMS host:
https://app.car-search.ai/api/v1Responses are JSON. Requests and responses use UTF-8. List endpoints return a pagination object with offset, limit, total_results and total_pages.
Authentication
Send your key in the Authorization header as a Bearer token. An X-API-Key header is also accepted. Keys are prefixed ddms_live_; the full secret is shown only once at creation and is stored on our side as a SHA-256 hash — we can never show it to you again.
Authorization: Bearer ddms_live_9f2c…
# — or —
X-API-Key: ddms_live_9f2c…| Status | Meaning |
|---|---|
401 | Missing, malformed, invalid or revoked key |
403 | Key lacks the required scope, or the request origin/IP isn't allow-listed for the key |
429 | Per-minute rate limit exceeded (see Rate limits) |
Scopes
Each key carries a set of scopes. A request is rejected with 403 if the key doesn't hold the scope the endpoint requires. A key with the * scope may call any endpoint.
| Scope | Grants |
|---|---|
inventory.read | Read inventory, individual vehicles and vehicle images |
search | Semantic + text search over inventory |
widget.chat | Embedded AI assistant: config + chat turns |
* | All of the above |
Rate limits & security
- Rate limit. Each key has a per-minute request limit (default 120/min). Exceeding it returns
429with aRetry-After: 60header. - Origin allow-list. A key may be restricted to specific browser origins (CORS). Requests from other origins get
403. - IP allow-list. A key may be restricted to specific server IP addresses for backend-to-backend use.
- Tenant isolation. A key can only ever read the data of the dealership that owns it.
Endpoints
/api/v1/inventoryinventory.readList the dealership's active inventory, newest first.
Query parameters
offset | Row to start at. Default 0. |
limit | Page size, max 100. Default 24. |
stock_status | Filter by status, e.g. in_stock, reserved, sold. |
q | Free-text filter (make, model, etc.). |
{
"data": [ { "vehicle_uid": "…", "make": "BMW", "model": "X5", "price": 42995, … } ],
"pagination": { "offset": 0, "limit": 24, "total_results": 6, "total_pages": 1 }
}/api/v1/inventory/:idinventory.readFetch one vehicle by its vehicle_uid, including its image list. Returns 404 if the vehicle isn't found in your dealership.
{ "data": { "vehicle": { … }, "images": [ { "image_uid": "…", … } ] } }/api/v1/searchsearchSemantic search over inventory. When embeddings are enabled the query is embedded and matched by meaning; otherwise it falls back to text search. The response mode is "semantic" or "text".
Body
{ "query": "economical family SUV under 20k",
"filters": { "make": "Kia" },
"offset": 0, "limit": 24 }{ "data": [ … ], "mode": "semantic",
"pagination": { "offset": 0, "limit": 24, "total_results": 3, "total_pages": 1 } }/api/v1/images/:imageId/:sizeinventory.readServe a vehicle image binary — drop it straight into an <img> on your site. :size is thumb, medium or full. Cached for 24h.
<img src="https://app.car-search.ai/api/v1/images/IMAGE_UID/medium">Because <img> can't send an Authorization header, images are best served through a key whose origin allow-list includes your website, or proxied by your own backend.
/api/v1/widget/configwidget.chatPublic branding + greeting for the embedded AI assistant: assistant name, welcome message, primary colour and logo.
{ "enabled": true, "assistant_name": "Assistant",
"welcome_message": "Hi! How can I help you find your next car?",
"primary_color": "#1d4ed8", "logo_url": null }/api/v1/widget/chatwidget.chatSend one chat turn to the AI sales assistant. It can search your live stock and answer buyer questions. Pass back the conversation_uid it returns to continue a thread.
{ "message": "Do you have any diesel estates?",
"conversation_uid": null, "visitor_id": "abc-123", "history": [] }/api/v1/widget/chat/streamwidget.chatSame as /widget/chat, but streams the reply token-by-token as Server-Sent Events for a live typewriter. Same request body. The response is text/event-stream; each data: line is one JSON event:
{ "type": "meta", "conversation_uid": "…" } // once, first
{ "type": "delta", "text": "Yes — we have " } // repeated: reply chunks
{ "type": "tool", "name": "search_inventory" } // a stock search is running
{ "type": "done", "reply": "…full text…", "conversation_uid": "…" }
{ "type": "error", "error": "…" } // only on a mid-stream failureA rejection that happens before the stream opens (disabled assistant, over a limit) returns a normal JSON error + status instead of a stream. The drop-in widget.js uses this endpoint automatically and falls back to /widget/chat where streaming isn't available.
/embed/pubkeypublicThe platform's Ed25519 public key (JWK). Marketplace widget bundles served at /widgets.wc.js carry an X-DDMS-Signature (base64 Ed25519 over the exact bytes) and an X-DDMS-Key-Id. Verify the signature before executing the bundle to guarantee it wasn't tampered with in transit or at a CDN:
const base = 'https://app.car-search.ai';
const [{ x }, res] = await Promise.all([
fetch(base + '/embed/pubkey').then(r => r.json()),
fetch(base + '/widgets.wc.js'),
]);
const bytes = new Uint8Array(await res.clone().arrayBuffer());
const sig = Uint8Array.from(atob(res.headers.get('X-DDMS-Signature')), c => c.charCodeAt(0));
const key = await crypto.subtle.importKey('jwk',
{ kty: 'OKP', crv: 'Ed25519', x }, { name: 'Ed25519' }, false, ['verify']);
if (!await crypto.subtle.verify('Ed25519', key, sig, bytes))
throw new Error('bundle failed signature verification');
await import(URL.createObjectURL(new Blob([bytes], { type: 'application/javascript' })));Errors
Errors return the appropriate HTTP status and a JSON body of the shape { "error": "…" }. Handle 401/403 (auth), 404 (not found), 429 (back off using Retry-After) and 5xx (retry with backoff).
Managing API keys
API keys are created and revoked inside your DealerDMS account — only signed-up dealerships can hold keys, and only a user with the admin.manage permission can manage them.
- Sign in and go to Admin & Settings → API Keys.
- Click New API key, name it (e.g. "Website"), and choose scopes.
- Copy the
ddms_live_…secret immediately — it is shown once. - Optionally restrict the key to specific origins/IPs and set a rate limit.
- Revoke a key any time; it stops working instantly.
Not a DealerDMS customer yet?
Get access to the API — and the whole platform — by booking a demo.
Request a demo