REST API
SunReye exposes a stable, third-party integration API under /api/v1. It is
auto-generated from the active inverter’s profile — the entity catalog, current state,
per-entity history, and one validated write route per writable entity all derive from the
manifest. Adding a metric or a whole new inverter extends this API with zero route code.
Authentication
Section titled “Authentication”The /api/v1 surface is guarded by API keys. Two credential sources are accepted, and a
request is authorized if it matches either:
- Admin-managed keys — issued per user from Settings → API Keys (admin only). These are stored hashed in the database, can be given an expiry, and can be revoked at any time. This is the recommended way to hand out access. See Users & Roles for who can manage them.
- Static keys — the
API_KEYSenvironment variable (comma-separated). Useful for a fixed, deploy-time key with no database.
Fail-open / fail-closed behavior governs the static list when no managed key is present:
- No static keys + development → open (fail-open, for convenience).
- No static keys + production → requests without a valid key are rejected with
401(fail-closed). Managed keys are still accepted. - Static keys configured → every request must present a valid static or managed key.
Pass the key as either header:
Authorization: Bearer <your-key>X-API-Key: <your-key>An invalid, expired, disabled, or missing key returns 401.
Issuing a managed key
Section titled “Issuing a managed key”- Sign in as an admin and open Settings → API Keys.
- Pick the owning user, give the key a name, optionally choose an expiry, and Create.
- Copy the key from the dialog — the full value is shown only once. Afterwards only a short prefix is stored for identification.
Revoking a key (from the same screen) takes effect immediately: the next request using it
returns 401.
Endpoints
Section titled “Endpoints”All paths are relative to the server base URL (e.g. http://localhost:3000).
| Method | Path | Purpose |
|---|---|---|
GET |
/api/v1/entities |
Full entity catalog — the discovery endpoint. Returns profile identity, capabilities, and every entity’s metadata. |
GET |
/api/v1/state |
Latest value of every entity from the live poll cache. 503 if no sample has been read yet. |
GET |
/api/v1/entities/:key |
One entity’s metadata plus its latest value (null if unread). 404 if the key is unknown. |
GET |
/api/v1/entities/:key/history |
Time series for one entity. |
PUT |
/api/v1/entities/:key |
Write a writable entity (validated). One such route exists per writable entity. |
Entity keys are dotted, e.g. settings.battery.grid_charge, and appear literally in the
path.
Entity history query parameters
Section titled “Entity history query parameters”GET /api/v1/entities/:key/history accepts:
| Param | Type | Default | Notes |
|---|---|---|---|
hours |
number ≥ 1 | 24 |
Look-back window. |
limit |
1–50000 | 5000 |
Max points. |
bucket |
minute | hour | day |
— | If set, returns downsampled rollups. |
inverterId |
string | active | Filter by inverter. |
With bucket, each point is { time, avg, max, min }; without it, raw { time, value }.
Writes
Section titled “Writes”Each writable entity gets a generated PUT /api/v1/entities/:key route whose body schema is
derived from the entity’s constraint:
- Enum entities accept only their allowed literal values.
- Numeric entities accept a number bounded by the entity’s
min/max.
curl -X PUT http://localhost:3000/api/v1/entities/settings.battery.max_charge_current \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{ "value": 40 }'Success returns { "ok": true, "key": "...", "value": 40 }. A value outside the entity’s
constraint is rejected with 422 and a validation detail; the same validation guards writes
from the dashboard and MQTT.
Errors
Section titled “Errors”The /api/v1 surface normalizes errors:
| Status | Meaning |
|---|---|
401 |
Missing/invalid API key (or unconfigured in production). |
404 |
Unknown entity key. |
422 |
Validation error (value out of range / not an allowed enum). |
503 |
No live sample available yet. |
500 |
Internal error (sanitized message). |
Internal dashboard API
Section titled “Internal dashboard API”The dashboard uses a separate /api/* surface (session-authenticated; mutations are
admin-only). It is not part of the stable integration contract, but is documented in
Architecture Deep-Dive for contributors. Prefer /api/v1 for
third-party integrations.