Quickstart
Create a token and read live plant data in a few minutes.
1. Create an API key
API keys are created self-service by client admins in the owner platform:
- Sign in at owner.dynvolt.com.
- Go to Settings → Admin → API access.
- Create a key, pick the scopes it needs, and copy it.
The screen only offers scopes your plan includes. Grant the fewest a given integration needs — the 17 scopes range from read-only telemetry to the two control scopes (curtailment:write, bess:write) that command the plant.
The full key is shown once, at creation. Store it in a secrets manager immediately — you cannot retrieve it again, only rotate it.
Keys look like this:
dvk_live_a1b2c3d4e5f6_9f8e7d6c... (dvk_live_<12 hex>_<64 hex>)2. Make your first request
List the sites your key can access:
curl https://api.owner.dynvolt.com/v1/sites \
-H "Authorization: Bearer $DYNVOLT_API_KEY"import httpx
client = httpx.Client(
base_url="https://api.owner.dynvolt.com",
headers={"Authorization": f"Bearer {DYNVOLT_API_KEY}"},
)
sites = client.get("/v1/sites").json()
print(sites["data"])const res = await fetch('https://api.owner.dynvolt.com/v1/sites', {
headers: { Authorization: `Bearer ${process.env.DYNVOLT_API_KEY}` },
});
const { data } = await res.json();
console.log(data);A successful response returns the list envelope used by all list endpoints:
{
"data": [
{
"site_id": "pv-ljubas",
"name": "PV Ljubas",
"health_status": "healthy"
}
],
"meta": { "count": 1 }
}health_status is one of healthy, degraded, offline, or unknown.
3. Read live plant data
Use a site_id from step 2 to pull a full live snapshot — loggers, inverters, sensors, weather, and predictions in one call (requires the plant:read scope):
curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/overview" \
-H "Authorization: Bearer $DYNVOLT_API_KEY"overview = client.get("/v1/sites/pv-ljubas/overview").json()const overview = await fetch(
'https://api.owner.dynvolt.com/v1/sites/pv-ljubas/overview',
{ headers: { Authorization: `Bearer ${process.env.DYNVOLT_API_KEY}` } },
).then((r) => r.json());For narrower reads, see /plant/status, /inverters, or /energy/hourly in the reference. The full surface is 108 operations — weather/EMI, efficiency & I-V diagnostics, curtailment, markets, earnings, predictions, full BESS telemetry, alerts, O&M, and reports.
4. Create a work order
Writes need a write scope — here om:write. Pass an Idempotency-Key so a retried request is not applied twice:
curl -X POST "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/work-orders" \
-H "Authorization: Bearer $DYNVOLT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c9d2e-4b0a-4f3f-8f2f-1a2b3c4d5e6f" \
-d '{
"title": "Inspect string L4-07 underperformance",
"description": "String is 12% below orientation peers for 3 days.",
"priority": "medium"
}'import uuid
wo = client.post(
"/v1/sites/pv-ljubas/work-orders",
headers={"Idempotency-Key": str(uuid.uuid4())},
json={
"title": "Inspect string L4-07 underperformance",
"description": "String is 12% below orientation peers for 3 days.",
"priority": "medium",
},
).json()const wo = await fetch(
'https://api.owner.dynvolt.com/v1/sites/pv-ljubas/work-orders',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.DYNVOLT_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify({
title: 'Inspect string L4-07 underperformance',
description: 'String is 12% below orientation peers for 3 days.',
priority: 'medium',
}),
},
).then((r) => r.json());Where to go next
- Authentication — key format, rotation, storage advice
- Scopes & modules — all 17 scopes and which are control scopes
- Curtailment — schedule / immediate / release, and the grid-permit invariant
- BESS — telemetry plus the dispatch-approval workflow
- Errors — what a
403 forbidden_scopeor409 conflict_safe_modemeans - Rate limits — 120 req/min, and how to back off
- API reference — all 108 operations, grouped by area