The Travr API
Your vehicles, positions, trips, alerts and zones as a clean REST API, and events pushed to your webhooks. For your own dashboards, your fleet-management or insurance platforms, and partners who integrate with Travr.
Base URL https://api.travr.pro/v1 · version 2026-10-01 · included in Travr Fleet and dealer accounts at no extra cost.
Three minutes to your first call
- 1. Create a key. Sign in to the Travr Admin console at admin.travr.pro → Developers → New API key. Pick the scopes it needs. The key is shown once — Travr keeps only a fingerprint.
- 2. Send it as a bearer token.
Authorization: Bearer trv_live_…on every request. The key sees your company and every account under it. - 3. Read. Lists return
{ data, has_more, next_cursor }; passcursorback for the next page. Times are ISO 8601 in UTC, distances in km, speeds in km/h. - 4. Subscribe. Add a webhook (console or API) and verify each delivery's signature — see below.
curl https://api.travr.pro/v1/me \
-H "Authorization: Bearer trv_live_YOUR_KEY"curl "https://api.travr.pro/v1/vehicles?limit=50" \
-H "Authorization: Bearer trv_live_YOUR_KEY"curl "https://api.travr.pro/v1/vehicles/VEHICLE_ID/positions?from=2026-10-01T00:00:00Z&to=2026-10-02T00:00:00Z" \
-H "Authorization: Bearer trv_live_YOUR_KEY"curl -X POST https://api.travr.pro/v1/vehicles/VEHICLE_ID/commands \
-H "Authorization: Bearer trv_live_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"type":"locate"}'Keys and scopes
Per company, created and revoked by your admins. Scopes limit what a key may read or do. Keys can carry an expiry date. Every key is logged to your audit trail.
Rate limits
600 requests per minute per key. Every response carries X-RateLimit-Limit, -Remaining and -Reset; over the limit you get 429 with Retry-After. Prefer webhooks to polling.
Errors
{ error: { code, message, details? } } with the matching HTTP status: 400 bad request, 401 unauthorised, 403 forbidden (scope or account), 404 not found, 429 rate limited. Quote X-Request-Id when you write to us.
| Scope | Allows |
|---|---|
| vehicles:read | Vehicles and their last known position, tracker and status |
| positions:read | Position history, up to 7 days per call |
| trips:read | Trips with distance, duration, speeds and GeoJSON routes |
| events:read | Alerts and events (movement, zones, power, battery, immobiliser…) |
| geofences:read | Zones (circles and polygons) |
| commands:locate | Ask a tracker for a fresh fix. Immobilising is never available through the API |
| webhooks:manage | Create and manage webhooks through the API (they can also be set up in the console) |
Events, pushed to you
One signed JSON POST per event, retried for six hours if your endpoint is down. Up to ten webhooks per company; each picks the events it wants.
| trip.started | A vehicle set off |
| trip.ended | A trip closed — distance, duration, start and end |
| alert.raised | Any alert: movement without key, power cut, low battery, overspeed, SOS, tracker offline… |
| geofence.entered | A vehicle entered a zone |
| geofence.exited | A vehicle left a zone |
| vehicle.immobilised | The immobiliser engaged |
| vehicle.released | The immobiliser was released |
| command.completed | A command you sent finished (acknowledged, failed or expired) |
- Answer
2xxwithin 10 seconds; do the work afterwards. - Retries after 1, 5, 15, 60 and 360 minutes; 25 failures in a row switch the webhook off (switch it back on in the console).
- Deliveries are at-least-once: ignore an
idyou have already processed. - Headers:
X-Travr-Event,X-Travr-Delivery,X-Travr-Signature.
Create one through the API (or in the console):
curl -X POST https://api.travr.pro/v1/webhooks \
-H "Authorization: Bearer trv_live_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/travr","events":["trip.ended","alert.raised"]}'What arrives:
{
"id": "8b04b737-0a8f-44fe-a3fd-6114e1704e2b",
"event": "trip.ended",
"created_at": "2026-10-01T11:03:23.013Z",
"attempt": 1,
"data": {
"vehicle": { "id": "…", "name": "CK0202", "registration": "CK0202", "company_id": "…" },
"trip": { "id": "…", "started_at": "…", "ended_at": "…", "distance_km": 12.4, "duration_s": 1860,
"start": { "latitude": 51.52, "longitude": -0.26, "address": null },
"end": { "latitude": 51.53, "longitude": -0.20, "address": null } }
}
}Verify the signature — t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body" with your webhook secret> — on the raw request body, before parsing it:
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyTravr(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(signatureHeader.split(',').map((kv) => kv.split('=')));
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!(age < 300)) return false; // older than five minutes: replay
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Endpoints
Generated from the OpenAPI document. Path parameters are UUIDs; list endpoints accept limit and cursor.
Account
/me— The key and its companyResponses: 200 OK · 401
/companies— Companies the key can seeResponses: 200 OK
Vehicles
/vehicles— List vehiclesVehicles in scope with their tracker and last known position. Scope vehicles:read.
| company_id | string (uuid) | Restrict to one company in scope (default all) |
| status | active | inactive | service_mode | suspended | decommissioned | |
| type | ||
| group | string | Exact group name |
| registration | string | Registration / plate (spaces ignored, case-insensitive) |
| q | string | Free text over name, registration and VIN |
| updated_since | string (date-time) | Only vehicles changed after this time |
| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK · 401 · 403
/vehicles/{id}— One vehicleResponses: 200 OK · 404
Positions
/vehicles/{id}/positions— Position historyEvery fix the tracker reported in the window, oldest first by default. The window may be at most 7 days (default: the last 24 hours); up to 1000 fixes per page. Scope positions:read.
| from | string (date-time) | |
| to | string (date-time) | |
| order | asc | desc | (default asc) |
| limit | integer | (default 200) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK · 400
Trips
/trips— List tripsTrips started in the window (default the last 30 days), newest first. Scope trips:read.
| company_id | string (uuid) | Restrict to one company in scope (default all) |
| from | string (date-time) | |
| to | string (date-time) | |
| status | open | closed | |
| include | route | route adds the GeoJSON route to every trip |
| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK
/vehicles/{id}/trips— Trips of one vehicle| from | string (date-time) | |
| to | string (date-time) | |
| status | open | closed | |
| include | route | |
| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK
/trips/{id}— One trip, with its route| include | none | none leaves the route out |
Responses: 200 OK · 404
Events
/events— Alerts and eventsEvents raised in the window (default the last 7 days), newest first. Scope events:read.
| company_id | string (uuid) | Restrict to one company in scope (default all) |
| vehicle_id | string (uuid) | |
| type | string | Comma-separated list of event types |
| severity | info | warning | critical | |
| acknowledged | boolean | |
| from | string (date-time) | |
| to | string (date-time) | |
| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK
Geofences
/geofences— List zones (geofences)| company_id | string (uuid) | Restrict to one company in scope (default all) |
| active | boolean | |
| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK
/geofences/{id}— One zoneResponses: 200 OK · 404
Commands
/vehicles/{id}/commands— Ask the tracker for a fresh positionQueues a locate command for the vehicle's tracker and answers 202 at once; poll GET /commands/{id} or subscribe to the command.completed webhook. A locate already pending for the same vehicle in the last two minutes is returned instead of a new one (reused: true). Scope commands:locate. Immobilise and release answer 403.
Responses: 202 Queued · 403 · 409 The vehicle has no tracker
/commands/{id}— Command statusResponses: 200 OK · 404
Webhooks
/webhooks— List webhooksResponses: 200 OK
/webhooks— Create a webhookThe response includes the signing secret — the only time it is returned by the API (it stays visible in the Admin console). At most 10 webhooks per company. Scope webhooks:manage.
Responses: 201 Created · 400 · 409 Limit reached
/webhooks/{id}— One webhookResponses: 200 OK · 404
/webhooks/{id}— Update a webhookAny of url, events, description, active. Switching a webhook back on resets its failure count.
Responses: 200 OK · 400
/webhooks/{id}— Delete a webhookResponses: 204 Deleted · 404
/webhooks/{id}/test— Send a test eventQueues a webhook.test delivery; it goes out within a minute.
Responses: 202 Queued · 409 The webhook is switched off
/webhooks/{id}/deliveries— Recent deliveries| limit | integer | (default 50) |
| cursor | string | next_cursor from the previous page |
Responses: 200 OK
Questions, a partner integration, or something missing? Write to sales@travr.pro. Immobilising a vehicle is deliberately not in the API: it stays behind a person and a PIN in the Travr dashboard and app.