DynVoltDYNVOLT · developers

Curtailment

Curtailment endpoints of the DynVolt Open API.

Active and scheduled curtailment windows, a forecast-based limit suggester, relay/fan command history, and the control writes that schedule, apply immediately, or release curtailment. See the Curtailment guide — curtailment can only ever reduce output, never raise it above the grid permit.

GET /v1/sites/{site_id}/curtailment

Active + scheduled curtailment windows (rows carry group_id, applied, is_active).

Scope: curtailment:read · Module: basic_scada

curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/curtailment" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

GET /v1/sites/{site_id}/curtailment/suggest

Suggest a power limit from forecast (?logger_id&date&start_hour&end_hour).

Scope: curtailment:read · Module: basic_scada

Query parameterTypeRequiredDescription
logger_idintegeryesLogger (plant) to suggest a limit for.
datestringyesTarget day, YYYY-MM-DD (UTC).
start_hourintegeryesWindow start hour, 023 (local plant hour).
end_hourintegeryesWindow end hour, 124. Must be greater than start_hour.
curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/curtailment/suggest" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

GET /v1/sites/{site_id}/commands/relay-history

Relay/fan command history (?node_id&limit).

Scope: curtailment:read · Module: basic_scada

Query parameterTypeRequiredDescription
node_idstringnoRestrict to one sensor/relay node.
limitintegernoMaximum rows to return (capped at 500).
curl "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/commands/relay-history" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY"

POST /v1/sites/{site_id}/curtailment/window

Schedule a curtailment window (site or single plant).

Scope: curtailment:write · Module: basic_scada · Control write — commands the plant

Path parameterTypeDescription
site_idstringSite identifier from GET /v1/sites.

Request body (application/json):

FieldTypeRequiredDescription
scopestringyes"site" (whole site) or "logger" (one plant).
logger_idintegerwhen scope="logger"Target logger. Required only for logger scope; ignored for site scope.
start_atstringyesWindow start — RFC3339 datetime.
end_atstringyesWindow end — RFC3339 datetime. Must be after start_at.
limit_modestringyes"percent" (% of rated power) or "kw" (absolute ceiling). For scope="site" + "kw", limit_value is the total site kW, split across plants proportional to their rated power.
limit_valuenumberyesThe limit, > 0. Interpreted per limit_mode.
  • Response: 201{ group_id, schedules: [ { id, logger_id, start_at, end_at, max_power_kw, ... } ] }. Keep group_id to release the window later.
  • Control write — reduces plant output. It can never raise output above the grid-connection permit; every apply and restore is clamped to that permit by the edge. See the Curtailment guide.
  • Supports the Idempotency-Key header for safe retries (24-hour replay). Returns 409 conflict_safe_mode while the site is in commissioning safe mode.
curl -X POST "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/curtailment/window" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "scope": "logger",
    "logger_id": 4,
    "start_at": "2026-08-12T10:00:00Z",
    "end_at": "2026-08-12T14:00:00Z",
    "limit_mode": "percent",
    "limit_value": 60
  }'

POST /v1/sites/{site_id}/curtailment/immediate

Curtail now (applied within ~10s).

Scope: curtailment:write · Module: basic_scada · Control write — commands the plant

Path parameterTypeDescription
site_idstringSite identifier from GET /v1/sites.

Request body (application/json):

FieldTypeRequiredDescription
scopestringyes"site" or "logger" — same semantics as the window endpoint.
logger_idintegerwhen scope="logger"Target logger. Required only for logger scope.
limit_modestringyes"percent" or "kw" — same semantics as the window endpoint.
limit_valuenumberyesThe limit, > 0.
duration_minutesintegernoHow long to hold, > 0 and ≤ 10080 (7 days). Omit or null = hold until released (still bounded to 7 days).
  • Response: 201 — same shape as the window endpoint. Applied within ~10 s.
  • Control write — reduces plant output. It can never raise output above the grid-connection permit; every apply and restore is clamped to that permit by the edge. See the Curtailment guide.
  • Supports the Idempotency-Key header for safe retries (24-hour replay). Returns 409 conflict_safe_mode while the site is in commissioning safe mode.
curl -X POST "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/curtailment/immediate" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "scope": "site",
    "limit_mode": "kw",
    "limit_value": 3500,
    "duration_minutes": 120
  }'

DELETE /v1/sites/{site_id}/curtailment/group/{group_id}

Release a curtailment group (restores clamped power first).

Scope: curtailment:write · Module: basic_scada · Control write — commands the plant

Path parameterTypeDescription
site_idstringSite identifier from GET /v1/sites.
group_idstringCurtailment group ID returned when the window/immediate curtailment was created.
  • No request body. Restores clamped power first (itself re-clamped to the grid permit), then cancels every row in the group.
  • Control write — reduces plant output. It can never raise output above the grid-connection permit; every apply and restore is clamped to that permit by the edge. See the Curtailment guide.
  • Supports the Idempotency-Key header for safe retries (24-hour replay). Returns 409 conflict_safe_mode while the site is in commissioning safe mode.
curl -X DELETE "https://api.owner.dynvolt.com/v1/sites/pv-ljubas/curtailment/group/grp_8a1f" \
  -H "Authorization: Bearer $DYNVOLT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)"