Version 1
Sensor Outage API
Let field devices installed on poles, transformers and substations report power loss automatically. Each event lands on the DisCo's control room map in real time and is grouped into an incident with consumer reports on the same feeder.
Overview
Device detects loss
Voltage on the line drops to zero or below threshold.
POST one event
Device sends its location, feeder and outage type.
Incident updated
Report is grouped, severity recalculated, map pulses live.
POST https://grid-light-connect.lovable.app/api/public/v1/sensor-events
Content-Type: application/json
Authorization: Bearer <SENSOR_API_KEY>1. Register a sensor
An approved DisCo operator (or admin) opens the control room → Sensors & API tab, enters a name, picks the feeder and the exact installation coordinates, then clicks Register sensor. A unique API key starting with bts_ is shown once — flash it into the device's secure storage. Only a hash is stored by BlackoutTrace. Sensors can be disabled or deleted at any time, which instantly invalidates the key.
2. Authentication
Send the key in either header. Requests without a valid, active key are rejected with 401/403. A key only allows posting to feeders belonging to its own DisCo.
Authorization: Bearer bts_3f9c...
# or
X-Sensor-Key: bts_3f9c...3. Send an event
curl -X POST https://grid-light-connect.lovable.app/api/public/v1/sensor-events \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SENSOR_KEY" \
-d '{
"feeder_id": "8d1e6f0a-2c4b-4f7e-9a51-3b2c1d0e9f87",
"lat": 8.4939,
"lng": 8.5153,
"outage_type": "Complete blackout",
"voltage": 0,
"comment": "Phase R/Y/B lost"
}'Request fields
| Field | Type | Required | Description |
|---|---|---|---|
| feeder_id | uuid | yes | Feeder the sensor is on. Shown in the Sensors tab; must belong to the sensor's DisCo. |
| lat | number | yes | Latitude, −90 … 90 (device GPS). |
| lng | number | yes | Longitude, −180 … 180 (device GPS). |
| outage_type | enum | yes | "Complete blackout" | "Sparking transformer" | "Low voltage" | "Cable snap" |
| voltage | number | no | Measured voltage at time of event (0 … 100000). |
| comment | string | no | Free text, max 500 chars (e.g. phase info, firmware id). |
Responses
201 Created — event accepted and attached to an incident:
{
"ok": true,
"report_id": "c0a1…",
"received_at": "2026-10-10T09:52:11.204Z",
"feeder": "33kV Station Road Feeder",
"node": "Bukan Sidi",
"incident": { "id": "5b7e…", "code": "INC-1012", "status": "Unassigned", "severity": "Medium", "report_count": 3 }
}200 OK with "duplicate": true — the same sensor already reported within the last 2 minutes; nothing new was stored.
Error codes
| 400 | invalid_json | Body is not valid JSON. |
| 401 | missing_or_invalid_key | No key, or wrong format. |
| 401 | unknown_sensor | Key not recognised (deleted or never issued). |
| 403 | sensor_disabled | Sensor was disabled by the DisCo. |
| 422 | validation_failed | A field is missing or out of range — see details[]. |
| 422 | feeder_not_in_sensor_disco | Feeder doesn't exist or belongs to another DisCo. |
| 500 | insert_failed | Temporary server problem — retry with backoff. |
Grouping & debounce
- The event is snapped to the nearest mapped neighbourhood node on the given feeder.
- If the feeder already has an open incident, the event joins it; otherwise a new incident (INC-####) opens.
- Severity rises with report count (2 → Medium, 5 → High, 8 → Critical) and the feeder pulses on the heatmap.
- Send one event per power-loss transition. Repeats within 2 minutes are ignored. Retry only on 5xx or network errors.
- Sensor reports appear in the control room alongside consumer reports, labelled with the sensor name.
Device examples
ESP32 (Arduino)
#include <HTTPClient.h>
void reportOutage(float lat, float lng, float volts) {
HTTPClient http;
http.begin("https://grid-light-connect.lovable.app/api/public/v1/sensor-events");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " SENSOR_KEY);
String body = String("{\"feeder_id\":\"") + FEEDER_ID + "\",\"lat\":" + String(lat, 6) +
",\"lng\":" + String(lng, 6) + ",\"outage_type\":\"Complete blackout\",\"voltage\":" + volts + "}";
int code = http.POST(body);
http.end();
}Python (Raspberry Pi)
import os, requests
r = requests.post("https://grid-light-connect.lovable.app/api/public/v1/sensor-events",
headers={"Authorization": f"Bearer {os.environ['SENSOR_KEY']}"},
json={"feeder_id": os.environ["FEEDER_ID"], "lat": 9.8965, "lng": 8.8583,
"outage_type": "Low voltage", "voltage": 158}, timeout=10)
print(r.status_code, r.json())Simulator
Operators and admins can test without hardware: open the control room → Sensors & API → Sensor simulator, pick a sensor and outage type, and press Simulate power loss. It calls this same public endpoint and shows the raw request and response.