Requests and responses
Every /v1 endpoint follows the same conventions, so once you have handled one resource you have handled them all.
Base URL
https://api.vehiso.com/v1
All requests use HTTPS. Send JSON request bodies with Content-Type: application/json, except image uploads, which are multipart/form-data.
Response shape
A single object is returned under data:
{
"data": {
"id": "5f0c6a8e-2d41-4f7b-9a53-1f0e8c2b7d19"
}
}
A list puts the items under data and the paging state under meta:
{
"data": [
{"id": "5f0c6a8e-2d41-4f7b-9a53-1f0e8c2b7d19"},
{"id": "b7e3a1c2-9f4d-4a6e-8c21-7d5b0e9f3a64"}
],
"meta": {
"next_cursor": "eyJpdiI6...",
"has_more": true
}
}
There is no further nesting: fields sit directly on each object. The examples on this page show only the fields relevant to the convention being described; the API reference lists every field.
Ids
Every id is a UUID string, for example 5f0c6a8e-2d41-4f7b-9a53-1f0e8c2b7d19. Numeric ids never appear in the API. Event ids are prefixed: evt_ followed by a UUID.
Money
Amounts are objects with an integer amount in minor units (pence for GBP) and an ISO 4217 currency code:
{"amount": 1499500, "currency": "GBP"}
That is £14,995.00. Never use floating point for money: divide by 100 only when displaying it.
In a request body, money is a plain integer in minor units of the dealer's currency. A vehicle's asking price of £14,995.00 is sent as "price": 1499500.
Times and dates
- Timestamps are ISO 8601 in UTC with a
Zsuffix:2026-09-27T14:02:11Z. - Dates without a time are
YYYY-MM-DD:2026-09-27.
Convert to the dealer's local time (Europe/London for UK dealers) only for display.
Errors
Errors use a consistent body and an appropriate HTTP status:
{
"success": false,
"message": "The request was not valid.",
"code": "validation_failed",
"request_id": "0b7c9d4e-6f1a-4c3b-8e2d-5a9f7b1c3e60",
"errors": {
"email": ["The email field must be a valid email address."]
}
}
| Field | Description |
|---|---|
success | Always false on an error. |
message | A human-readable explanation. Show it in logs; do not match on it, because the wording may change. |
code | A stable, machine-readable string. Branch on this. |
request_id | The id of this request. Quote it when you contact support. |
errors | Present on 422 only. Maps each invalid field to a list of messages. |
A 404 for an id that does not exist looks like this:
{
"success": false,
"message": "No vehicle was found with that id.",
"code": "not_found",
"request_id": "0b7c9d4e-6f1a-4c3b-8e2d-5a9f7b1c3e60"
}
Status codes
| Status | When | Example code |
|---|---|---|
200, 201 | Success. | |
202 | Accepted for background processing, for example a batch upload. | |
204 | Success with no body. | |
401 | The key is missing, invalid, revoked or expired. | invalid_api_key |
403 | The key lacks a scope, the plan does not include the API, or an allowlist refused the request. | insufficient_scope |
404 | The resource does not exist, or the key cannot see it. | not_found |
405 | The method is not allowed on this path. | method_not_allowed |
409 | A conflict with the record's current state, or an idempotency key reused with a different body. | idempotency_key_reused |
413 | The request body is too large, for example too many image files at once. | payload_too_large |
422 | Validation failed. errors says which fields. | validation_failed |
429 | Rate limit exceeded. Wait for Retry-After seconds. | rate_limited |
5xx | Something went wrong on our side. Safe to retry with backoff; use an Idempotency-Key on POSTs. | server_error |
The authentication and permission codes are listed in Authentication and API keys.
The OAuth endpoints are the one exception to this format. They answer with the standard RFC 6749 body, {"error": "invalid_grant", "error_description": "..."}, because that is what OAuth client libraries expect.
Request ids
Every response, successful or not, carries an X-Request-Id header. Error bodies repeat it as request_id. Log it alongside your own request logs: the dealer can find the same request in the DMS under Administration > Developers, and support can trace it from the id alone.
HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: 0b7c9d4e-6f1a-4c3b-8e2d-5a9f7b1c3e60
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
Request bodies are never logged by Vehiso, only the method, path, status and timing.
Reference lists
Status and type values (vehicle statuses, enquiry statuses, enquiry types, enquiry sources, appointment types and deal statuses) are available from GET /v1/reference/{list}, which works with any scope:
curl https://api.vehiso.com/v1/reference/vehicle-statuses \
-H "Authorization: Bearer $VEHISO_API_KEY"
The lists are vehicle-statuses, enquiry-statuses, enquiry-types, enquiry-sources, appointment-types and deal-statuses. Each code comes with the dealer's own name for it, so use the lists for labels as well. Read them rather than hard-coding values.