EBXv1

API reference

Dispatch

Telling an asset what to do: immediate dispatch and its history, scheduled dispatch and the anticipated dispatch forecast. Immediate dispatch is an unrestricted override, so read its description before using it.

6 endpoints
GET/v1/assets/{asset_id}/dispatch/immediate

Get immediate dispatch history

dispatch.read

Required scopes: dispatch.read

Get a history of immediate dispatch issued for an asset.

Time filtering is overlap-based and uses half-open window semantics. With both start_time and end_time, the endpoint returns dispatch intervals that overlap [start_time, end_time).

Boundary behavior:

  • a dispatch ending exactly at start_time is excluded
  • a dispatch starting exactly at end_time is excluded

The control payload matches scheduled dispatch: control_mode, bess_power_mw, poc_power_mw, and reserved pv_power_mw, with the same per-mode field matrix. set_power_mw remains a deprecated alias for bess_power_mw.

History is limited to the last 30 days.

Path parameters

asset_idREQUIRED
string

Query parameters

sort_by
enumstart_timeend_timeField to sort the results by
sort_order
enumascdescOrder to sort the results by, either 'asc' or 'desc'
start_time
string<date-time>Return immediate dispatch intervals whose end_time is strictly after this timestamp. With end_time, the API matches intervals overlapping the half-open window [start_time, end_time).
end_time
string<date-time>Return immediate dispatch intervals whose start_time is strictly before this timestamp. With start_time, the API matches intervals overlapping the half-open window [start_time, end_time).
Example value
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/dispatch/immediate" \
  -H "Authorization: Bearer $EBX_TOKEN"
Example response200
{
  "dispatches": [
    {
      "start_time": "2026-10-22T20:30:00Z",
      "end_time": "2026-10-22T20:30:00Z",
      "set_power_mw": 0,
      "control_mode": "passive",
      "bess_power_mw": 0,
      "poc_power_mw": 0,
      "pv_power_mw": 0
    }
  ]
}
Can raise
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 422validation failed
  • 429rate limited
ALL CODES →
POST/v1/assets/{asset_id}/dispatch/immediate

Set immediate dispatch power (unrestricted override)

dispatch.write

Required scopes: dispatch.write

Override the dispatch of an asset for a bounded window, bypassing every safety safeguard.

Unrestricted emergency override — use with extreme care.

This endpoint deliberately bypasses every platform safeguard: declared unavailabilities, power limits, and regulatory or market restrictions are neither checked nor enforced. The platform attempts to execute the dispatch regardless of the asset's declared state, schedule, or market obligations. Executing it can violate market regulations and cause system imbalances. The caller carries full responsibility for ensuring the requested set point is physically safe for the asset and permissible in the current situation. Use only when strictly necessary, e.g. on explicit TSO instruction or in an emergency.

Behavior:

  • The dispatch becomes active at start_time (defaults to now if omitted) and runs until end_time (required).
  • At end_time, the asset is automatically reset to its currently scheduled wholesale power.
  • While an immediate dispatch is active, conflicting wholesale schedule events for the same asset are rejected.
  • Submitting a new request while an immediate dispatch is already active will override the existing one.

Constraints (request validation and one plausibility bound — none are safety checks):

  • An active bess_power_mw target must not exceed the asset's usable_power_capacity in magnitude. This is a static sanity bound, not a safety check: it refuses a setpoint the asset could not physically produce under any circumstances — a typo of 500 instead of 50 — and consults nothing about the asset's live state, obligations or restrictions. While the asset is in phase 1 of the validation rollout (strict_validation false) a breach is reported in warnings and the dispatch is still stored; in phase 2 it is rejected. The historical 2x-capacity bound is always rejected. An asset with no recorded capacity is not refused on this basis.
  • end_time is required and must be in the future.
  • end_time must not run past the end of the next UTC day. An override lasting longer than that is a plan rather than an override, and plans belong in the schedule, which enforces the same horizon. Refused with 422 IMMEDIATE_DISPATCH_TOO_LONG; while the asset is in phase 1 of the validation rollout the dispatch is still created, with the breach returned in warnings.
  • start_time is optional. If provided, it must not be more than 1 second in the past (this allowance covers round-trip latency only and prevents backdating).
  • The window end_time - start_time must be at least the configured minimum (default 10s). Requests with a shorter effective window are rejected with 400.

The control payload matches scheduled dispatch: control_mode, bess_power_mw, poc_power_mw, and reserved pv_power_mw, with the same per-mode field matrix. set_power_mw remains a deprecated alias for bess_power_mw.

Path parameters

asset_idREQUIRED
string

Request body · required

control_mode
enumpassivebess_onlybess_and_pocpoc_onlyControl scheme deciding which setpoints apply. Defaults to bess_only when omitted (backwards compatible with single-BESS integrations). passive: no setpoints (free run); bess_only: bess_power_mw; bess_and_poc: both setpoints, PoC target has priority; poc_only: poc_power_mw. bess_and_poc and poc_only require a co-located asset.
power_mw
numberDeprecated: use bess_power_mw. Implicitly mapped to bess_power_mw when the latter is absent.
bess_power_mw
numberBESS power setpoint in MW (charging negative, discharging positive). Required for bess_only and bess_and_poc; must be null/zero otherwise.
poc_power_mw
numberPoint-of-connection power setpoint in MW (import negative, export positive). Required for poc_only and bess_and_poc; must be null/zero otherwise.
pv_power_mw
numberReserved for a future direct PV setpoint — must be null; PV is currently steered via the PoC target.
start_time
string<date-time>Optional start time of the dispatch in UTC. Defaults to the server's current time when omitted, and must be within 1s of it — immediate dispatches always start now; there is no future scheduling.
end_timeREQUIRED
string<date-time>End time of the dispatch in UTC. Required. Must not run past the end of the next UTC day — an override lasting longer than that is a plan, and plans belong in the schedule, where they are visible per delivery block and metered against the asset's other commitments. While the asset is in phase 1 of the validation rollout a longer window is still accepted, with the breach returned in warnings; in phase 2 it is refused.
set_power_mw
numberDeprecated: use bess_power_mw. Implicitly mapped to bess_power_mw when the latter is absent.
Example value
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/dispatch/immediate" \
  -H "Authorization: Bearer $EBX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"control_mode":"passive","power_mw":0,"bess_power_mw":0,"poc_power_mw":0,"pv_power_mw":0,"start_time":"2026-10-22T20:30:00Z","end_time":"2026-10-22T20:30:00Z","set_power_mw":0}'
Example response200
{
  "start_time": "2026-10-22T20:30:00Z",
  "end_time": "2026-10-22T20:30:00Z",
  "set_power_mw": 0,
  "control_mode": "passive",
  "bess_power_mw": 0,
  "poc_power_mw": 0,
  "pv_power_mw": 0,
  "warnings": [
    "<string>"
  ]
}
Can raise
  • 400malformed request
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 409state conflict
  • 422validation failed
  • 429rate limited
ALL CODES →
PUT/v1/assets/{asset_id}/dispatch/immediate/disable

Disable immediate dispatch

dispatch.write

Required scopes: dispatch.write

Disable the active immediate dispatch for a specific asset.

Sets the active dispatch's end_time to the current time and restores the scheduled wholesale power. Returns the now-closed dispatch record. Returns 404 if no dispatch was active.

Path parameters

asset_idREQUIRED
string
Example value
curl -X PUT "https://api.ebx.energy/v1/assets/DE_BESS_01/dispatch/immediate/disable" \
  -H "Authorization: Bearer $EBX_TOKEN"
Example response200
{
  "start_time": "2026-10-22T20:30:00Z",
  "end_time": "2026-10-22T20:30:00Z",
  "set_power_mw": 0,
  "control_mode": "passive",
  "bess_power_mw": 0,
  "poc_power_mw": 0,
  "pv_power_mw": 0
}
Can raise
  • 400malformed request
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 409state conflict
  • 429rate limited
ALL CODES →
GET/v1/assets/{asset_id}/dispatch/scheduled

Get scheduled dispatch

dispatch.read

Required scopes: dispatch.read

Get the current dispatch schedule for an asset.

The schedule is returned for the specified date and time range. If no start or end time is provided, the entire day is considered.

Rows describe stored schedule targets, including system-created defaults. created_by and updated_by retain provenance. dispatch_applicability distinguishes applicable targets, defaults suppressed in Testing, and historical defaults whose lifecycle at delivery cannot be established. A missing row is not a zero target. A scheduled zero does not confirm that the device received or applied zero. Targets do not extend past their delivery block merely because the next block has no row.

Querying can be done exclusively on 15-minute block intervals (i.e. on 15 minute marks, e.g. between 00:00:00 and 18:45:00, but not between 00:10:00 and 18:32:00).

set_power_mw (deprecated alias of bess_power_mw) is the scheduled BESS power in MW for each delivery time slot. Until per-mode persistence lands, responses report control_mode=bess_only and poc_power_mw=null for all events.

Path parameters

asset_idREQUIRED
string

Query parameters

start_time
string<date-time>Start Time in ISO8601 format
end_time
string<date-time>End Time in ISO8601 format
Example value
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/dispatch/scheduled" \
  -H "Authorization: Bearer $EBX_TOKEN"
Example response200
{
  "schedules": [
    {
      "control_mode": "passive",
      "dispatch_applicability": "applicable",
      "created_by": "<string>",
      "updated_by": "<string>",
      "set_power_mw": 0,
      "bess_power_mw": 0,
      "poc_power_mw": 0,
      "delivery_time": "2026-10-22T20:30:00Z",
      "set_time": "2026-10-22T20:30:00Z"
    }
  ],
  "warnings": [
    "<string>"
  ],
  "dry_run": false
}
Can raise
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 422validation failed
  • 429rate limited
ALL CODES →
PUT/v1/assets/{asset_id}/dispatch/scheduled

Update scheduled dispatch

dispatch.write

Required scopes: dispatch.write

Update the scheduled dispatch for a specific asset.

The schedule is a list of targets: a BESS power in MW (bess_power_mw; the former power_mw is a deprecated alias) and the time from which it applies (delivery_time). The asset's power is steered towards the most recent target at the asset's default ramp rate. Targets can be submitted at up to per-second resolution (sub-second precision is truncated), e.g. to make the asset ramp slower than its default rate.

Control modes (co-located PV+BESS sites): each target carries a control_mode deciding which setpoints apply (sign convention: import/charging negative, export/discharging positive):

control_modebess_power_mwpoc_power_mw
passivemust be null/zeromust be null/zero
bess_only (default when omitted)requiredmust be null/zero
bess_and_pocrequiredrequired — PoC target has priority
poc_onlymust be null/zerorequired

Requests violating this matrix are rejected with 422. pv_power_mw is reserved and must be null. bess_and_poc and poc_only require a co-located asset.

Grid constraints: on assets where grid import is forbidden, explicit PoC targets (bess_and_poc or poc_only) must be >= 0. Explicit PoC targets must also respect the grid-connection limit. For standalone batteries these grid checks apply to bess_only targets too, because battery power crosses the grid connection.

Co-located bess_only: the BESS follows its target while the producer exports its surplus freely, subject to the site's import/export limits. Negative BESS targets are accepted for PV charging even when grid import is forbidden. The EMS must reduce charging when production is insufficient and enforce the export ceiling; acceptance does not guarantee that the full requested power will be available.

In bess_and_poc, bess_power_mw may not exceed poc_power_mw: the co-located producer is a source and cannot absorb a surplus.

Targets may span multiple 15-minute delivery blocks. Every block that a target lands in is overridden in full with the submitted targets; blocks not covered by the request are left untouched. At the start of each block the target resets to 0 MW unless a target is placed exactly at the block start.

The entire request is rejected when:

  1. any target is beyond the end of the next UTC day,
  2. any target lands in a block that has started, or whose conditional aFRR gate has closed, inclusive at 5 minutes 15 seconds before block start,
  3. two targets overlap, i.e. fall within the same second,
  4. any target's magnitude is implausibly large (abs(power) > 2× the asset's usable power capacity) — a loose sanity check, not an availability check.

The conditional gate applies when the target block has an aFRR energy bid that is successful, in FALLBACK, or unresolved, or the asset is currently commanded active in aFRR (including internal collateralization). Capacity bids alone do not close it. The gate can reopen when neither condition remains. Unavailable activity data returns 503 when needed to decide. When neither aFRR condition applies, updates are accepted until block start. Allow roughly 10 seconds for an accepted update to take effect; this is an estimate, not a guaranteed delivery time or an additional gate. Updates within that final window are accepted and propagated on a best-effort basis: acceptance confirms storage, not execution, and the update may not be applied during that block.

Note: capacity validation uses declared data only — configured capacity, the limits and unavailabilities on record, and what other products have committed for the block. Live asset state also determines the conditional wholesale gate; capacity checks do not use it. A target within the configured capacity is accepted even if the asset cannot presently deliver it. Which breaches are refused and which come back in warnings depends on the asset's strict_validation flag (or the strict_validation query parameter, when given), and on which check fails first; the validation reference is the authority on both for these checks.

Stored targets are not reduced against reserved market capacity at execution time. This does not guarantee that the executed magnitude always equals the requested one.

The dispatch outcome of each target can be reviewed after the fact via GET /assets/{asset_id}/reports/schedule/dispatches. A row there is labelled clipped when the executed magnitude was smaller than the one requested, whatever the cause.

Path parameters

asset_idREQUIRED
string

Query parameters

validate_only
booleandefault falseRun every check and store nothing. The response is the one this request would really have produced — its status code included, so a payload that would be refused is still refused — with dry_run: true. Worth using before a live PUT: a real PUT replaces every target in each block it touches, so there is otherwise no way to validate a schedule without overwriting the one you are asking about.
strict_validation
booleanOverride the asset's strict_validation flag for this request. false stores a schedule that breaks a plausibility rule (for example PHYSICAL_CAPACITY_EXCEEDED) and returns the breach in warnings instead of a 409. Gate closure, the horizon and the 2x capacity sanity bound are still enforced. Omit to use the asset's flag.

Request body · required

schedulesREQUIRED
array[object]List of scheduled power events
Example value
curl -X PUT "https://api.ebx.energy/v1/assets/DE_BESS_01/dispatch/scheduled" \
  -H "Authorization: Bearer $EBX_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schedules":[{"control_mode":"passive","power_mw":0,"bess_power_mw":0,"poc_power_mw":0,"pv_power_mw":0,"delivery_time":"2026-10-22T20:30:00Z"}]}'
Example response200
{
  "schedules": [
    {
      "control_mode": "passive",
      "dispatch_applicability": "applicable",
      "created_by": "<string>",
      "updated_by": "<string>",
      "set_power_mw": 0,
      "bess_power_mw": 0,
      "poc_power_mw": 0,
      "delivery_time": "2026-10-22T20:30:00Z",
      "set_time": "2026-10-22T20:30:00Z"
    }
  ],
  "warnings": [
    "<string>"
  ],
  "dry_run": false
}
Can raise
  • 400malformed request
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 409state conflict
  • 422validation failed
  • 429rate limited
ALL CODES →
GET/v1/assets/{asset_id}/forecasts/anticipated_dispatch

Get anticipated dispatch forecast N seconds ahead

dispatch.read

Required scopes: dispatch.read

Return scheduled dispatch power in MW; missing targets and testing defaults are null.

Path parameters

asset_idREQUIRED
string

Query parameters

seconds_into_the_futureREQUIRED
integerForecast horizon in seconds from now (max 1 day / 86_400 s)
Example value
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/forecasts/anticipated_dispatch?seconds_into_the_future=900" \
  -H "Authorization: Bearer $EBX_TOKEN"
Example response200
{
  "set_time": "2026-10-22T20:30:00Z",
  "set_power_mw": 0
}
Can raise
  • 401no valid token
  • 403scope missing
  • 404unknown or not visible
  • 422validation failed
  • 429rate limited
ALL CODES →

Send feedback

This goes straight to the docs team on Slack, with the page you are on. It is not attributed to you, so say who you are if you would like an answer.

0/2000