Which OCPP version am I dealing with?
There are three OCPP versions in the field, and you do not get to choose which one you are talking to. The station does — it is the client, and it offers the versions its firmware supports. All you can do is accept one of them.
This matters because the versions are not compatible. They are different protocols with overlapping vocabulary.
The three versions#
OCPP 1.6 (2015) is the workhorse. Most chargers in service speak it, and most chargers sold today still do. It is flat, simple and widely interoperable. Its limitations are real — no EVSE tier, three unrelated messages per transaction, a bare string for a credential — but everything supports it.
OCPP 2.0.1 (2020) is a redesign, not an increment. It introduces the
EVSE tier, the unified TransactionEvent message, structured id tokens, and a
device model that lets a CSMS discover a station's capabilities instead of
guessing. There was a 2.0 before it; it was withdrawn, and nobody should
implement it.
OCPP 2.1 (2024) extends 2.0.1 rather than replacing it. It adds bidirectional power transfer (vehicle-to-grid), DER controls, battery swapping, richer tariffs and local pricing. A 2.1 station is a 2.0.1 station with more functional blocks available.
| 1.6 | 2.0.1 | 2.1 | |
|---|---|---|---|
| Structure | connectors only | station → EVSE → connector | same as 2.0.1 |
| Transaction | 3 message types | one TransactionEvent | same as 2.0.1 |
| Transaction id | integer, CSMS allocates | string, station allocates | same as 2.0.1 |
| Credential | bare string, ≤ 20 chars | typed IdToken | same as 2.0.1 |
| Device model | none | components and variables | extended |
| Subprotocol | ocpp1.6 | ocpp2.0.1 | ocpp2.1 |
How the version gets picked#
The station lists what it can speak in the WebSocket handshake, in its own order of preference:
Sec-WebSocket-Protocol: ocpp2.0.1, ocpp1.6
The server picks one it supports and names it in the response. If it supports
none of them, it completes the handshake with no Sec-WebSocket-Protocol
header and closes the connection immediately — so a station that connects and
instantly drops is very often a version mismatch rather than a credentials
problem.
Flowion prefers ocpp1.6 when a station offers several. That is a
deliberate choice and worth understanding: our 1.6 implementation is the most
complete, so a station capable of both gets the version we support best rather
than the newest one. If you specifically want a station on 2.x, configure the
station to offer only that.
The negotiated version is recorded against the charger, so you can always see what a given station actually settled on rather than what its datasheet claims.
What Flowion does with each#
All three versions connect, boot, authorize, report status, run transactions and send meter values. Beyond that the coverage differs, because the newer versions have far more surface area to cover.
- 1.6 is the most complete. The Core and Remote Trigger profiles are finished, and local authorization list management is implemented.
- 2.0.1 and 2.1 cover the core flows — boot, authorization, status, transactions, meter values — plus the device model and the CSMS-initiated commands in scope so far.
- Large parts of 2.1's newer functional blocks — tariffs, smart charging, reservations, firmware and certificate management, bidirectional power, DER control, battery swapping — are not implemented.
A per-block coverage table, tracking all nineteen of OCPP 2.1's functional blocks with an honest status for each, is planned. Until it lands, do not infer support for a feature from a version number appearing in this list.
What this means for your integration#
Mostly, nothing. This is the point of Flowion's design: the REST API talks about chargers, EVSEs, connectors, transactions and id tokens, and none of those names belong to a particular OCPP version. You can run a mixed fleet and write one integration.
Where version does leak through, it does so deliberately, because pretending otherwise would mean lying to you:
Some commands cannot be expressed on some wires. A reset scheduled for "when the station goes idle" is a 2.x concept; 1.6 has no way to say it. Rather than silently sending something else, the request fails and tells you the wire cannot carry it. The console goes further and only offers a charger the options its negotiated version can honour.
Some fields are genuinely absent. A 1.6 station never reports what type of credential was presented, or why it sent a particular transaction event. Those fields are empty rather than filled with a plausible guess — "1.6 did not say" stays distinguishable from a real value.
A few 1.6 values have no modern equivalent. Temperature and RPM readings, for instance, have no 2.x measurand to map onto. They are reported as unmapped and logged rather than silently dropped.
How Flowion maps onto OCPP is the full account of where the translation is lossless and where it is not.
A note on security profiles#
OCPP 1.6 defines three security profiles. Flowion currently implements Security Profile 2: HTTP Basic authentication over TLS. The station presents its identity as the username and a key as the password, on an encrypted connection.
Security Profile 3 — TLS client certificates, with no shared password — is not implemented yet. If your hardware requires it, it will not connect today.
Unencrypted OCPP is not supported and should not be used anywhere. The specification itself says a CSMS should not listen for unencrypted connections from the internet.
Practical advice#
- Check what a station negotiated, not what its spec sheet says. Firmware configuration decides this, and a 2.0.1-capable station configured to offer 1.6 will speak 1.6.
- Do not plan a migration around a version bump. 1.6 to 2.0.1 is a different protocol, usually a firmware upgrade, and sometimes not available for hardware already installed.
- Expect a mixed fleet indefinitely. Chargers last ten years. Any fleet that grows over time will contain several versions at once, which is exactly the situation the protocol-neutral API is designed for.