API reference
Authentication
The API is protected by OpenID Connect. Machine-to-machine (M2M) integrations exchange a client ID and secret for an access token, and send that token as a bearer token on every request.
- Token endpoint
- https://auth.ebx.energy/oauth2/token
- Client auth
- client_secret_basic · client_secret_post
Get a token
You need two things before your first call: credentials you create once, and a short-lived access token you exchange them for. No request works without the token.
- 01
Sign in to your account
Sign in with your Control Center account, using email and password or Google SSO.
No account yet? Contact operations@ebx.energy.
- 02
Create client credentials
Create one integration per system that calls the API, and grant it only the scopes it needs. Creating an integration requires the
clients.writescope. You can only grant scopes you hold yourself (verify them withGET /v1/users/me).To give a partner access to only some of your assets, add
asset_idsto the request. The integration then reaches only those assets, and its scopes must be exactlyasset.read. Any other asset answers404.Example requestEBX_USER_TOKEN="<user token from step 1>" INTEGRATION=$(curl -s -X POST "https://api.ebx.energy/v1/integrations" \ -H "Authorization: Bearer $EBX_USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-integration", "scopes": ["asset.read", "dispatch.read"]}') EBX_CLIENT_ID=$(echo "$INTEGRATION" | jq -r .integration_id) EBX_CLIENT_SECRET=$(echo "$INTEGRATION" | jq -r .client_secret)import httpx user_token = "<user token from step 1>" res = httpx.post( "https://api.ebx.energy/v1/integrations", headers={"Authorization": f"Bearer {user_token}"}, json={"name": "my-integration", "scopes": ["asset.read", "dispatch.read"]}, ) integration = res.json() client_id = integration["integration_id"] client_secret = integration["client_secret"]const userToken = "<user token from step 1>"; const created = await fetch("https://api.ebx.energy/v1/integrations", { method: "POST", headers: { Authorization: `Bearer ${userToken}`, "Content-Type": "application/json", }, body: JSON.stringify({ name: "my-integration", scopes: ["asset.read", "dispatch.read"] }), }); const { integration_id: clientId, client_secret: clientSecret } = await created.json();Example response{ "integration_id": "4kq1v2l9h8t3m5n7p0r6s2u8wx", "client_secret": "1m9c0f7e2d...", "name": "my-integration", "organization_id": "your-org", "scopes": ["asset.read", "dispatch.read"] }Store the
integration_id(your client ID) andclient_secret. The secret is shown only once. To rotate it, add a second secret, then delete the old one. - 03
Exchange them for an access token
POST the
client_credentialsgrant to the token endpoint. Bothclient_secret_basic(shown) andclient_secret_postare accepted.Example requestEBX_ACCESS_TOKEN=$(curl -s -X POST "https://auth.ebx.energy/oauth2/token" \ -u "$EBX_CLIENT_ID:$EBX_CLIENT_SECRET" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ | jq -r .access_token)import httpx res = httpx.post( "https://auth.ebx.energy/oauth2/token", auth=(client_id, client_secret), data={"grant_type": "client_credentials"}, ) access_token = res.json()["access_token"]const exchanged = await fetch("https://auth.ebx.energy/oauth2/token", { method: "POST", headers: { Authorization: `Basic ${btoa(`${clientId}:${clientSecret}`)}`, "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ grant_type: "client_credentials" }), }); const { access_token: accessToken } = await exchanged.json();Example response{ "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjNiZTk...", "token_type": "Bearer", "expires_in": 3600 }The token is valid for 60 minutes. Cache it and send it on every request until it expires, then exchange again. Don't request a token per call.
- 04
Call the API
Send the access token in the
Authorizationheader. It only reaches your own organisation's assets, and an integration created withasset_idsonly the assets listed there. Each endpoint in the reference states the scope it requires: a token without it gets403, a missing or expired one401.Example requestcurl "https://api.ebx.energy/v1/assets" \ -H "Authorization: Bearer $EBX_ACCESS_TOKEN"import httpx res = httpx.get( "https://api.ebx.energy/v1/assets", headers={"Authorization": f"Bearer {access_token}"}, ) print(res.json())const listed = await fetch("https://api.ebx.energy/v1/assets", { headers: { Authorization: `Bearer ${accessToken}` }, }); const assets = await listed.json();
Scopes
Scopes are granted per integration when you create it, and carried in its access token. What each group unlocks:
Assets
asset.read · asset.write
Assets and their state and state history, curtailment, planned state, virtual segmentation, unavailability, power and energy limits, and backup-market defaults and bids.
Dispatch
dispatch.read · dispatch.write
Immediate and scheduled dispatch, forecasts and control tests.
Markets
market.read · market.write
FCR and aFRR bids, the valid bid intervals and the auction results.
Restrictions
restrictions.review · restrictions.approve · restrictions.write_direct
Reviewing and approving restriction requests. write_direct applies a restriction without going through approval.
Alarms
alarm.read · alarm.write
Alarm history, active alarms and the rule catalogue; acknowledging and muting.
Onboarding
onboarding.read · onboarding.write
Onboarding workflows and their tasks: gates, DPL rows, RfI fields, documents and prequalification submission. Only EBX grants onboarding.write.
Reporting
report.read · invoice.read
Dispatch and performance reports, settlement over a range, and invoices.
Organisation
users.read · users.write · clients.read · clients.write
Users in your organisation and their scopes, and M2M integrations and their secrets.
Pools
pool.read · pool.write
Organisation pools and pool state. Beta, and for users only: not grantable to integrations.