Skip to main content
Version: 1.0.0

Vehiso API

The Vehiso API lets a dealer, and the partner apps a dealer connects, read and write the dealer's own data from outside Vehiso: bespoke websites, DMS tooling, data warehouses, automation tools and AI assistants.

Authentication​

Every request except the OAuth endpoints carries a bearer token: Authorization: Bearer <key>. One host serves every dealer, so the key is what names the dealer. Keys are created in the Vehiso DMS (Dealer Management System) under Administration > Developers and are shown once.

PrefixKindUse
vh_live_sk_secret keyservers, scripts, ETL, against the dealer's data
vh_live_pk_publishable keya visitor's browser on a bespoke website
vh_test_sk_, vh_test_pk_sandbox keysbuilding and testing, against the shared sandbox dealer
vh_oat_OAuth access tokenpartner apps, see OAuth below

A secret key must never be sent from a browser: a request with an Origin header made with one is refused (secret_key_in_browser), so the key is noticed and rolled. A publishable key is checked against its list of allowed origins instead.

A key acts as its owner, a user of the dealership. What a key can do is its scopes AND its owner's current permissions: a scope whose permission the owner has lost stops working (403 scope_not_permitted_for_owner), and a key whose owner is deleted or banned stops working altogether (401 key_owner_inactive). A key may also be restricted to some branches, in which case it sees and writes only their data.

Scopes​

Each operation names the scope it needs in x-required-scope and in its description. GET /me and GET /reference/{list} work with any key. Publishable keys always carry exactly vehicles:read (public fields of website-visible stock only), branches:read, public:enquiries and public:valuation_requests, and can be given nothing more. Secret keys, OAuth and webhooks need a paid plan (403 plan_not_entitled).

Responses​

JSON throughout. A single object is returned flat under data; a list is data (an array) plus meta. Ids are UUIDs; numeric ids never appear.

Pagination and syncing​

Lists use cursor pagination: ?limit= (1 to 100, default 25) and ?cursor=, the meta.next_cursor of the previous page. meta.has_more says whether another page follows. Lists are ordered by updated_at, then id, ascending, so a sync job can page to the end and remember the last updated_at it saw for next time. Every list takes ?updated_since= (an ISO 8601 date-time), and lists of records that can be deleted take ?include_deleted=true, which returns deleted rows as tombstones ({ "id": "...", "deleted": true, "deleted_at": "..." }) so a mirror can apply deletes. GET /events is the exception: events never change, so it is ordered by when they happened.

Idempotency​

Send an Idempotency-Key header (up to 255 characters) on any POST. A retry with the same key and the same body within 24 hours gets the stored response, marked Idempotent-Replayed: true, instead of doing the work twice. The same key with a different body is refused with 409 idempotency_key_reused, and a retry that arrives while the first attempt is still running gets 409 idempotency_key_in_use. Server errors are not stored, so they can always be retried.

Errors​

Errors share one body: { "success": false, "message": "...", "code": "not_found", "request_id": "...", "errors": { "field": ["..."] } }. code is a stable, machine-readable string to branch on; message is for people and may change. errors is present on 422 only, keyed by field. Status codes: 401 for a missing or bad key, 403 for a scope, plan or key restriction, 404, 409 for a conflict with the record's state, 422 for validation, 429 for rate limiting and 5xx for our faults. The OAuth endpoints are the exception: they answer RFC 6749 { "error", "error_description" } bodies, which is what OAuth client libraries expect.

Every response carries X-Request-Id. Quote it to support.

Rate limits​

Limits are per key, per minute: 120 for secret keys and OAuth tokens, 300 for publishable keys. The two public writes are further limited to 10 per minute per visitor address. Responses carry X-RateLimit-Limit and X-RateLimit-Remaining; a 429 carries Retry-After in seconds.

Money, times and codes​

Money is an object in minor units with its currency: { "amount": 1499500, "currency": "GBP" } is 14,995.00 pounds. Money in a request body (a vehicle's price, say) is an integer in minor units of the dealer's currency. Times are ISO 8601 in UTC (2026-09-27T14:02:11Z); dates are YYYY-MM-DD. Statuses, types and sources are codes; GET /reference/{list} lists them with the dealer's names for them.

OAuth​

A partner serving many dealers uses the authorisation code flow with PKCE (S256) instead of asking dealers for keys: send the dealer to GET /oauth/authorize, exchange the code at POST /oauth/token for an access token (vh_oat_, one hour) and a refresh token (vh_ort_, 90 days, rotated on every use), and revoke at POST /oauth/revoke.

Versioning​

/v1 is in the path. Changes within v1 are additive only: new endpoints, new optional request fields and new response fields. Treat unknown response fields as normal.

Authentication​

An API key or an OAuth access token, as Authorization: Bearer <token>. Secret keys start vh_live_sk_ (sandbox vh_test_sk_), publishable keys vh_live_pk_ (sandbox vh_test_pk_) and OAuth access tokens vh_oat_.

Security Scheme Type:

http

HTTP Authorization Scheme:

bearer

Bearer format:

vh_live_sk_... / vh_oat_...

Contact

Vehiso developer support: support@vehiso.com

URL: https://developers.vehiso.com