Developer docs · v0.6

Fleetkeep API

A read/write API over a Fleetkeep user's vehicles, for partner integrations such as telematics platforms, vehicle marketplaces, accountancy tooling, and services that already hold a UK fleet operator's vehicle list and need to keep it synchronised. The end user grants your client access through a standard OAuth authorisation-code flow; you call /api/v1/vehicles with a Bearer token from there.

OAuth 2.1
Authorisation
Authorisation Code + PKCE
6
Endpoints
List · get · add · update · delete · bulk
3
Scopes
vehicles:read · vehicles:write · offline_access
60 rpm
Rate limit
Per access token

What you can do

Each Fleetkeep user has a list of vehicles. Each vehicle has a registration plate, a label, operational status, assigned driver notes, and four compliance dates: MOT, road tax, insurance renewal, and last service. The API lets your client read those records and write to them on behalf of the user, once they've consented through the OAuth flow.

GET /api/v1/vehicles

List every vehicle on the authorised user's account.

requires vehicles:read
GET /api/v1/vehicles/{id}

Fetch a single vehicle including MOT, tax, insurance and service due dates.

requires vehicles:read
POST /api/v1/vehicles

Add a vehicle by registration. Triggers DVLA + DVSA auto-fill server-side.

requires vehicles:write
PATCH /api/v1/vehicles/{id}

Update mutable fields (label, insurance date, service date, service interval).

requires vehicles:write
DELETE /api/v1/vehicles/{id}

Remove a vehicle from the user's account.

requires vehicles:write
POST /api/v1/vehicles/bulk

Add up to 50 registrations in a single request.

requires vehicles:write

Full request and response schemas, error codes and example payloads are in the OpenAPI 3.1 specification. All dates are ISO 8601 (YYYY-MM-DD). Vehicle plates are normalised server-side to upper-case with no spaces.

Authorisation

Fleetkeep is an OAuth 2.1 authorisation server. The flow is the standard authorisation-code grant with PKCE. There are no implicit, device, or password grants. Here is the request you make and what comes back at each step:

  1. Discover the metadata

    Once at integration time. Cache the response.

    GET https://fleetkeep.co.uk/.well-known/oauth-authorization-server
  2. Send the user to /auth/oauth2/authorize

    Browser redirect. Includes client_id, redirect_uri, code_challenge (SHA-256), code_challenge_method=S256, scope (any combination of vehicles:read, vehicles:write and offline_access) and a CSRF state. Include offline_access in the scope list if you want a refresh token returned at step 4.

    https://fleetkeep.co.uk/auth/oauth2/authorize?
      response_type=code
      &client_id=YOUR_CLIENT_ID
      &redirect_uri=https://yourapp.example/oauth/callback
      &scope=vehicles:read vehicles:write offline_access
      &code_challenge=...
      &code_challenge_method=S256
      &state=...
  3. User sees the consent screen

    They review the scopes and your client name. They approve or deny.

  4. Exchange the code for tokens

    Server-to-server. code_verifier proves you started the flow. The authorisation code expires after 10 minutes and is single-use.

    POST https://fleetkeep.co.uk/auth/oauth2/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code=...
    &redirect_uri=https://yourapp.example/oauth/callback
    &client_id=YOUR_CLIENT_ID
    &code_verifier=...

    Returns an access_token (1 hour). If offline_access was in the requested scope, also returns a refresh_token (30 days during partner beta).

  5. Call the API with the access token
    GET https://fleetkeep.co.uk/api/v1/vehicles
    Authorization: Bearer ACCESS_TOKEN
    Accept: application/json
  6. Refresh before the access token expires

    A successful refresh returns a new access token and a new refresh token; the old refresh token is invalidated immediately.

    POST https://fleetkeep.co.uk/auth/oauth2/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=refresh_token
    &refresh_token=...
    &client_id=YOUR_CLIENT_ID

Quick start with curl

Once you have an access token, listing a user's vehicles is one request:

curl https://fleetkeep.co.uk/api/v1/vehicles \
  -H "Authorization: Bearer $FLEETKEEP_ACCESS_TOKEN" \
  -H "Accept: application/json"

Adding a vehicle by registration triggers a DVLA + DVSA lookup server-side, so the response includes the make, model, MOT due date and tax due date populated automatically:

curl -X POST https://fleetkeep.co.uk/api/v1/vehicles \
  -H "Authorization: Bearer $FLEETKEEP_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  --data '{
    "registration": "BD15KFG",
    "label": "Transit white",
    "insuranceDueDate": "2026-08-22",
    "serviceIntervalDays": 42
  }'

Rate limits

Each access token gets 60 requests per minute, sliding window. The bulk-add endpoint counts as one request regardless of payload size. When you breach the limit you'll get an HTTP 429 with Retry-After header in seconds. Backoff and retry; do not loop.

If your integration is hitting the cap with a real partner workflow we can lift it; tell us. We don't lift it for testing.

Token lifetimes

  • Authorisation code: 10 minutes. Single-use. Bound to the PKCE code verifier.
  • Access token: 1 hour. Bearer-scoped.
  • Refresh token: 30 days during partner beta. Single-use; each refresh issues a new token and invalidates the previous one. Returned only when the requested scopes include offline_access.

Custom refresh windows. If your integration has a use case for a longer refresh window (up to 365 days), tell us when we set up your client. We do not currently issue tokens with no expiry; refresh-token rotation is the kill-switch when something leaks. Per-client configurable TTL is on the roadmap and depends on upstream support in @better-auth/oauth-provider.

Discovery and metadata URLs

Standards-compliant discovery: every URL below is publicly served and cacheable.

Errors

Error responses are JSON with an error field. The HTTP status conveys the class:

  • 400: invalid request shape (missing required field, malformed body).
  • 401: missing, expired or revoked Bearer token. If you hold a refresh token, refresh first; otherwise re-run the authorisation flow from step 2.
  • 403: token is valid but its scopes don't cover the operation.
  • 404: vehicle ID doesn't exist or doesn't belong to the authorised user.
  • 409: registration already on the user's account (POST /vehicles).
  • 429: rate-limited. Honour Retry-After.
  • 5xx: server fault. Retry with backoff.

Bulk endpoint: POST /api/v1/vehicles/bulk returns 207 Multi-Status with a per-item result array. Each item carries its own status code so partial successes are handled cleanly.

What we won't ship in v0.5

Three things are deliberately out of scope right now:

  • Webhooks for compliance status changes. If you need pushed updates when a vehicle's MOT expires or a tax renewal posts, polling once a day is the current pattern. Webhooks are on the v0.6+ roadmap.
  • Multi-partner / dynamic client registration. Partner clients are issued manually. RFC 7591 dynamic client registration isn't enabled.
  • Driver, contact or audit-log read endpoints. The API is vehicle-scoped; user contact details and the consent ledger stay with the Fleetkeep app for now.

Building on Fleetkeep?

Drop us a note describing the integration and we'll set up sandbox credentials within a working day.

Get in touch →