Set up your workspace
The Quickstart moved fast. This page covers the same ground properly: what each object is for, which decisions are permanent, and which of them you will regret guessing at.
Getting an access token#
Every endpoint except the health check expects a bearer token:
Authorization: Bearer <token>
There is no way to issue one for yourself yet. No self-service sign-up, no API keys, no service accounts, no client-credentials flow. Tokens today come from a signed-in console session, which means an interactive login by a human.
If you are running the platform yourself, sign in to the console at
http://localhost:3000/sign-inand read theidTokenfromhttp://localhost:3000/api/auth/session. That is a development convenience, not an integration path — the token is short-lived and tied to a browser session.Machine-to-machine credentials are the next thing to be built here.
Tokens carry the organization they speak for. A token is not a global key; it authenticates a user, and what that user may do depends on their role in the organization owning the resource.
Creating an organization#
curl -X POST https://api.flowion.io/v1/organizations \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Charging", "slug": "acme"}'
Both fields are required, and they are genuinely different things.
name is free-form and for humans. Change it whenever.
slug is a URL-safe identifier you choose. Lowercase alphanumeric
segments joined by single hyphens, 1–63 characters — a DNS label. It is
globally unique, and 409 means someone else has it.
The slug matters more than it looks, because it is the first path segment of every charger's connection URL:
wss://gateway.flowion.io/ocpp/acme/CP-01
It can be changed later, but changing it invalidates the URL every station in your fleet is configured with. Treat it as permanent.
Creating an organization also creates an environment named prod, marks it
default, and makes you the Owner — the only Owner, assigned at creation.
There is no transfer operation, and adding a member with the role owner is
rejected.
Adding environments#
You get prod for free. Add others as you need them:
curl -X POST https://api.flowion.io/v1/organizations/$ORG/environments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Staging", "slug": "staging"}'
Environment slugs are unique within an organization, not globally.
Two settings are worth configuring early, via PATCH on the environment:
time_zone— an IANA name such asEurope/Stockholm, the zone an operator reads timestamps in. Does not affect how the API stores or returns them.price_zone— an ENTSO-E bidding zone EIC code, which day-ahead electricity prices are fetched against. Unset means no price data.
Environments covers how many you should have and why moving a charger between them is a hardware job.
Provisioning a charger#
curl -X POST https://api.flowion.io/v1/environments/$ENV/chargers \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"charge_point_id": "CP-01"}'
Only charge_point_id is required. It is the identity the station uses on the
wire and the last segment of its connection URL, so it has to match whatever
the hardware is configured with — exactly, including case.
It is unique per environment, not globally. You may have CP-01 in both
staging and prod.
Choose a scheme that survives contact with reality. Serial numbers are stable but meaningless; location-based ids read well until a charger is moved. Many operators use a site prefix plus a number, and accept that a relocated unit keeps its original name.
The authorization key#
The response contains an authorization_key, a 40-character hex string. It is
the password half of the station's credentials.
It is returned exactly once. Only an Argon2id hash is stored. There is no endpoint that returns it again, because there is nothing to return it from.
If you lose it, rotate it:
curl -X POST https://api.flowion.io/v1/chargers/$CHARGER/credential \
-H "Authorization: Bearer $TOKEN"
This returns a fresh key and invalidates the old one — which means the station cannot reconnect until it is reconfigured. Rotating the credential of a charger you cannot physically reach will take it offline until you can.
Withholding acceptance#
A charger is created with registration_status: "Accepted", meaning it may
operate as soon as it connects. You can change that with a PATCH:
| Status | Effect on a connected station |
|---|---|
Accepted | Operates normally. |
Pending | Stays connected and answers you, but sends nothing of its own and will not start transactions. |
Rejected | Goes silent entirely — stops sending and stops answering until its retry interval elapses. |
Pending is the useful one: it is how you hold a newly installed station
still while you configure it.
Rejected is a bigger hammer than it looks. A rejected station stops
responding to you at all, so you cannot command your way back out of it. See
Lifecycle of a charging session.
Sites and circuits#
Neither is required to get a charger online, and you can add both later.
A site is a physical location. It groups chargers for reporting and can override the environment's price and time zones — which is what you want when one environment spans countries.
A circuit is the electrical wiring, as a tree per site. It exists so the
platform can keep a site within its grid connection. Each circuit records a
phase count (1 or 3) and a rated current; single-phase circuits also record
which line — L1, L2 or L3 — they are wired to.
Two invariants are enforced when you write, not when you read:
- A child's lines must be a subset of its parent's.
- Children's rated current, summed per line, must not exceed the parent's rating on that line.
So a circuit tree that oversubscribes its own wiring cannot be persisted. If a write is rejected, the tree you described is not physically buildable.
Circuit membership is per-EVSE, not per-charger, because a multi-EVSE station's EVSEs can draw independently.
Inviting people#
Members belong to the organization, not to an environment:
curl -X POST https://api.flowion.io/v1/organizations/$ORG/members \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"user_id": "...", "role": "admin"}'
Roles are owner, admin and user, lowercase on the wire.
Owners and Admins reach every environment implicitly. A member with role
user reaches only the environments explicitly granted to them, through the
environment-access endpoints — which is how you give a contractor staging
and nothing else.
A sensible order#
- Create the organization. Choose the slug carefully.
- Rename or keep
prod; addstagingif you want somewhere to break things. - Set the time zone, and the price zone if you want price data.
- Provision one charger and get it connected before provisioning fifty.
- Add sites and circuits once you know the physical layout.
- Invite people, granting
userrole members only the environments they need.
Next: Connect a simulated charger, or Connect a real charger.