Live updates
Polling every list endpoint on a timer works, but it means guessing at an
interval that is either too slow to feel live or too fast to be kind to
the rate limit. GET /v1/environments/{environment_id}/live-events is a
Server-Sent Events stream that tells you when to refetch, instead.
curl -N "$API/environments/$ENV/live-events" \
-H "Authorization: Bearer $TOKEN"
-N matters for curl — without it, output buffers and you will not see
frames as they arrive.
These are nudges, not data#
An event names what changed, never the new value. You still call the
matching GET endpoint to find out what changed to. That is what makes a
dropped event tolerable — delivery is best-effort, so a client has to
reconcile once when the stream opens and again after every reconnect,
rather than treating the stream as a complete history of everything that
happened while it was connected.
The frame shape#
Every frame's SSE event: field matches its payload's type, and
data: is one JSON object:
event: connector_status_changed
data: {"type":"connector_status_changed","charging_station_id":"...","connector_id":"...","status":"Occupied"}
The payload is deliberately flat rather than nested per event type — a
client only ever needs charging_station_id (and usually
connector_id) to know what to refetch, regardless of which kind of
event arrived. Eight event types exist today:
type | Carries, beyond charging_station_id | Means |
|---|---|---|
charging_station_connected | — | The station's OCPP connection came up. |
charging_station_disconnected | — | It went down. |
connector_status_changed | connector_id, status | A connector's status changed. |
transaction_started | connector_id, transaction_id | A session began. |
transaction_updated | connector_id, transaction_id | A session's meter values or state changed. |
transaction_ended | connector_id, transaction_id | A session finished. |
meter_values_updated | connector_id | New meter readings landed for a connector — coalesced, so this does not fire per sample. |
command_status_changed | command_id | A command you created moved to a new status — see Sending commands to a charger. |
A field the event type does not carry is simply absent from the JSON
object, not sent as null.
The stream does not stay open forever#
Access is checked once, at connect — the same user-role environment
check as everything else scoped to an environment. A stream that ran
indefinitely would outlive a membership revoked five minutes into it, so
it is closed after 30 minutes regardless. EventSource (or your own
reconnect loop) picking the connection back up re-runs that check, which
is also your cue to reconcile: refetch whatever you're tracking on every
reconnect, not just the first connect.
A comment frame arrives immediately on connect, before any real event —
harmless to an EventSource client, but it is what makes a quiet
environment's stream visibly open rather than looking stuck for the first
15 seconds. After that, a keep-alive comment arrives every 15 seconds on
its own, so a transparent proxy between you and the API does not time out
an idle connection.