Skip to main content

Authentication and API keys

Every request is authenticated with a bearer token in the Authorization header:

GET /v1/me HTTP/1.1
Host: api.vehiso.com
Authorization: Bearer vh_live_sk_...

There are no sessions, cookies or CSRF tokens. The key identifies the dealer, so every dealer uses the same host, api.vehiso.com.

Key types​

PrefixTypeUse it forData it reaches
vh_live_sk_SecretServers, scripts, ETL jobsThe dealer's own data
vh_live_pk_PublishableBrowser code on a bespoke websiteThe dealer's public data
vh_test_sk_Secret (test)Building and testingThe shared sandbox dealer
vh_test_pk_Publishable (test)Building and testingThe shared sandbox dealer
vh_oat_OAuth access tokenPartner appsThe dealer that authorised the app

A key has the form vh_<environment>_<type>_<lookup>_<secret>. The fixed vh_ prefix lets secret scanners such as GitHub secret scanning recognise a leaked key.

A key is shown once, when it is created or rolled. Vehiso stores only a SHA-256 hash of its secret part, so a lost key cannot be recovered, only replaced.

Secret keys​

Secret keys can be given any scope the key owner is allowed. Treat them like a password: keep them in a secrets manager or environment variable on a server.

A secret key must never be used from a browser. If a request made with a secret key carries an Origin header, the API refuses it with 403 secret_key_in_browser. A key that has been in browser code has been exposed to everyone who opened developer tools, so roll it and use a publishable key instead.

Secret keys need a paid Vehiso plan. On the Free plan, requests with a secret key are refused with 403 plan_not_entitled.

Publishable keys​

Publishable keys are made to be embedded in a website's front-end code. They are fixed to a public subset of the API and cannot be given more:

  • vehicles:read, limited to public fields and stock that is visible on the website
  • branches:read
  • POST /v1/public/enquiries
  • POST /v1/public/valuation-requests

A publishable key must have an origin allowlist of at least one https://host[:port] entry. A browser request from an origin that is not on the list is refused with 403 origin_not_allowed. Publishable keys work on every plan, including Free.

See Building a website with a publishable key.

Test keys and the sandbox​

Test keys (vh_test_sk_..., vh_test_pk_...) resolve to a shared sandbox dealer rather than your own dealership. Use them while you build: you can create vehicles, enquiries and customers freely without affecting real stock or real customers.

  • The sandbox is shared by every developer, so do not put real personal data in it and do not rely on its contents staying as you left them.
  • A test key acts as the sandbox's own user, not as one of your staff.
  • A secret test key follows your own dealership's plan: it needs a paid plan just as a live secret key does.
  • If the sandbox is unavailable, test keys are refused with 401 sandbox_unavailable.

Switching to production is a matter of swapping the test key for a live one. Nothing else about the request changes.

Who a key acts as​

A key belongs to the dealership, but it acts as a person: its owner, by default the staff member who created it. Every request runs as that user, so branch restrictions, activity logs and business rules behave exactly as they would for that person in the DMS.

A key's effective permissions are its scopes and its owner's current permissions in the DMS:

  • If the owner loses the DMS permission a scope needs, that scope stops working and the API answers 403 scope_not_permitted_for_owner. The rest of the key keeps working.
  • If the owner is deleted or banned, the whole key stops working with 401 key_owner_inactive.

The DMS shows these problems next to the key, and an administrator can move the key to another owner without changing the key itself. The permission each scope needs is listed on the Scopes page.

Optional restrictions​

Each key can be narrowed further in the DMS:

RestrictionApplies toEffect
BranchesAll keysThe key sees and writes only data for the chosen branches. Empty means all branches.
IP allowlistSecret keysRequests from any other IP address are refused with 403 ip_not_allowed.
Origin allowlistPublishable keys (required)Browser requests from any other origin are refused with 403 origin_not_allowed.
ExpiryAll keysAfter the expiry date the key is refused with 401 api_key_expired.

An IP allowlist is a good idea for any secret key used from servers with fixed addresses.

Rolling and revoking keys​

In Administration > Developers:

  • Roll replaces a key's secret. The old key is revoked immediately and a copy with a new secret is shown once. Update wherever the key is stored straight away; requests with the old key are refused with 401 api_key_revoked.
  • Revoke disables a key permanently.

Roll a key whenever it may have been exposed: committed to a repository, pasted into a ticket, used in a browser, or held by someone who has left.

Authentication errors​

StatuscodeMeaning
401missing_api_keyNo Authorization: Bearer header.
401invalid_api_keyThe key is malformed or unknown.
401api_key_revokedThe key was revoked or rolled, or the OAuth grant was revoked.
401api_key_expiredThe key is past its expiry date.
401key_owner_inactiveThe key's owner was deleted or banned.
403insufficient_scopeThe key does not have the scope this endpoint needs.
403scope_not_permitted_for_ownerThe key has the scope, but its owner no longer has the permission behind it.
403plan_not_entitledSecret keys, webhooks and OAuth need a paid plan.
403ip_not_allowedThe request came from an IP address outside the key's allowlist.
403origin_not_allowedThe browser origin is not on the publishable key's allowlist.
403secret_key_in_browserA secret key was used from a browser. Roll it.

The response body format is described in Requests and responses.