API Documentation
HOP Sensors Ingest API
Overview Start Here
The HOP Sensors API is a JSON REST API. All requests must be authenticated with a pre-shared API key passed as a request header. All ingest endpoints accept JSON payloads. All query endpoints return JSON arrays.
| Property | Value |
|---|---|
| Protocol | HTTPS only |
| Format | JSON (Content-Type: application/json) |
| Auth | X-API-Key header (pre-shared key) |
| Ingest endpoints | 3 — telemetry, event, outage |
| Query endpoints | 8 — dashboard read access |
| Timestamps | ISO 8601 UTC — e.g. 2026-06-11T14:32:00Z |
Authentication
Every request must include your API key in the X-API-Key header. Your key is provisioned when your account is created. Keep it secret — it grants full read/write access to your substation data.
# All requests — required header X-API-Key: your-api-key-here
Base URL
All API endpoints share a single base URL:
https://portal.hopsensorsllc.com/api
Full example for the telemetry ingest endpoint:
POST https://portal.hopsensorsllc.com/api/ingest/telemetry
Errors
| Status | Meaning |
|---|---|
| 200 OK | Request succeeded. Response body confirms what was written. |
| 401 Unauthorized | Missing or invalid X-API-Key header. |
| 422 Unprocessable Entity | Request body failed validation. Response includes field-level error detail. |
| 500 Internal Server Error | Database write or query failed. Response includes detail string. |
POST /ingest/telemetry Ingest
Write a real-time telemetry reading for one feeder at one substation. Send one record per feeder per polling cycle. Your SCADA system or middleware should call this endpoint at whatever interval it samples data — the platform supports any rate up to once per second per feeder.
Request Body
| Field | Type | Description | |
|---|---|---|---|
| substation_id | REQUIRED | string | Your identifier for this substation. Must be consistent across all records. E.g. SUB001 |
| voltage_kv | REQUIRED | float | Bus voltage in kilovolts. |
| current_a | REQUIRED | float | Line current in amperes. |
| power_mw | REQUIRED | float | Active power in megawatts. |
| frequency_hz | REQUIRED | float | Grid frequency in hertz. This field drives ML anomaly scoring. |
| feeder_id | optional | string | Feeder identifier. E.g. FDR002. Enables per-feeder event correlation. |
| device_id | optional | string | Device or RTU identifier. E.g. SUB001-FDR002. |
| site_name | optional | string | Human-readable site label shown in the dashboard. E.g. Portland North. |
| latitude | optional | float | Substation latitude. Required for map view. E.g. 45.5231 |
| longitude | optional | float | Substation longitude. Required for map view. E.g. -122.6765 |
| status_code | optional | string | Operational status tag from your SCADA system. E.g. NORMAL, OUTAGE. |
| timestamp | optional | string | ISO 8601 UTC timestamp. If omitted, the server uses the time of receipt. |
Example Request
// POST /api/ingest/telemetry // Header: X-API-Key: your-api-key-here // Header: Content-Type: application/json { "substation_id": "SUB001", "feeder_id": "FDR002", "device_id": "SUB001-FDR002", "site_name": "Portland North", "latitude": 45.5231, "longitude": -122.6765, "voltage_kv": 229.8, "current_a": 1187.4, "power_mw": 274.2, "frequency_hz": 60.002, "status_code": "NORMAL", "timestamp": "2026-06-11T14:32:00Z" }
{ "status": "telemetry written", "substation_id": "SUB001" }POST /ingest/event Ingest
Write a discrete grid event — a state change, alarm, or operational transition at a device or feeder. Events populate the Alarms & Outages tab and trigger email alerts for FEEDER_TRIP conditions.
Request Body
| Field | Type | Description | |
|---|---|---|---|
| device_id | REQUIRED | string | Device or RTU originating the event. E.g. SUB001-FDR002. |
| severity | REQUIRED | string | ALARM — triggers alerting pipeline. INFO — logged, no alert. |
| event_type | REQUIRED | string | See event types below. E.g. FEEDER_TRIP, FEEDER_RESTORE. |
| feeder_id | optional | string | Feeder affected. Used for trip/restore correlation. |
| old_state | optional | string | State before transition. E.g. CLOSED. |
| new_state | optional | string | State after transition. E.g. OPEN. |
| message | optional | string | Free-text description included in alert emails. |
| latitude | optional | float | Event location — for map overlay. |
| longitude | optional | float | Event location — for map overlay. |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt. |
Event Types
| event_type | severity | Effect |
|---|---|---|
| FEEDER_TRIP | ALARM | Logged + email alert sent (subject to rate limits). Marks feeder as tripped. |
| FEEDER_RESTORE | INFO | Logged + restore notification sent (only if a TRIP alert was previously sent for this feeder). |
| Any other value | INFO or ALARM | Logged to the events table. Visible in dashboard Alarms & Outages tab. |
Example — Feeder Trip
{
"device_id": "SUB001-FDR002",
"feeder_id": "FDR002",
"severity": "ALARM",
"event_type": "FEEDER_TRIP",
"old_state": "CLOSED",
"new_state": "OPEN",
"message": "Overcurrent protection operated — FDR002",
"latitude": 45.5231,
"longitude": -122.6765,
"timestamp": "2026-06-11T14:35:00Z"
}{ "status": "event written", "device_id": "SUB001-FDR002" }POST /ingest/outage Ingest
Write or update an outage record with customer impact data. Send an ACTIVE record when an outage begins and a RESTORED record when it is cleared. Outages appear on the Alarms & Outages tab and the Executive View grid health metric.
Request Body
| Field | Type | Description | |
|---|---|---|---|
| outage_id | REQUIRED | string | Unique identifier for this outage. Must match on the restore record. E.g. OUT-SUB001-FDR002-1749650000. |
| feeder_id | REQUIRED | string | Feeder affected. |
| customers_out | REQUIRED | integer | Customers without power. Send 0 on restore. |
| status | REQUIRED | string | ACTIVE — outage in progress. RESTORED — outage cleared. |
| region | optional | string | Region or substation label. E.g. SUB001. |
| mw_interrupted | optional | float | Load interrupted in megawatts. |
| cause | optional | string | Cause category. E.g. STORM, EQUIPMENT, ANIMAL, UNKNOWN. |
| etr_time | optional | string | Estimated time to restore — ISO 8601 UTC. |
| latitude / longitude | optional | float | Outage location for map overlay. |
| timestamp | optional | string | ISO 8601 UTC. Defaults to time of receipt. |
Example — Active Outage
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"region": "SUB001",
"customers_out": 847,
"mw_interrupted": 12.4,
"status": "ACTIVE",
"cause": "STORM",
"etr_time": "2026-06-11T16:00:00Z",
"latitude": 45.5231,
"longitude": -122.6765,
"timestamp": "2026-06-11T14:35:00Z"
}Example — Restore
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"customers_out": 0,
"status": "RESTORED",
"timestamp": "2026-06-11T15:48:00Z"
}{ "status": "outage written", "outage_id": "OUT-SUB001-FDR002-1749650000" }GET /query/telemetry/latest Query
Returns the most recent telemetry reading per substation from the last 1 minute. Used by the live telemetry panel in the dashboard.
GET /api/query/telemetry/latest X-API-Key: your-api-key-here
[
{
"substation_id": "SUB001",
"frequency_hz": 60.002,
"voltage_kv": 229.8,
"current_a": 1187.4,
"power_mw": 274.2,
"latitude": 45.5231,
"longitude": -122.6765,
"time": "2026-06-11 14:32:00+00:00"
}
]GET /query/telemetry/timeseries Query
Returns voltage, power, and frequency time series for a single substation. Drives the time-series charts on the Telemetry tab.
| Param | Type | Default | Description |
|---|---|---|---|
| substation_id | string | — | Required. Substation to query. |
| minutes | integer | 30 | Lookback window. Accepts: 5, 15, 30, 60, 360, 1440, 10080. |
GET /api/query/telemetry/timeseries?substation_id=SUB001&minutes=60
GET /query/ml/latest Query
Returns the most recent ML anomaly score per substation. Drives the score cards on the ML Anomaly tab.
GET /api/query/ml/latest
[
{
"substation_id": "SUB001",
"anomaly_score": 0.2341, // 0.0 – 1.0. >= 0.7 = anomaly
"is_anomaly": false,
"baseline_mean": 60.001,
"baseline_std": 0.0082,
"z_score": 0.585,
"latest_value": 60.002, // frequency_hz reading that was scored
"time": "2026-06-11 14:32:00+00:00"
}
]GET /query/ml/trend Query
Returns ML anomaly score history across all substations for trend visualization.
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window. |
GET /api/query/ml/trend?minutes=60
GET /query/events Query
Returns recent grid events — feeder trips, restores, and any other logged events — ordered most-recent first.
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window. Max returned: 100 records. |
GET /api/query/events?minutes=60
GET /query/outages/active Query
Returns currently active outages — records with status = ACTIVE written within the last 2 hours, deduplicated by outage ID.
GET /api/query/outages/active
[
{
"outage_id": "OUT-SUB001-FDR002-1749650000",
"feeder_id": "FDR002",
"region": "SUB001",
"customers_out": 847,
"mw_interrupted": 12.4,
"cause": "STORM",
"etr_time": "2026-06-11T16:00:00Z",
"time": "2026-06-11 14:35:00+00:00"
}
]GET /query/frequency/thresholds Query
Returns frequency readings for all substations for the threshold plot on the Frequency tab.
| Param | Type | Default | Description |
|---|---|---|---|
| minutes | integer | 30 | Lookback window. |
GET /api/query/frequency/thresholds?minutes=30
GET /query/substations Query
Returns all distinct substation IDs that have sent telemetry in the last hour, with their coordinates. Useful for validating that your integration is sending data for the expected substations.
GET /api/query/substations
[
{ "substation_id": "SUB001", "latitude": 45.5231, "longitude": -122.6765 },
{ "substation_id": "SUB002", "latitude": 45.5152, "longitude": -122.6784 }
]Data Model
All data is stored in InfluxDB 3 Core across three measurements. Understanding the schema helps when interpreting query responses.
scada_grid
Primary time-series measurement. Stores raw telemetry and ML anomaly scores together, time-aligned.
| Field | Type | Source |
|---|---|---|
| substation_id | tag | Ingest |
| feeder_id, device_id, site_name | tag | Ingest |
| voltage_kv, current_a, power_mw, frequency_hz | field | Ingest |
| anomaly_score, is_anomaly, z_score | field | ML service (written every 60s) |
| baseline_mean, baseline_std, latest_value, model_version | field | ML service |
scada_events
Discrete event log. Every feeder trip, restore, and other state transition.
outages
Outage lifecycle records. Each outage has an ACTIVE record and a RESTORED record identified by the same outage_id.
GET /health
Returns API health status. No authentication required. Use for uptime monitoring and integration validation.
GET /api/health
{
"status": "ok",
"version": "v5",
"database": "hopsensors_v5",
"timestamp": "2026-06-11T14:32:00.000000+00:00"
}