MeshDash Docs
R2.0
/
Home API Reference Core API Endpoints

Core API Endpoints

API Reference api rest endpoints nodes packets messages channels telemetry traceroute waypoints neighbors sse geocode console alert
Complete reference for all core REST endpoints — status, nodes, packets, messages, channels, and more.

All endpoints use JSON request/response bodies unless noted. Protected endpoints require the access_token cookie. See Authentication for login details.

Interactive Swagger UI is available at http://device-ip:8000/docs when MeshDash is running.

System Status

GET /api/status

Returns live system health. Also silently refreshes JWT session if it has less than half its lifetime remaining. No auth required.

GET /api/status

Example response:

{
  "api_status": "online",
  "connection_status": "Connected",
  "is_system_ready": true,
  "local_node_info": {
    "node_id": "!aabbccdd",
    "node_num": 2864434397,
    "long_name": "My Node",
    "short_name": "NODE",
    "hardware_model_string": "TLORA_V2_1_1P6",
    "firmware_version": "2.3.14.5678",
    "battery_level": 87,
    "lora_region": "EU_868",
    "lora_hop_limit": 3,
    "channel_count": 2,
    "last_updated": 1705329000.123
  },
  "last_error": null,
  "public_mode": false
}

GET /api/stats

Session statistics since last startup.

{
  "packets_received_session": 1420,
  "text_messages_session": 47,
  "position_updates_session": 203,
  "telemetry_reports_session": 612,
  "nodes_seen_session": 8,
  "channels_seen_session": 2,
  "elapsed_time_session": 86400.0,
  "start_time": 1705242600.0
}

GET /api/system/connection_history?limit=60

Returns timestamped connection log entries. Used for the connection uptime graph.

limit (int)
Number of records to return. Default: 60. Records are ordered oldest-first.

Example response item:

{"timestamp": 1705329000.0, "value": 0.9, "status": "Connected"}

value: 0.9 = connected, 0.5 = transitioning/reconnecting, 0.1 = disconnected.

Nodes

GET /api/nodes

All nodes currently known to MeshDash (in-memory dict, populated from DB on startup + live packets).

GET /api/nodes

Returns a dict keyed by node ID (!xxxxxxxx):

{
  "!aabbccdd": {
    "node_id": "!aabbccdd",
    "node_num": 2864434397,
    "long_name": "Alpha Base",
    "short_name": "ALPH",
    "hw_model": "TLORA_V2_1_1P6",
    "isLocal": true,
    "snr": 8.5,
    "rssi": -82,
    "lastHeard": 1705329000,
    "battery_level": 87,
    "voltage": 3.95,
    "latitude": 51.5074,
    "longitude": -0.1278,
    "altitude": 12,
    "user": { "longName": "Alpha Base", "shortName": "ALPH", "macaddr": "..." },
    "deviceMetrics": { "batteryLevel": 87, "voltage": 3.95, "channelUtilization": 2.1, "airUtilTx": 0.3 },
    "source": "RF",
    "source_confidence": 0.92
  }
}

GET /api/nodes/{node_id}

Single node by its hex ID. Returns 404 if not found.

GET /api/nodes/!aabbccdd

GET /api/nodes/{node_id}/history/{table_name}

Historical data for a specific node from the database.

table_name
positions | telemetry | packets
limit (int)
Max records. Default: 1000.
start (float)
Unix timestamp lower bound.
end (float)
Unix timestamp upper bound.
GET /api/nodes/!aabbccdd/history/telemetry?limit=200&start=1705200000

GET /api/nodes/{node_id}/count/{item_type}

Count of records for a node in the database. Useful for analytics and pagination.

item_type
messages_sent | positions | telemetry
start / end (float)
Optional Unix timestamp range filter.
GET /api/nodes/!aabbccdd/count/messages_sent
→ {"node_id": "!aabbccdd", "item_type": "messages_sent", "count": 47}

GET /api/local_node/full

Comprehensive info about the local radio, including LoRa config, WiFi SSID, Bluetooth state, and all operational parameters read directly from the device.

{
  "node_id": "!aabbccdd",
  "node_num": 2864434397,
  "hardware_model_string": "TLORA_V2_1_1P6",
  "firmware_version": "2.3.14.5678",
  "long_name": "Alpha Base",
  "short_name": "ALPH",
  "latitude": 51.5074,
  "longitude": -0.1278,
  "altitude": 12,
  "battery_level": 87,
  "voltage": 3.95,
  "channel_utilization": 2.1,
  "air_util_tx": 0.3,
  "uptime_seconds": 86400,
  "lora_region": "EU_868",
  "lora_hop_limit": 3,
  "lora_tx_power": 20,
  "lora_tx_enabled": true,
  "wifi_ssid": "HomeNetwork",
  "bluetooth_enabled": true,
  "node_info_broadcast_secs": 900,
  "position_broadcast_secs": 300,
  "gps_mode": "ENABLED",
  "role": "CLIENT",
  "region": "EU_868",
  "max_channels": 8,
  "nodedb_count": 12
}

Returns a stub {"status": "connecting"} when in WebSerial mode and the radio is still initialising.

Packets

GET /api/packets?limit=50

Returns packets from the in-memory RAM buffer (most recent first). Fastest endpoint for the live feed.

limit (int)
Max packets to return. Default: 50. Cannot exceed MAX_PACKETS_MEMORY.

GET /api/packets/history?limit=100

Returns packets from the SQLite database. Includes fully decoded payloads, source detection results, and signal data.

limit (int)
Records to return. Default: 100.

Each packet includes a source field (RF | MQTT | UNKNOWN | LOCAL) and source_confidence (0–1) derived from the packet source detection engine.

Messages

GET /api/messages/history

Returns text message history from the database.

from_id (str)
Filter by sender node ID.
to_id (str)
Filter by recipient. Use ^all for broadcast channel messages.
channel (int)
Filter by channel index. Ignored when to_id is a specific node (not ^all).
limit (int)
Records to return. Default: 100.
# All messages on channel 0
GET /api/messages/history?channel=0&limit=50

# Direct messages to/from a specific node
GET /api/messages/history?from_id=!aabbccdd&to_id=!11223344

Each message includes a status field: SENT, DELIVERED, BROADCAST, or FAILED. Status is updated asynchronously when ACK/NACK routing packets are received.

POST /api/messages — Send a Message (Auth Required)

POST /api/messages
Content-Type: application/json

{
  "message": "Hello from the dashboard!",
  "destination": "^all",
  "channel": 0
}
message (str, required)
Text to send. No length limit enforced at API layer; Meshtastic firmware limits apply (~228 bytes).
destination (str)
Node ID (!xxxxxxxx) for direct message, or ^all for broadcast. Default: ^all.
channel (int)
Channel index (0–7). Default: 0.

Example response:

{
  "status": "broadcast",
  "channel": 0,
  "packet_id": 3141592653,
  "timestamp": 1705329000
}

status will be broadcast for ^all or sent for direct messages. Delivery updates arrive later via the SSE stream as message_status_update events.

Returns 503 if is_system_ready is false. Always check /api/status before sending.

Channels

GET /api/channels

Returns all configured radio channels read directly from the connected device.

[
  {
    "index": 0,
    "name": "LongFast",
    "role": "PRIMARY",
    "psk": "AQ==",
    "uplink": false,
    "downlink": false
  },
  {
    "index": 1,
    "name": "MyChannel",
    "role": "SECONDARY",
    "psk": "base64encodedpsk==",
    "uplink": false,
    "downlink": false
  }
]

PSK is returned as base64. Returns 503 if the radio is not connected, unless in WebSerial mode (returns cached data from local_node_info).

Neighbors

GET /api/neighbors

Returns all neighbor table entries from the database. Neighbours are populated from NEIGHBOR_INFO_APP packets.

[
  {
    "node_id": "!aabbccdd",
    "neighbor_id": "!11223344",
    "snr": 7.25,
    "last_seen": "2024-01-15 14:30:00"
  }
]

Traceroutes

GET /api/traceroutes?limit=50

Returns stored traceroute results from the database.

POST /api/traceroute/run — Run a Traceroute (Auth Required)

Sends a live traceroute request to a target node and waits up to 60 seconds for the response.

POST /api/traceroute/run
Content-Type: application/json

{
  "node_id": "!11223344",
  "hop_limit": 5
}

Example response:

{
  "target": "!11223344",
  "origin": "!aabbccdd",
  "rssi": -90,
  "snr": 5.25,
  "hops_used": 2,
  "direct_link": false,
  "path_to": [
    {"from": "!aabbccdd", "to": "!99887766", "snr": 7.5},
    {"from": "!99887766", "to": "!11223344", "snr": 4.75}
  ],
  "path_back": [
    {"from": "!11223344", "to": "!99887766", "snr": 4.25},
    {"from": "!99887766", "to": "!aabbccdd", "snr": 6.0}
  ],
  "timestamp": 1705329000.0
}

Returns 504 if no response is received within 60 seconds.

Waypoints

GET /api/waypoints

Returns all waypoints received from the mesh (from WAYPOINT_APP packets).

[
  {
    "from_id": "!aabbccdd",
    "waypoint_id": 123,
    "name": "Camp Alpha",
    "latitude": 51.5074,
    "longitude": -0.1278,
    "description": "Base camp",
    "timestamp": 1705329000.0
  }
]

Hardware Logs

GET /api/hardware_logs?limit=50

Returns hardware event logs (Admin packet payloads, GPIO events, etc.).

Metrics & Analytics

GET /api/metrics/averages?limit=100

Returns SNR/RSSI averages computed every 5 minutes across all non-local nodes.

{
  "most_recent": {
    "timestamp": 1705329000.0,
    "average_snr": 6.8,
    "average_rssi": -88.5,
    "node_count": 7
  },
  "history": [...]
}

GET /api/counts/totals

Database-wide record counts.

{
  "total_messages": 1247,
  "total_positions": 8934,
  "total_telemetry": 23401
}

Geocoding (Reverse)

GET /api/geocode?lat={lat}&lon={lon}

Reverse geocodes a coordinate using Nominatim (OpenStreetMap). Results are cached to disk (lat_lon_to_location.json) to respect the 1 req/s rate limit.

GET /api/geocode?lat=51.5074&lon=-0.1278

{
  "cached": false,
  "key": "51.5074,-0.1278",
  "short": "Palace of Westminster, Westminster",
  "full": "Palace of Westminster, ...",
  "city": "London",
  "county": "Greater London",
  "country": "United Kingdom",
  "postcode": "SW1A 0AA"
}

Console (Meshtastic CLI Bridge)

POST /api/console — Auth Required

Executes a meshtastic CLI-style command directly against the connected radio. The full command string is parsed and dispatched via the Meshtastic Python SDK. Runs in a thread with a 30-second timeout.

POST /api/console
Content-Type: application/json

{"command": "meshtastic --info"}

Response is plain text. Supported commands include:

FlagDescription
--infoPrint owner name, node count, firmware
--nodesTable of all nodes with SNR and last heard
--channelsChannel list
--set section.key valueSet a localConfig field
--get fieldGet a config field
--set-owner "Long Name"Set long name
--set-owner-short "SHRT"Set short name
--setlat/--setlon/--setaltSet fixed GPS position
--remove-positionClear fixed position, re-enable GPS
--ch-index N --ch-add "Name"Add a channel
--ch-index N --ch-delDelete/disable a channel
--ch-index N --ch-set key valSet channel setting
--sendtext "msg" --dest !idSend a text message
--traceroute !idRun a traceroute
--gpio-wrb PIN STATE --dest !idWrite remote GPIO
--gpio-rd MASK --dest !idRead remote GPIO
--rebootReboot the radio
--set-canned-message "msg1;msg2"Set canned messages

Alert

POST /api/alert?msg={message}

Broadcasts a system alert message over the SSE stream to all connected dashboard clients. Does not send to the radio.

POST /api/alert?msg=Power+outage+detected
→ {"status": "Alert broadcasted"}

Monitor (Website Scraper → Mesh)

POST /api/monitor — Auth Required

Fetches a URL, extracts a specific HTML text block, and sends it as a Meshtastic message. SSRF-protected (rejects private/internal IP ranges).

POST /api/monitor
Content-Type: application/json

{
  "url": "https://example.com/status",
  "block_id": 2,
  "prefix": "STATUS:",
  "node_id": "!aabbccdd",
  "channel": 0
}

Extract (URL Content)

POST /extract — Auth Required

Fetches a URL and returns structured text blocks extracted from the HTML. SSRF-protected.

POST /extract
Content-Type: application/json

{
  "url": "https://example.com",
  "block_id": null,
  "text_only": false
}

With block_id: null, returns up to 50 blocks: {"blocks": [{"text": "...", "id": 0, "tag": "h1"}, ...]}. With a specific block_id, returns just that block.

SSE (Server-Sent Events)

GET /sse

Real-time event stream. Connect once and receive all live updates. Maximum 50 concurrent clients per slot.

On connection, two bootstrap events are emitted immediately:

  • connection_status — current radio status string
  • nodes — full node list as JSON array

Ongoing event types:

EventData
packetFully processed packet object
node_updateUpdated node data for a single node
statsSession statistics (broadcast every 10 s)
connection_statusConnection status string
local_node_infoFull local node info after connection
message_status_update{"mesh_packet_id": N, "status": "DELIVERED"}
traceroute_resultResult of a live traceroute
sync_status{"is_syncing": bool, "current": "..."}
system_updateSystem announcements (update available, etc.)
activity"RX" or "TX" — for LED/activity indicators
plugin_updatePlugin status change
errorError message string
pingKeepalive (every 30 s when idle)

GET /sse/{slot_id}

SSE stream for a specific radio slot in multi-radio mode. See Slot Management.

Node Configuration (Radio Protobuf)

GET /api/node/config — Auth Required

Reads the full device configuration as a structured JSON snapshot by introspecting the radio's protobuf config objects. Returns all fields across localConfig, moduleConfig, and active channels, along with their types and current values.

{
  "identity": {"long_name": "Alpha Base", "short_name": "ALPH"},
  "localConfig": {
    "lora": [
      {"path": "localConfig.lora.region", "name": "region", "type": "enum", "value": 5, "options": {"UNSET": 0, "US": 1, "EU_433": 2, "EU_868": 5, ...}},
      {"path": "localConfig.lora.hop_limit", "name": "hop_limit", "type": "int", "value": 3},
      {"path": "localConfig.lora.tx_power", "name": "tx_power", "type": "int", "value": 20}
    ],
    "position": [...],
    "device": [...]
  },
  "moduleConfig": { ... },
  "channels": {
    "0": {"index": 0, "role": "1", "fields": [...]}
  }
}

POST /api/node/config/save — Auth Required

Applies a list of configuration changes to the radio and optionally triggers a reboot. Changes are dispatched only when the value has actually changed from the current state.

POST /api/node/config/save
Content-Type: application/json

{
  "changes": [
    {"path": "localConfig.lora.hop_limit", "value": "4"},
    {"path": "localConfig.lora.tx_power", "value": "20"},
    {"path": "identity.long_name", "value": "New Node Name"},
    {"path": "channels.0.name", "value": "LongFast"}
  ],
  "reboot": true
}
changes (list, required)
Array of {"path": "...", "value": "..."} objects. Use the path values from GET /api/node/config. All values are sent as strings and coerced to the correct protobuf type.
reboot (bool)
If true and changes were written, the radio is rebooted after 1.5 s. Default: true.

Response:

{
  "status": "success",
  "written": ["localConfig.lora", "localConfig.device"],
  "errors": [],
  "reboot_triggered": true
}

status is "partial" if some changes failed. Always check the errors array.

C2 Activity Log

GET /api/c2/status — Auth Required

Returns the in-memory C2 bridge activity log and statistics.

{
  "stats": {
    "heartbeats_sent": 240,
    "heartbeat_failures": 2,
    "proxy_requests_received": 47,
    "proxy_responses_sent": 46,
    "outbox_messages_forwarded": 3,
    "last_contact": 1705329000.0,
    "last_error": null
  },
  "logs": [...]
}