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.
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.
/api/v1/vehiclesList every vehicle on the authorised user's account.
vehicles:read/api/v1/vehicles/{id}Fetch a single vehicle including MOT, tax, insurance and service due dates.
vehicles:read/api/v1/vehiclesAdd a vehicle by registration. Triggers DVLA + DVSA auto-fill server-side.
vehicles:write/api/v1/vehicles/{id}Update mutable fields (label, insurance date, service date, service interval).
vehicles:write/api/v1/vehicles/{id}Remove a vehicle from the user's account.
vehicles:write/api/v1/vehicles/bulkAdd up to 50 registrations in a single request.
vehicles:writeFull 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:
- Discover the metadata
Once at integration time. Cache the response.
GET https://fleetkeep.co.uk/.well-known/oauth-authorization-server - 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 ofvehicles:read,vehicles:writeandoffline_access) and a CSRFstate. Includeoffline_accessin 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=... - User sees the consent screen
They review the scopes and your client name. They approve or deny.
- Exchange the code for tokens
Server-to-server.
code_verifierproves 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). Ifoffline_accesswas in the requested scope, also returns arefresh_token(30 days during partner beta). - Call the API with the access token
GET https://fleetkeep.co.uk/api/v1/vehicles Authorization: Bearer ACCESS_TOKEN Accept: application/json - 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.
- /.well-known/oauth-authorization-server, RFC 8414 OAuth metadata.
- /.well-known/oauth-protected-resource: RFC 9728 resource server descriptor.
- /.well-known/api-catalog: RFC 9727 API catalogue (linkset format).
- /.well-known/agent-skills/index.json: Cloudflare Agent Skills descriptor.
- /openapi.json: OpenAPI 3.1 specification.
- /llms-full.txt: full-text bundle for LLM ingestion.
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 →