Skip to main content

Sending positions

Use this if your operatives' positions come from your own app or vehicle telematics. If they use the Tracker app, it does all of this for you.

Start​

POST /v1/journeys/{id}/start with { "position": { … } } when the operative sets off. It:

  • moves the visit to en_route;
  • works out the route and arrival time from the position (one routing request per journey);
  • sends the customer the "on the way" message.

Starting a journey that's already en route is harmless and returns its current state.

Positions​

POST /v1/journeys/{id}/positions with up to 100 fixes:

{
"positions": [
{ "lat": 53.8001, "lng": -1.5489, "accuracyM": 6, "headingDeg": 12, "speedMps": 8.9, "recordedAt": "2026-11-02T09:41:05Z" }
]
}
  • Batch. Buffer fixes and send them about every 10 seconds, or immediately for the first one. Each request costs both of us, and phones' batteries.
  • recordedAt is when the device took the fix; without it, the time we receive it. Fixes older than the latest one we have are ignored, so retrying a batch is safe.
  • Offline. Keep fixes while the device has no signal and send them when it's back, oldest first. The map catches up.
  • Arrival. A fix within 60 m of the destination marks the visit arrived.
  • Staleness. If nothing arrives for 3 minutes, the customer's page says the location hasn't updated. Journeys still en route after 4 hours expire.
  • Positions are only accepted while en_route. You'll get 409 invalid_state before start or after arrival, so stop sending.

Every response is the customer's current view (TrackingSnapshot), including the ETA, which your app can show the operative too.

Routes and arrival times. Tracker refreshes the route when the operative leaves it by more than 300 m, or when it gets old. In between, it works out the arrival time from how far along the route they are, so most uploads don't call a routing provider at all.

Arrive​

POST /v1/journeys/{id}/arrive when the operative arrives, if they haven't already been marked arrived by position. The tracking page stops showing a location.

Follow a visit live​

The customer's page uses two public endpoints, and your own screens can too:

  • GET /api/track/{token} returns the current TrackingSnapshot.
  • GET /api/track/{token}/live is a WebSocket. It sends a full snapshot message first, then smaller position messages as the operative moves, and a new snapshot when anything else changes. Send the text ping about every 25 seconds; the server replies pong without waking the journey, and a missing pong means you should reconnect. The socket closes when the visit ends.
const ws = new WebSocket(`wss://<your Tracker host>/api/track/${token}/live`);
ws.onmessage = (e) => {
if (e.data === 'pong') return;
const message = JSON.parse(e.data);
if (message.type === 'snapshot') render(message.snapshot);
else update(message.position, message.eta);
};
setInterval(() => ws.readyState === WebSocket.OPEN && ws.send('ping'), 25_000);