Skip to main content

Streaming with SSE (live telemetry)

Instead of refreshing every few seconds to ask "where is the vehicle now?", you open a single connection and the server pushes updates the moment they happen — GPS positions, trip events, anything live. That's Server-Sent Events (SSE).

Think of it like a live news feed: you tune in once, and updates appear on screen as they're broadcast. No refresh, no polling, no delay.

Looking for a specific endpoint?

The subscription endpoints (/api/v1/monitoring/unit/{id}, /api/v1/monitoring/vehicle/{id}/primary) and everything else in the Monitoring + SSE collections are listed in the API Reference with full parameters and response shapes.

How it works — the mental model

There are three things going on, in this order:

┌─────────────┐ 1. open stream ┌──────────────┐
│ your code │ ─────────────────────────────► │ Destiny SSE │
│ (or browser)│ ◄──── stream_id event ──────── │ server │
└─────┬───────┘ └──────┬───────┘
│ │
│ 2. subscribe per vehicle/unit (REST POST) │
│ /api/v1/monitoring/unit/{id} │
│ ─────────────────────────────────────────────►│
│ │
│ ◄──── gps / event messages stream in ─────────│
│ ◄──── periodic "ping" event ──────────────────│
│ │
│ 3. answer the ping (REST POST) │
│ /sse/pong/{stream_id} │
│ ─────────────────────────────────────────────►│

You open one SSE connection and can subscribe to many vehicles/units over it. The server sends a stream_id after you connect — you'll need it to subscribe and to answer pings.

Try it in your terminal (5 minutes, real API)

This uses the live API at https://www.acmdestiny.net. You'll need a Destiny username and password.

1. Get your bearer token

The Destiny login is unusual: the token comes back in the Authorization response header, not the body. The -D - flag below dumps the response headers so you can grab it.

curl -D - -s -o /dev/null \
-X POST https://www.acmdestiny.net/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"YOUR_USERNAME","password":"YOUR_PASSWORD"}' \
| grep -i '^authorization:'

You'll see something like:

Authorization: bearer eyJ0eXAiOiJKV1...

Copy the long string after bearer — that's YOUR_TOKEN for the next steps.

The token rotates every few minutes. In a real integration, read the Authorization response header on every call and update your stored token when a new one comes back (old ones stay valid for 7 days).

2. Find a unit you want to watch

curl -s https://www.acmdestiny.net/api/v1/units/listing \
-H "Authorization: bearer YOUR_TOKEN" | head -c 800

Pick a unit_id from the response. We'll call it UNIT_ID below.

3. Open the live stream — leave this terminal running

curl --no-buffer -N \
-H "Authorization: bearer YOUR_TOKEN" \
https://www.acmdestiny.net/sse/connect

--no-buffer -N tells curl to flush output as data arrives. You should immediately see a stream_id event, then periodic ping events:

event: stream_id
data: {"type":"stream_id","stream_id":"abcd1234..."}

event: ping
data: {"type":"ping"}

Copy the stream_id.

4. In a SECOND terminal, subscribe to that unit

curl -X POST \
-H "Authorization: bearer YOUR_TOKEN" \
https://www.acmdestiny.net/api/v1/monitoring/unit/UNIT_ID

Within seconds, the first terminal starts receiving gps events as the unit reports its position:

event: gps
data: {"type":"gps","telemetry":{"gps":{"latitude":-26.10,"longitude":28.05,...}}}

That's it — you're watching live telemetry stream in real time.

5. Keep the connection alive (ping/pong)

The server sends a ping event once every minute. Your client has 10 seconds to answer with a pong POST. If it doesn't, the server closes the SSE connection entirely — you can't just resume. You have to:

  1. Open a brand-new SSE connection (back to step 3).
  2. Re-subscribe to every unit/vehicle you were watching (back to step 4).

In a real app the pong looks like:

curl -X POST -H "Authorization: bearer YOUR_TOKEN" \
https://www.acmdestiny.net/sse/pong/STREAM_ID

…fired every time a ping arrives.

Try it in your browser (DevTools console)

Open any page on acmdestiny.net (so the session cookie is set), open DevTools → Console, and paste:

const base = 'https://www.acmdestiny.net';
const token = 'YOUR_TOKEN'; // from step 1 above
const unitId = 1; // from step 2

const es = new EventSource(`${base}/sse/connect`);
let streamId = null;

es.onmessage = async (e) => {
const msg = JSON.parse(e.data);
if (msg.type === 'stream_id') {
streamId = msg.stream_id;
console.log('stream_id:', streamId);
// now subscribe to the unit
await fetch(`${base}/api/v1/monitoring/unit/${unitId}`, {
headers: { Authorization: `bearer ${token}` },
});
} else if (msg.type === 'ping') {
await fetch(`${base}/sse/pong/${streamId}`, {
method: 'POST',
headers: { Authorization: `bearer ${token}` },
});
} else if (msg.type === 'gps') {
const g = msg.telemetry?.gps;
console.log(`live: ${g.latitude}, ${g.longitude}`);
}
};
es.onerror = (err) => console.error('SSE error — browser will auto-reconnect', err);

Watch the console — every GPS update from that unit will print live.

Important browser quirk: the native EventSource API doesn't let you set custom headers (so no Authorization: bearer … on the SSE call itself). The snippet above relies on a session cookie already being set by acmdestiny.net. If you need bearer auth on the stream from a non-cookie context, use a fetch-based polyfill like eventsource-polyfill that supports custom headers, or pass the token as a query parameter (check the API Reference for the exact pattern).

Things to know before shipping it

  • One connection, many subscriptions — but with a practical ceiling. You don't need a separate SSE connection per vehicle — open one, subscribe to all the units/vehicles you care about over it. SSE is designed for a single user watching a manageable set live (a dispatcher with a map showing 5–50 vehicles, a control-room tab, a driver-app screen). It is not the right tool for bulk live telemetry across an entire fleet of hundreds or thousands of units — see "When SSE isn't the right tool" below. Browsers also cap you at 6 SSE connections per origin (shared across tabs).
  • The token rotates. Read the Authorization response header after every REST call and update your stored token. Old tokens stay valid for 7 days.
  • Answer pings within 10 seconds. The server sends a ping every minute and expects a pong POST within 10 seconds. Miss it once and the connection is closed — you'll need to open a new SSE connection and re-subscribe to every unit/vehicle.
  • Browsers may freeze background tabs. When a tab is inactive (or the user's computer goes to sleep), the browser can pause your JS — pings stop being answered and the server drops you. Detect the disconnect (onerror) and reconnect + re-subscribe when the tab becomes visible again (document.visibilitychange).
  • It auto-reconnects in the browser. The native EventSource retries on disconnect automatically. In Node or a server-side client, you may need to reconnect yourself (and re-subscribe to your units).
  • SSE is one-way. Subscriptions and pongs go through normal REST calls; only the event stream is push.

When SSE isn't the right tool

  • You want yesterday's trip — use the History Query API, not SSE.
  • You want a snapshot right now — a single GET /units/{id}/telemetry call is simpler than opening a stream.
  • You want bulk live telemetry across hundreds or thousands of units at once — that's outside SSE's design. Use LDBS (Live Data Bridge Services) instead. LDBS is a separate ACM service built specifically for high-volume, fleet-wide live data feeds (think: piping every event into your data warehouse, or running fleet-wide analytics in real time). Contact your ACM account manager to get an LDBS connection provisioned.

Use SSE when you genuinely want live, ongoing updates for a user watching a specific set of vehicles — a dashboard map, a control room view, a real-time alert handler. Use LDBS for fleet-wide programmatic streaming.

Not comfortable writing this yourself?

Paste this URL into Claude or Claude Code along with what you want to build:

https://api.acmtrack.com/llms-full.txt

Example prompt: "Here's the ACM Destiny API: https://api.acmtrack.com/llms-full.txt. I have a Destiny username and password and the ID of one of my vehicles. Write me a Python script that opens an SSE stream, subscribes to that vehicle, prints each GPS update with a timestamp, and answers pings automatically."

The AI gets the full API context (this guide included) and writes the integration for you.