Platform-level REST endpoints available on every PumpFuse product.
These cover device identity, configuration, relay control, power monitoring, telemetry, and OTA firmware updates.
For product-specific REST endpoints, see the product's own REST API Reference (e.g., Watchdog REST API).
| Version | Date | Notes |
|---|---|---|
| 1.0 | 2026-02-11 | Initial release |
http://<device-ip>/api/v1
All endpoints accept and return application/json unless otherwise noted. No authentication.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/info | Device identity |
| GET | /api/v1/status | Full runtime snapshot |
| GET | /api/v1/config | Configuration snapshot |
| POST | /api/v1/config | Update configuration |
| GET | /api/v1/relay | Relay state |
| POST | /api/v1/relay | Set relay state |
| GET | /api/v1/power | Latest power reading |
| GET | /api/v1/telemetry | Telemetry settings |
| POST | /api/v1/telemetry | Update telemetry settings |
| POST | /api/v1/ota | Push firmware binary |
Returns device identity. Available in both normal and recovery modes.
Response
{
"product": "pf-watchdog",
"firmware": "1.0.0",
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "192.168.40.114",
"uptime": 3600
}
| Field | Type | Description |
|---|---|---|
product | string | Product identifier (e.g., "pf-watchdog", "pf-sump") |
firmware | string | Firmware version from esp_app_desc |
mac | string | WiFi station MAC address, colon-separated uppercase hex |
ip | string | Current IP address on the WiFi network |
uptime | integer | Seconds since boot |
Returns a full runtime snapshot including connectivity, relay, power, and telemetry state.
Response
{
"product": "pf-watchdog",
"firmware": "1.0.0",
"uptime": 3600,
"ip": "192.168.40.114",
"wifi_connected": true,
"mqtt_connected": true,
"relay": true,
"power_w_last": 12.345,
"power_sample_ts_ms": 1739260200000,
"telemetry_active": true,
"telemetry_interval_s": 5
}
| Field | Type | Description |
|---|---|---|
product | string | Product identifier |
firmware | string | Firmware version |
uptime | integer | Seconds since boot |
ip | string | Current IP address |
wifi_connected | boolean | WiFi station connected |
mqtt_connected | boolean | MQTT broker connected |
relay | boolean | true = closed (power on) |
power_w_last | float | Most recent power sample in watts |
power_sample_ts_ms | integer | Timestamp of power sample (milliseconds since boot epoch) |
telemetry_active | boolean | Whether telemetry streaming is enabled |
telemetry_interval_s | integer | Telemetry publish interval in seconds |
Returns current device configuration.
Response
{
"device_name": "test-board",
"wifi_ssid": "MyNetwork",
"mqtt_broker": "192.168.40.164",
"mqtt_port": 1883,
"mqtt_topic": "pumpfuse/test-board",
"mqtt_user": "mqtt",
"mqtt_pass": "secret123"
}
| Field | Type | Description |
|---|---|---|
device_name | string | Device display name |
wifi_ssid | string | Stored WiFi SSID (read-only here; set via BLE provisioning) |
mqtt_broker | string | MQTT broker hostname or IP |
mqtt_port | integer | MQTT broker port |
mqtt_topic | string | MQTT base topic prefix |
mqtt_user | string | MQTT username |
mqtt_pass | string | MQTT password |
Update device configuration. All fields are optional — include only the fields you want to change. Changes are persisted to NVS immediately.
Request
{
"device_name": "living-room",
"mqtt_broker": "10.0.0.5",
"mqtt_port": 1883,
"mqtt_topic": "pumpfuse/living-room",
"mqtt_user": "mqtt",
"mqtt_pass": "newpass"
}
| Field | Type | Required | Validation |
|---|---|---|---|
device_name | string | No | Non-empty, max 32 characters |
mqtt_broker | string | No | Non-empty, max 128 characters |
mqtt_port | integer | No | 1–65535, must be whole number |
mqtt_topic | string | No | Non-empty, max 128 characters |
mqtt_user | string | No | Max 64 characters (empty string allowed) |
mqtt_pass | string | No | Max 64 characters (empty string allowed) |
Response: Updated config JSON (same schema as GET).
Errors
| Status | Body | Cause |
|---|---|---|
| 400 | invalid_body | Request body unreadable or too large |
| 400 | invalid_json | Body is not valid JSON object |
| 400 | unknown_key | Body contains unrecognized key |
| 400 | invalid_device_name | Empty string or exceeds max length |
| 400 | invalid_mqtt_broker | Empty string or exceeds max length |
| 400 | invalid_mqtt_port | Not a number, fractional, or out of range |
| 400 | invalid_mqtt_topic | Empty string or exceeds max length |
| 400 | invalid_mqtt_user | Exceeds max length |
| 400 | invalid_mqtt_pass | Exceeds max length |
| 400 | missing_mqtt_required | MQTT change leaves broker or topic empty |
| 500 | save_error | NVS write failure |
Side effects:
device_name was updated, device_name is rolled back.Returns current relay state.
Response
{
"relay": true
}
| Field | Type | Description |
|---|---|---|
relay | boolean | true = relay closed (power flowing), false = relay open |
Set relay state.
Request
{
"relay": false
}
| Field | Type | Required | Description |
|---|---|---|---|
relay | boolean | Yes | true = close relay (power on), false = open relay (power off) |
Response: Same schema as GET.
Errors
| Status | Body | Cause |
|---|---|---|
| 400 | invalid_body | Body unreadable |
| 400 | invalid_payload | Missing relay field or not boolean |
Side effects:
/api/v1/status, MQTT relay/state, etc.).Returns the most recent power reading.
Response
{
"power_w": 12.345,
"ts_ms": 1739260200000
}
| Field | Type | Description |
|---|---|---|
power_w | float | Power in watts (3 decimal places) |
ts_ms | integer | Sample timestamp in milliseconds (device uptime epoch) |
Returns current telemetry settings.
Response
{
"active": true,
"interval_s": 5
}
| Field | Type | Description |
|---|---|---|
active | boolean | Whether power telemetry is being published via MQTT |
interval_s | integer | Publish interval in seconds |
Update telemetry settings. Both fields are optional.
Request
{
"active": true,
"interval_s": 10
}
| Field | Type | Required | Validation |
|---|---|---|---|
active | boolean | No | Must be boolean |
interval_s | integer | No | 1–60, must be whole number |
Response: Updated telemetry JSON (same schema as GET).
Errors
| Status | Body | Cause |
|---|---|---|
| 400 | invalid_body | Body unreadable |
| 400 | invalid_json | Not valid JSON object |
| 400 | invalid_active | active field is not boolean |
| 400 | invalid_interval | Not a number, fractional, or out of range 1–60 |
| 500 | save_error | Runtime state save failure |
Side effects:
telemetry/state and status topics.Push a firmware binary for over-the-air update.
Request
application/octet-streamcurl -s http://192.168.40.114/api/v1/ota \
-X POST \
-H "Content-Type: application/octet-stream" \
--data-binary @build/pf-watchdog_1.0.0.bin
Response: "OK" (plain text), then the device reboots.
Side effects:
When the device enters recovery mode (failed boot or forced recovery), it starts a WiFi access point and exposes a minimal set of endpoints.
Access Point: SSID ppp (hidden), IP 192.168.4.1, open authentication.
| Method | Path | Description |
|---|---|---|
| GET | / | HTML page showing product name and firmware version |
| GET | /hotspot-detect.html | iOS captive portal redirect → / |
| GET | /api/v1/info | Same JSON schema as normal mode (IP is always 192.168.4.1) |
| POST | /api/v1/ota | Push firmware binary (same handler as normal mode) |
Recovery mode is designed for the ota-flash tool to discover and reflash unresponsive devices.
All JSON endpoints respond with Content-Type: application/json. The OTA endpoint expects application/octet-stream.
Error responses use plain text bodies (not JSON). The HTTP status code indicates the error class (400 for client errors, 500 for server errors), and the body contains a brief machine-readable error identifier.
No rate limiting. The device processes requests serially on the HTTP server thread.
The wifi_ssid field in config is read-only via REST. WiFi credentials can only be set via BLE provisioning.