API reference
Control Tests
Endpoints tagged “Control Tests” in the API specification.
GET/v1/assets/{asset_id}/betriebsfahrt
Betriebsfahrt status of the asset
dispatch.read
Required scopes: dispatch.read
Whether a Betriebsfahrt can be driven on this asset outside an onboarding, which preconditions still fail, and the session being worked on.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"enabled": false,
"server_now": "2026-10-22T20:30:00Z",
"preconditions": [
{
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
}
],
"can_create_session": false,
"dry_run_allowed": false,
"default_capacity_mw": "<string>",
"default_planned_start": "2026-10-22T20:30:00Z",
"session": {
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
},
"history_count": 0
}GET/v1/assets/{asset_id}/betriebsfahrt/sessions
Betriebsfahrt history of the asset
dispatch.read
Required scopes: dispatch.read
The asset's earlier standalone sessions, newest first, superseded and ended ones included. The session at hand is not among them: it is the status document's.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();[
{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}
]POST/v1/assets/{asset_id}/betriebsfahrt/sessions
Plan a Betriebsfahrt on the asset
dispatch.write
Required scopes: dispatch.write
Generates a draft from the products and the power the operator states: one run per direction the products lie in, each with three retention phases, two 15-minute delivery strokes and an extended third stroke sized from the asset's usable energy. Replaces an existing draft. Nothing reaches the asset until the session is confirmed.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
Request body · required
- productsREQUIRED
- array[enum]The products the workbooks are for. The directions driven are the ones these lie in: name only positive products and the drive is one run.
- capacity_mw
- number | stringThe power the recording steps to, in MW; defaults to the asset's usable power. Above the usable power the plan is refused.
- first_direction
- enum
positivenegativeDirection of the first run: positive (discharge) needs the run's energy plus the margin in the battery, negative (charge) that much room. Default: the order the energy favours. - extended_stroke_minutes
- integerThe first run's third delivery stroke, a multiple of 15 min; default the longest the usable energy allows.
- planned_start
- string<date-time>UTC start of run 1 on a quarter hour; defaults to the next quarter hour after the submission lead time.
- execution_mode
- enum
realdry_run
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"products":["fcr_positive"],"capacity_mw":0,"first_direction":"positive","extended_stroke_minutes":0,"planned_start":"2026-10-22T20:30:00Z","execution_mode":"real"}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions",
headers={"Authorization": f"Bearer {token}"},
json={
"products": [
"fcr_positive",
],
"capacity_mw": 0,
"first_direction": "positive",
"extended_stroke_minutes": 0,
"planned_start": "2026-10-22T20:30:00Z",
"execution_mode": "real",
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"products":["fcr_positive"],"capacity_mw":0,"first_direction":"positive","extended_stroke_minutes":0,"planned_start":"2026-10-22T20:30:00Z","execution_mode":"real"}),
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
GET/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}
Get one standalone Betriebsfahrt session
dispatch.read
Required scopes: dispatch.read
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}PATCH/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}
Adjust a standalone Betriebsfahrt draft
dispatch.write
Required scopes: dispatch.write
Regenerates the draft with the given products, power, run order, extended stroke, start or execution mode. Only drafts can be adjusted.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
Request body · required
- products
- array[enum]
- capacity_mw
- number | string
- first_direction
- enum
positivenegative - extended_stroke_minutes
- integer
- planned_start
- string<date-time>
- execution_mode
- enum
realdry_run
curl -X PATCH "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"products":["fcr_positive"],"capacity_mw":0,"first_direction":"positive","extended_stroke_minutes":0,"planned_start":"2026-10-22T20:30:00Z","execution_mode":"real"}'import httpx
r = httpx.patch(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000",
headers={"Authorization": f"Bearer {token}"},
json={
"products": [
"fcr_positive",
],
"capacity_mw": 0,
"first_direction": "positive",
"extended_stroke_minutes": 0,
"planned_start": "2026-10-22T20:30:00Z",
"execution_mode": "real",
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000", {
method: "PATCH",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"products":["fcr_positive"],"capacity_mw":0,"first_direction":"positive","extended_stroke_minutes":0,"planned_start":"2026-10-22T20:30:00Z","execution_mode":"real"}),
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}/abort
Abort a standalone Betriebsfahrt
dispatch.write
Required scopes: dispatch.write
Ends the session and sends the driver's kill switch (zero every product on the asset; cancel the run when known). Aborting again resends an undelivered kill switch.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
Request body · required
- reasonREQUIRED
- string
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/abort" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"reason":"<string>"}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/abort",
headers={"Authorization": f"Bearer {token}"},
json={
"reason": "<string>",
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/abort", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"reason":"<string>"}),
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}/confirm
Confirm a standalone Betriebsfahrt
dispatch.write
Required scopes: dispatch.write
Freezes the plan and hands it to the control-test driver: the typed asset id must match, the plan must still be the reviewed one, the asset's maintenance window must cover the drive, and the battery must hold the first run's energy plus the margin (positive run first) or have that much room (negative run first) with telemetry arriving. The drive starts at the planned quarter hour without further prompts; the plan is published to the driver ten minutes before. Nothing is filed with a TSO.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
Request body · required
- confirm_asset_idREQUIRED
- stringThe asset id typed by the operator; must equal the session's asset.
- row_versionREQUIRED
- integerThe row version of the draft the operator reviewed; any adjustment since (plan, start, mode) is refused.
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/confirm" \
-H "Authorization: Bearer $EBX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"confirm_asset_id":"<string>","row_version":0}'import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/confirm",
headers={"Authorization": f"Bearer {token}"},
json={
"confirm_asset_id": "<string>",
"row_version": 0,
},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/confirm", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({"confirm_asset_id":"<string>","row_version":0}),
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 422validation failed
- 429rate limited
POST/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}/runs/{run_index}/rebuild-workbooks
Build a run's unusable workbooks again
dispatch.write
Required scopes: dispatch.write
Builds the workbooks of one run again from telemetry, for the products whose file the generator could not make a usable one of — a recording the telemetry store received late leaves gaps in it. A usable workbook is never rebuilt: its bytes are what was recorded. The files appear within the next poll cycle.
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
- run_indexREQUIRED
- integerRun index within the session: 0 = first run, 1 = second run.
curl -X POST "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/rebuild-workbooks" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.post(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/rebuild-workbooks",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/rebuild-workbooks", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"id": "<string>",
"onboarding_id": "<string>",
"purpose": "onboarding",
"asset_id": "<string>",
"status": "draft",
"execution_mode": "real",
"supersedes_session_id": "<string>",
"parameters": {
"first_direction": "positive",
"single_direction": false,
"extended_stroke_minutes": 0,
"extended_stroke_min_minutes": 0,
"extended_stroke_max_minutes": 0,
"second_run_extended_stroke_minutes": 0,
"capacity_mw": {
"positive": "<string>",
"negative": "<string>"
},
"products": {
"positive": [
"<string>"
],
"negative": [
"<string>"
]
},
"product_capacity_mw": "<string>",
"usable_energy_mwh": "<string>",
"energy_safety_margin": "<string>",
"energy_per_run_mwh": {
"positive": "<string>",
"negative": "<string>"
},
"soc_energy_margin": "0.05",
"ramp_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"fcr_ramp_required_mw_per_s": {
"positive": "<string>",
"negative": "<string>"
},
"measurement_point": "<string>",
"retention_minutes": 0,
"stroke_minutes": 0,
"stated_products": [
"<string>"
],
"stated_capacity_mw": "<string>"
},
"findings": [
{
"code": "<string>",
"severity": "blocker",
"message": "<string>"
}
],
"planned_start": "2026-10-22T20:30:00Z",
"planned_end": "2026-10-22T20:30:00Z",
"plan_sha256": "<string>",
"plan": "<string>",
"runs": [
{
"index": 0,
"direction": "positive",
"products": [
"<string>"
],
"requested_capacity_mw": "<string>",
"phases": [
{
"index": 0,
"kind": "retention",
"start": "2026-10-22T20:30:00Z",
"end": "2026-10-22T20:30:00Z",
"setpoint_mw": "<string>",
"window_id": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"status": "pending",
"follow_checks": [
{
"phase_index": 0,
"checked_at": "2026-10-22T20:30:00Z",
"expected_mw": "<string>",
"measured_mw": "<string>",
"passed": false,
"detail": "<string>"
}
],
"proofs": "<string>",
"workbooks": "<string>",
"uploads": "<string>",
"verdicts": "<string>",
"overrides": [
{
"product": "<string>",
"reason": "<string>",
"by_user_id": "<string>",
"by_user_name": "<string>",
"at": "2026-10-22T20:30:00Z"
}
],
"error": "<string>",
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z"
}
],
"driver": {
"request_id": "<string>",
"run_id": "<string>",
"status": "<string>",
"journal_written_at": "2026-10-22T20:30:00Z",
"tracking_source": "<string>",
"events": [
"<string>"
]
},
"progress": {
"current_run_index": 0,
"current_phase_index": 0,
"confidence": "confirmed",
"remaining_seconds": 0
},
"actual_start": "2026-10-22T20:30:00Z",
"actual_end": "2026-10-22T20:30:00Z",
"confirmed_by_user_name": "<string>",
"manual_zero_offered": false,
"manual_action_required": "<string>",
"handoff_at": "2026-10-22T20:30:00Z",
"alarms": [
{
"rule_id": "TELEMETRY-001",
"severity": "INFO",
"message": "<string>",
"opened_at": "2026-10-22T20:30:00Z",
"resolved_at": "2026-10-22T20:30:00Z"
}
],
"state_of_charge": {
"key": "<string>",
"label": "<string>",
"status": "ok",
"detail": "<string>"
},
"confirmed_at": "2026-10-22T20:30:00Z",
"aborted_at": "2026-10-22T20:30:00Z",
"abort_reason": "<string>",
"last_error": "<string>",
"last_error_at": "2026-10-22T20:30:00Z",
"row_version": 0,
"created_at": "2026-10-22T20:30:00Z",
"updated_at": "2026-10-22T20:30:00Z"
}- 400malformed request
- 401no valid token
- 403scope missing
- 404unknown or not visible
- 409state conflict
- 429rate limited
GET/v1/assets/{asset_id}/betriebsfahrt/sessions/{session_id}/runs/{run_index}/workbooks/{product}
Download a Betriebsfahrt workbook
dispatch.read
Required scopes: dispatch.read
A time-limited link to one Doppelhubkurve workbook the drive produced (one per product of the run).
Path parameters
- asset_idREQUIRED
- stringAsset identifier, e.g. DE_50HZ_WEM_PAS_1.
- session_idREQUIRED
- string<uuid>Betriebsfahrt session identifier from the status response.
- run_indexREQUIRED
- integerRun index within the session: 0 = first run, 1 = second run.
- productREQUIRED
- enum
fcr_positivefcr_negativeafrr_positiveafrr_negativeThe product whose workbook is wanted, named the way the drive was asked for it.
curl "https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/workbooks/fcr_positive" \
-H "Authorization: Bearer $EBX_TOKEN"import httpx
r = httpx.get(
"https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/workbooks/fcr_positive",
headers={"Authorization": f"Bearer {token}"},
)
print(r.json())const res = await fetch("https://api.ebx.energy/v1/assets/DE_BESS_01/betriebsfahrt/sessions/00000000-0000-0000-0000-000000000000/runs/0/workbooks/fcr_positive", {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await res.json();{
"filename": "<string>",
"download_url": "<string>",
"expires_at": "2026-10-22T20:30:00Z"
}