Skip to main content

HTTP API Reference

lplex exposes a REST + SSE API for reading frames, managing sessions, and discovering devices. CORS is enabled (Access-Control-Allow-Origin: *).

Endpoints

Ephemeral streaming

GET /events

Opens an SSE stream of live CAN frames. No session, no replay.

Query parameters:

ParamTypeDescription
pgnuint32Filter by PGN (repeatable, OR'd)
exclude_pgnuint32Exclude PGN from results (repeatable, OR'd)
manufacturerstringFilter by manufacturer name (repeatable, OR'd)
instanceuint8Filter by device instance (repeatable, OR'd)
namestringFilter by 64-bit CAN NAME hex (repeatable, OR'd)

Different filter types are AND'd together.

Response: Content-Type: text/event-stream

data: {"seq":1234,"ts":"2026-03-06T10:15:32.123Z","prio":2,"pgn":129025,"src":10,"dst":255,"data":"5A1F2B3C4D5E6F70"}

data: {"seq":1235,"ts":"2026-03-06T10:15:32.145Z","prio":3,"pgn":130306,"src":22,"dst":255,"data":"01A4060000030000"}

Example:

curl -N "http://inuc1.local:8089/events?pgn=129025&manufacturer=Garmin"

Buffered sessions

PUT /clients/{clientId}

Create or reconnect a buffered session. The client ID must be 1-64 alphanumeric characters, hyphens, or underscores.

Request body:

{
"buffer_timeout": "PT5M",
"filter": {
"pgn": [129025, 130306],
"exclude_pgn": [60928],
"manufacturer": ["Garmin"],
"instance": [0],
"name": ["0x00A1B2C3D4E5F600"]
}
}

Only buffer_timeout is required. filter is optional.

Response: 200 OK

{
"client_id": "myapp",
"seq": 5000,
"cursor": 4800,
"devices": [
{"src": 10, "name": "0x00A1B2C3D4E5F600", "manufacturer": "Garmin", ...}
]
}
FieldDescription
seqCurrent head sequence number
cursorWhere this client will resume from (last ACK'd + 1)
devicesSnapshot of known devices

GET /clients/{clientId}/events

Opens an SSE stream for a buffered session. Replays buffered frames from the cursor, then transitions to live streaming.

Response: Content-Type: text/event-stream (same format as GET /events)

Returns 404 if the session does not exist or has expired.


PUT /clients/{clientId}/ack

Acknowledge receipt of frames up to the given sequence number. Advances the session cursor.

Request body:

{
"seq": 1500
}

Response: 204 No Content


Frame transmission

note

Both /send and /query are disabled by default. Enable them with -send-enabled or the send.enabled config option. Use send.rules to define ordered allow/deny rules with PGN ranges and CAN NAME lists — see Configuration.

POST /send

Send a CAN frame to the bus.

Request body:

{
"pgn": 129025,
"src": 10,
"dst": 255,
"prio": 2,
"data": "0102030405060708"
}
FieldTypeDescription
pgnuint32PGN number
srcuint8Source address
dstuint8Destination (255 for broadcast)
priouint8Priority (0-7, lower is higher priority)
datastringHex-encoded payload

Response: 202 Accepted


POST /query

Send an ISO Request (PGN 59904) asking devices to transmit a specific PGN, then wait for the response. This is the primary way to query on-demand PGNs (e.g., address claim, product info) from specific devices.

Request body:

{
"pgn": 129025,
"dst": 255,
"timeout": "PT5S"
}
FieldTypeDescription
pgnuint32PGN to request (required)
dstuint8Destination address (default 255 for broadcast)
timeoutstringISO 8601 duration to wait for response (default PT2S)

Response: 200 OK — the first matching frame as JSON (same format as SSE frame events).

Error responses:

  • 400 Bad Request — missing PGN or invalid timeout
  • 503 Service Unavailable — tx queue full
  • 504 Gateway Timeout — no response within timeout

Example:

# Request position from all devices
curl -X POST http://localhost:8089/query \
-H 'Content-Type: application/json' \
-d '{"pgn": 129025}'

# Request product info from device at address 10
curl -X POST http://localhost:8089/query \
-H 'Content-Type: application/json' \
-d '{"pgn": 126996, "dst": 10, "timeout": "PT5S"}'

Device discovery

GET /devices

Returns a snapshot of all discovered NMEA 2000 devices.

Response: 200 OK

[
{
"src": 10,
"name": "0x00A1B2C3D4E5F600",
"manufacturer": "Garmin",
"manufacturer_code": 229,
"device_class": 25,
"device_function": 130,
"device_instance": 0,
"unique_number": 123456,
"product_code": 1234,
"model_id": "GPS 19x HVS",
"software_version": "5.60",
"model_version": "1",
"model_serial": "ABC123",
"first_seen": "2026-03-06T10:00:00Z",
"last_seen": "2026-03-06T10:15:32Z",
"packet_count": 45023,
"byte_count": 360184
}
]

Last-known values

GET /values

Returns the last-seen frame for each (device, PGN) pair.

Query parameters: Same as GET /events (pgn, manufacturer, instance, name).

Response: 200 OK

[
{
"name": "0x00A1B2C3D4E5F600",
"src": 10,
"manufacturer": "Garmin",
"model_id": "GPS 19x HVS",
"values": [
{
"pgn": 129025,
"ts": "2026-03-06T10:15:32.123Z",
"data": "5A1F2B3C4D5E6F70",
"seq": 1234
}
]
}
]

GET /values/decoded

Same as /values but with decoded PGN fields added.

Response: 200 OK

[
{
"name": "0x00A1B2C3D4E5F600",
"src": 10,
"manufacturer": "Garmin",
"model_id": "GPS 19x HVS",
"values": [
{
"pgn": 129025,
"ts": "2026-03-06T10:15:32.123Z",
"data": "5A1F2B3C4D5E6F70",
"seq": 1234,
"decoded": {
"latitude": 47.6062,
"longitude": -122.3321
}
}
]
}
]

Replication status

GET /replication/status

Returns replication connection and sync state. Only available when replication is configured.

Response: 200 OK

{
"connected": true,
"instance_id": "boat-001",
"local_head_seq": 50000,
"cloud_cursor": 49950,
"holes": [
{"start": 10000, "end": 10500}
],
"live_lag": 50,
"backfill_remaining_seqs": 500,
"last_ack": "2026-03-06T10:15:30Z"
}

Health and metrics

GET /healthz

Health check endpoint.

Response: 200 OK

{
"status": "ok"
}

Reports unhealthy when the CAN bus has been silent longer than bus-silence-threshold.

GET /metrics

Prometheus metrics endpoint.


Frame JSON format

Every frame in the SSE stream and API responses uses this format:

{
"seq": 1234,
"ts": "2026-03-06T10:15:32.123Z",
"prio": 2,
"pgn": 129025,
"src": 10,
"dst": 255,
"data": "5A1F2B3C4D5E6F70"
}
FieldTypeDescription
sequint64Monotonically increasing sequence number (starts at 1)
tsstringRFC 3339 timestamp with millisecond precision
priouint8CAN priority (0-7)
pgnuint32NMEA 2000 Parameter Group Number
srcuint8Source address (0-253)
dstuint8Destination address (255 = broadcast)
datastringHex-encoded payload bytes

Cloud API

The cloud server (lplex-cloud) exposes a similar API namespaced by instance. See Cloud Overview for the full cloud endpoint reference.