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

FieldTypeRequiredDescription
feeder_iduuidyesFeeder the sensor is on. Shown in the Sensors tab; must belong to the sensor's DisCo.
latnumberyesLatitude, −90 … 90 (device GPS).
lngnumberyesLongitude, −180 … 180 (device GPS).
outage_typeenumyes"Complete blackout" | "Sparking transformer" | "Low voltage" | "Cable snap"
voltagenumbernoMeasured voltage at time of event (0 … 100000).
commentstringnoFree 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

400invalid_jsonBody is not valid JSON.
401missing_or_invalid_keyNo key, or wrong format.
401unknown_sensorKey not recognised (deleted or never issued).
403sensor_disabledSensor was disabled by the DisCo.
422validation_failedA field is missing or out of range — see details[].
422feeder_not_in_sensor_discoFeeder doesn't exist or belongs to another DisCo.
500insert_failedTemporary 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.

Sign in to the control room →