First public version of the EBX API, spanning asset onboarding through market participation to reporting and settlement, and the baseline for this changelog. No changes are listed, there is no earlier version to compare against.
API reference
Changelog
What changed between API versions, and what changed inside the version you are on.
Changes to v1
2026-10-147 BREAKING
BREAKINGGET/v1/settlements
The settlement response has a new shape; update any client that reads it. Every block carries all eight products, fcr, afrrc_pos, afrrc_neg, afrre_pos, afrre_neg, backup_fcr, backup_afrr_pos and backup_afrr_neg, each as revenue, collateralization, penalty and net, where net is revenue less the two costs and every line multiplies out to its amount; this replaces the 4-hour and 15-minute aFRR capacity keys and the separate backup entries. Capacity revenue is paid on delivered volume, as bid_mw, accepted_mw, delivered_mw, hours and price_per_mw_h in place of capacity_mw, price_per_mw and total, and aFRR energy revenue splits into market and shadow lines that add up to it. Organisation and asset sums move under totals, each block gains ltu_mw, and status is now optional: omit it for the latest state of every block.
BREAKINGGET/v1/assets/{asset_id}/tso-poolPATCH/v1/assets/{asset_id}/tso-pool
GET and PATCH /v1/assets/{asset_id}/tso-pool, added on 2026-10-05, are withdrawn, and tso.read and tso.write can no longer be granted to integrations. An asset's FCR and aFRR pool membership is managed by EBX: remove any call to these endpoints, and ask EBX to move an asset into or out of a pool. fcr_enabled, afrr_enabled and tso_connected stay deprecated and ignored on create and update.
BREAKINGGET/v1/assets/{asset_id}/backup_markets/afrr/bidsPOST/v1/assets/{asset_id}/backup_markets/afrr/bidsGET/v1/assets/{asset_id}/backup_markets/afrr/defaultPUT/v1/assets/{asset_id}/backup_markets/afrr/default
Nothing changes on the wire. product on aFRR backup-market defaults and bid overrides is now published as pos or neg: na, which these endpoints already refused, is gone, and with it the CollateralizationProduct schema. When you regenerate a client, replace references to CollateralizationProduct.
rule_id can now carry six more rules, BETRIEBSFAHRT-001 to BETRIEBSFAHRT-006, raised by a Betriebsfahrt that hits a fault, fails a check or ends without its drive; GET /v1/alarm-rules describes each one. If you match rule_id against a fixed list, add these, or handle a rule you do not recognise instead of failing on it.
A Betriebsfahrt no longer ends itself on a fault. A driver fault, a failed check or a drive outside its allowance now raises a BETRIEBSFAHRT alarm, and the operator decides whether to abort; each session lists the alarms it raised, oldest first, in alarms. If you match their rule_id against a fixed list, add BETRIEBSFAHRT-001 to BETRIEBSFAHRT-006.
Fault evidence no longer carries unavailability_hours, because a fault hold no longer has a fixed length: it is chosen when the fault is declared, so read the hold's end_time instead. The active hold gains power_limit, set when the asset is held to part of its power rather than taken out, and each notification recipient gains language, the language their fault notifications are written in.
An onboarding's history can now carry gate_skipped, recorded for each gate a transition onboarding starts out skipped. If you match event_type against a fixed list, add it, or handle a type you do not recognise.
An integration can now be restricted to some of your organisation's assets, for example to give a partner read access to one asset: pass asset_ids when you create it. A restricted integration is read-only and must hold exactly asset.read. The list shows asset_ids for each restricted integration and omits it for one that reaches every asset.
ADDEDGET/v1/assets/{asset_id}/backup_markets/fcr/bidsPOST/v1/assets/{asset_id}/backup_markets/fcr/bidsDELETE/v1/assets/{asset_id}/backup_markets/fcr/bids/{bid_id}GET/v1/assets/{asset_id}/backup_markets/fcr/defaultPUT/v1/assets/{asset_id}/backup_markets/fcr/default
FCR now has backup markets too. Set an asset's FCR backup-market default with PUT /v1/assets/{asset_id}/backup_markets/fcr/default, and override it for a window with a bid under /v1/assets/{asset_id}/backup_markets/fcr/bids. They work as the aFRR endpoints do, but take no product, since FCR is symmetric.
ADDEDGET/v1/assets/{asset_id}/markets/afrr/capacity/resultsGET/v1/assets/{asset_id}/markets/afrr/energy/resultsGET/v1/assets/{asset_id}/markets/fcr/results
Every FCR, aFRR capacity and aFRR energy result now names the bid it was built from, in bid_id. Use it to match a result to its bid: start_time, end_time and product alone are ambiguous when several bids share an interval and product.
A fault can now hold an asset to the power it still has instead of taking it out entirely: pass power_limit, the upper and lower bounds in MW. amend narrows an active hold after a further loss, and unavailability_hours sets how long the hold lasts, from 1 to 720 hours, 8 by default. The message draft takes the same fields, so the reviewed text describes what is declared.
Read one TSO pool's alarm history over a time range: the history counterpart of GET /v1/alarms/active/fleet, under alarm.read. Like every pool alarm, it is restricted to EBX.
For an open alarm, the triage context reports what the asset can still do: its available power and energy per direction against nameplate, the bounds a partial fault would declare, and the other assets in its pool with their alarms. It is read-only and needs alarm.write.
Onboardings now say whether they are a transition, in is_transition: an asset already live elsewhere, onboarded to produce its PQ application. A transition links the existing asset, starts with the hardware, software and monitoring-test gates skipped, and sends no invitations. Only EBX can create one, and for every other onboarding is_transition is false.
ADDEDPATCH/v1/onboarding-tasks/{onboarding_id}/rfiGET/v1/onboarding-tasks/{onboarding_id}/rfi-catalogues/{catalogue}PATCH/v1/onboardings/{onboarding_id}/rfiGET/v1/onboardings/{onboarding_id}/rfi-catalogues/{catalogue}
The RfI can record PQ list selections, such as the TSO, DSO or balance group, in pq_selections, picked from the PQ portal's catalogues. Read a catalogue with GET /v1/onboardings/{onboarding_id}/rfi-catalogues/{catalogue}, or under /v1/onboarding-tasks when you hold the task.
Promoting a reviewed draft to the official PQ application now starts with POST /v1/onboardings/{onboarding_id}/pq-official/confirmation, and the official sync carries its confirmation_id. When the sync review reports hierarchy_repair, send confirm_hierarchy_repair as true on both the confirmation and the sync, and leave it false otherwise; a mismatch is refused with 409, and only EBX can confirm a hierarchy repair. Test runs and sync reviews report input_issues, and a sync review also reports the DSO and any resources missing from the PQ portal.
2026-10-055 BREAKING
rule_id can now carry three more rules, DISPATCH-015, DISPATCH-016 and DISPATCH-017; GET /v1/alarm-rules describes each one. rule_id is also now typed as a known rule or any other string, so a rule added later reaches a client generated from the spec as a plain string instead of failing validation. If you match rule_id against a fixed list, add these three, or handle a rule you do not recognise instead of failing on it.
Nothing changes on the wire. The schema names announced on 2026-09-28 have landed: UnavailabilitySchema has its name back, and the energy-limit, power-limit and scheduled-dispatch items lose their module paths to become EnergyLimitSchema, PowerLimitSchema and PowerEventResponseSchema. Planned state uses PlannedStateEnergyLimitSchema, PlannedStatePowerLimitSchema, PlannedStatePowerEventResponseSchema and UnavailabilitySchema, and the daily report's unavailabilities are DailyReportUnavailabilitySchema. Regenerate once, and replace any reference to an api__routes__schemas__… name.
Nothing changes on the wire. The discussion thread on an RfI field or a DPL row was published as two identical schemas, DiscussionThread-Input and DiscussionThread-Output, and is now one, DiscussionThread. When you regenerate a client, replace references to either old name.
BREAKINGPUT/v1/assets/{asset_id}
The update request's numeric limits are now published as standard minimum, maximum and exclusiveMinimum; before, they appeared as ge, le and gt, which validators ignore. The limits themselves are unchanged and were already enforced, so a client that validates against the spec now catches before sending what the API already refused with 422. fcr_enabled, afrr_enabled and tso_connected are deprecated and ignored on update; switch pools with PATCH /v1/assets/{asset_id}/tso-pool instead.
Scope gains inventory.read, inventory.write and inventory.admin. They are for EBX staff and cannot be granted to your users or integrations, so nothing you send or receive changes; a client generated from the spec only gets three more values in the enum.
An asset's FCR and aFRR pool membership has its own endpoint. GET /v1/assets/{asset_id}/tso-pool reads it under tso.read, PATCH switches either pool under tso.write, and both scopes can now be granted to integrations. Out of a pool, the asset's share of that market is covered by the other assets and none of its data for that market is sent to the TSO; bids are still accepted. On the asset itself, fcr_enabled and afrr_enabled are now read-only and deprecated, tso_connected is deprecated, and all three are ignored on create and update. They will be removed in v2.
Setting, shortening or deleting a power limit, energy limit or unavailability directly now needs restrictions.write_direct alone; asset.write is no longer required as well. A user or integration that holds restrictions.write_direct without asset.write was refused before and is accepted now.
ADDEDGET/v1/onboarding-tasks/{onboarding_id}/documents/{document_id}/downloadGET/v1/onboardings/{onboarding_id}/documents/{document_id}/download
Onboarding documents now download as an attachment under the file name they were uploaded with. Pass inline=true to preview a PDF, PNG, JPEG or WebP in the browser instead; any other type is refused with 409.
Set one PQ-workbook field of an onboarding's RfI, such as the planned CoD, without sending the whole RfI. Every other value stays as stored, so the call cannot overwrite an edit saved in between. It needs onboarding.write, and the value is validated and attributed as in a full RfI update.
Lists the same onboardings as GET /v1/onboardings, with the same filters and visibility, but only where each one stands: its asset, overall status, current gate, and each gate's status and deadline in process order. Use it to find an onboarding, then fetch the full record by id. It needs onboarding.read.
Whoever holds an onboarding's EMS task can now set the EMS connection with PATCH /v1/onboarding-tasks/{onboarding_id}/ems-network, which changes only the network fields it is sent. The task view carries asset_id and ems_network, and the RfI network configuration gains gateway and subnet_mask, empty until set.
Each RfI field's metadata now records who last changed it, in last_changed_by: a user_id, the email where known, and kind, user or system. It is null until a change has been attributed.
ADDEDGET/v1/users/me
Your own profile now carries organization_name, your organisation's registered company name or null, and analytics_allowed, whether EBX's usage analytics may record you. It is false for integrations and for anyone who has opted out.
Prequalification with the TSO is now prepared in two workspaces, training and official. Choose the target and a contract per reserve type, run test submissions and curve checks in training, review each sync before it updates a draft, and promote a reviewed draft to the official application. Reading needs onboarding.read and every change onboarding.write. The PQ profile also gains delivery_duration_caps_hours, an optional cap per PQ product on the delivery duration the application requests.
2026-09-284 BREAKING
rule_id can now carry seven more rules. CAPACITY-004 and POOL-012 flag an accepted aFRR capacity award that accepted aFRR energy bids do not cover, for an asset and for a TSO pool; FCR-005 flags FCR active beyond its award; POOL-008 to POOL-011 check what EBX reports to the TSO for a pool. If you match rule_id against a fixed list, add these, or handle a rule you do not recognise instead of failing on it.
calendar.read and calendar.write are no longer scopes. Sending either when you create a user or an integration, or set a user's scopes, is now rejected with 422; responses no longer list them, and a token that still carries them keeps working until it expires.
An onboarding's history can now carry two more event types: cod_changed, recorded each time the planned CoD moves, and onboarding_aborted, recorded when an onboarding is aborted, with the reason as its note. If you match event_type against a fixed list, add both, or handle a type you do not recognise.
BREAKINGGET/v1/assets/{asset_id}/planned_stateGET/v1/assets/{asset_id}/unavailabilityPOST/v1/assets/{asset_id}/unavailabilityPATCH/v1/assets/{asset_id}/unavailability
Nothing changes on the wire: the unavailability windows these return are the same JSON as before. Only the schema's name in the OpenAPI document changed, from UnavailabilitySchema to api__routes__schemas__asset__unavailability__UnavailabilitySchema, so a client generated from the spec gets the type under that name. A coming release restores the name UnavailabilitySchema. The same release drops the module paths the energy-limit, power-limit and scheduled-dispatch schemas have carried from the start: they become EnergyLimitSchema, PowerLimitSchema and PowerEventResponseSchema, and planned state's own versions PlannedStateEnergyLimitSchema, PlannedStatePowerLimitSchema and PlannedStatePowerEventResponseSchema. If you can, wait for it before regenerating, so your references change only once.
Two reports for an asset, under report.read. The daily report covers one UTC day, today by default: aFRR activations with their fill rate, dispatch overrides, unavailabilities, alarms and the settled aFRR capacity revenue, which reads null, not zero, until a settlement result exists. The performance timeline gives activation metrics and settled capacity revenue per fifteen_minutes, hourly or daily bucket over [start_time, end_time): the last 24 hours by default, and at most 90 days.
EBX can now share notes and open points with the asset owner during an onboarding. Onboarding responses carry them in notes and shared_open_points, and the asset owner's task view in shared_notes and shared_open_points, each open point with its owner, due date, decision and comment thread. The asset owner can reply on an open point until EBX closes it with a decision; after that, a reply is refused with 409.
An onboarding can now be aborted, with a reason and optionally a category; the reason is recorded in its history. An abort is final. It is refused with 409 while a Betriebsfahrt session drives the asset or a PQ request is in flight, and for an onboarding that is already completed or aborted.
A block can record its cause: category, one of the open points' six topics, and waiting_on, the party that has to act. Both are optional, so a block without them is accepted as before.
Betriebsfahrt sessions now say when the plan is handed over for execution (handoff_at) and whether the drive asks for manual action, such as zeroing the battery (manual_action_required; manual_zero_offered says whether aborting sends that zero). A draft also shows whether the battery holds the first run's energy plus the plan's margin (state_of_charge, soc_energy_margin), which is what confirming it now checks, rather than a full or an empty battery.
EDITORIALPUT /v1/assets/{asset_id}/dispatch/scheduled no longer promises that wholesale targets are trimmed at execution time to the power left after reserved FCR and aFRR capacity: that reduction was removed. A conflict with a reservation is caught when the schedule is submitted, as a rejection or a warning depending on the asset's strict_validation, and a target accepted with a warning is no longer cut back against the reservation at delivery. In GET /v1/assets/{asset_id}/reports/schedule/dispatches, clipped now marks any shortfall between the requested and the executed magnitude, which is rare, and message gives no reason for it.
EDITORIALGET /v1/balancing-groups/afrr/schedules now documents its precision: newly generated publications use 0.001 MW for assets and zone totals, and revisions already exchanged keep the precision they had. Only the complete set of assets in a balancing group sums to the zone total, so a filtered or owner-only view does not; do not round or net the two directions together.
2026-09-181 BREAKING
The activity field is gone from every onboarding response. Read an onboarding's history from GET /v1/onboardings/{onboarding_id}/activity instead, following next_cursor until it is null. Nothing was deleted — the same records are still written, and the new endpoint returns them.
An onboarding's history is now its own paginated endpoint, returning items and next_cursor newest first. limit defaults to 100 and accepts 1 to 500. It needs the onboarding.read scope, and internal tracker events are omitted for readers outside EBX.
EDITORIALPOST /v1/assets/{asset_id}/dispatch/immediate now carries a horizon: 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. While an asset is in phase 1 of the validation rollout the dispatch is still created and the breach comes back in warnings as IMMEDIATE_DISPATCH_TOO_LONG; once strict_validation is on for the asset it is refused with 422. Shorten the window, or move it to the schedule, before that happens.
2026-09-17ADDITIVE
Immediate dispatch now takes the same control payload as scheduled dispatch: control_mode, with bess_power_mw and poc_power_mw. Nothing needs to change today — set_power_mw is still accepted and maps to bess_power_mw, and control_mode defaults to bess_only. It is deprecated and no longer required, so send bess_power_mw instead when convenient. Sending set_power_mw together with bess_power_mw or power_mw is rejected.
The dispatch these return now carries control_mode, bess_power_mw, poc_power_mw and pv_power_mw alongside the deprecated set_power_mw. control_mode is one of passive, bess_only, bess_and_poc or poc_only; bess_and_poc and poc_only apply to co-located assets, and pv_power_mw is reserved and always null.
Assets carry an ancillary-service ramp pair, as_max_pos_ramp_rate_mw_per_sec and as_min_neg_ramp_rate_mw_per_sec, in MW/s like the wholesale pair. Both are optional and read null until an asset is backfilled; treat null as "use the wholesale ramp".