OCPP gets a charging station talking to its operator's backend. OCPI (Open Charge Point Interface) is what lets that operator's network talk to everyone else's: the mobility service providers whose apps and RFID cards drivers actually carry, the roaming hubs that connect hundreds of parties, and the navigation and smart-charging services that need live data. If OCPP is the device protocol, OCPI is the business-to-business protocol.
This series walks through OCPI 2.2.1 from an implementer's point of view. We cover the handshake, the transport rules, each functional module, and finally how a CSMS turns OCPP events into OCPI objects. The examples are real HTTP requests and JSON bodies in the shape the specification defines, along with the interop problems that tend to show up when two independently built implementations meet for the first time.
What OCPI is, and what it is not
OCPI is a REST/JSON protocol over HTTPS, maintained by the EVRoaming Foundation. Every party exposes HTTP endpoints, and every party also acts as an HTTP client calling the other side. There are no WebSockets and no persistent connections: each exchange is an ordinary request and response, authenticated with a token in the Authorization header.
It is not a device protocol. Nothing in OCPI talks to a charger directly. A START_SESSION command sent by an eMSP lands on the CPO's backend, and the CPO's CSMS turns it into an OCPP RemoteStartTransaction (or RequestStartTransaction in 2.0.1). Keeping that boundary clear in your architecture saves a lot of confusion later. For a broader map of where OCPI sits next to OCPP, OICP, OpenADR and ISO 15118, see EV charging protocols explained.
Roles
OCPI 2.2 introduced an explicit Role enum. A single platform can hold several roles, and each role is identified by a country_code (ISO 3166-1 alpha-2) plus a three-character party_id, for example NL/EXA.
| Role | Who it is | Typical responsibilities |
|---|---|---|
CPO | Charge Point Operator | Owns the chargers. Publishes locations, tariffs, sessions and CDRs; receives tokens and commands. |
EMSP | e-Mobility Service Provider | Owns the driver relationship. Publishes tokens, authorizes, sends commands, receives sessions and CDRs, bills the driver. |
HUB | Roaming hub | Sits between many CPOs and eMSPs and routes messages so each party needs one connection instead of dozens. |
NSP | Navigation Service Provider | Consumes location and availability data for maps and route planning. |
NAP | National Access Point | A government data portal that collects location data. |
SCSP | Smart Charging Service Provider | Sends charging profiles to influence how much power a session draws. |
OTHER | Anything else | Catch-all for parties that do not fit the roles above. |
In practice most integrations are CPO-to-eMSP, either directly or through a hub. Many companies are both: a CPO that also runs a driver app is a CPO and an eMSP at the same time, and OCPI 2.2 lets it register both roles over a single connection.
Modules and interface roles
Functionality is split into modules. Each module has a Sender interface and a Receiver interface (the InterfaceRole values SENDER and RECEIVER). The Sender is the owner of the data; the Receiver is the party the data is about to be shared with. A CPO is the Sender of Locations, and the eMSP implements the Receiver side so the CPO can push updates to it.
| Module ID | Owner (Sender) | Purpose | Covered in |
|---|---|---|---|
credentials, versions | Both | Discovery and registration | Part 2 |
locations | CPO | Sites, EVSEs, connectors, live status | Part 4 |
tokens | eMSP | RFID cards and app users, real-time authorization | Part 5 |
sessions | CPO | Live charging sessions | Part 6 |
cdrs | CPO | Charge Detail Records for billing | Part 6 |
tariffs | CPO | Prices the CPO charges the eMSP | Part 7 |
commands | eMSP sends, CPO executes | Remote start/stop, reservations, unlock | Part 8 |
chargingprofiles | SCSP sends, CPO executes | Power limits for active sessions | Part 9 |
hubclientinfo | Hub | Which parties are reachable via the hub | Part 9 |
Only credentials and versions are mandatory. Everything else is negotiated: during registration each side publishes which modules it implements and in which interface role, and the other side should only call what was advertised.
Versions: 2.1.1, 2.2 and 2.2.1
You will meet three versions in production. OCPI 2.1.1 is old but still widely deployed. OCPI 2.2 added the role model, hubs and routing headers, the Charging Profiles and Hub Client Info modules, and changed a number of objects (sessions got a PENDING status, tariffs got a type, CDRs got cdr_location and several cost totals). OCPI 2.2.1 is a maintenance release of 2.2 that fixes errata and clarifies ambiguous text; for most purposes treat it as "2.2 done properly" and target it for new work. Newer releases have followed since, but 2.2.1 remains the common baseline in roaming agreements, and the concepts in this series carry forward.
Peer-to-peer vs hub
In a peer-to-peer setup a CPO and an eMSP register directly with each other. Each bilateral connection needs its own commercial agreement, credentials handshake, testing and monitoring. That is manageable for a handful of partners and painful for fifty.
With a hub, every party connects once to the hub, and the hub forwards messages between them. OCPI 2.2 made this a first-class part of the protocol: requests carry routing headers (OCPI-to-party-id, OCPI-from-country-code, and so on) so the hub knows where to deliver them, and the hub publishes which parties are connected through the Hub Client Info module. The trade-off is that you inherit the hub's quirks, timeouts and validation rules. The commercial side of this choice is covered in EV roaming explained.
Push vs pull
Most data modules support both directions:
- Pull: the Receiver calls the Sender's
GETendpoint, typically withdate_fromto fetch only what changed since the last sync. Paginated and simple, but data is only as fresh as the polling interval. - Push: the Sender calls the Receiver's
PUT/PATCH/POSTendpoints as soon as something changes. Near real-time, but if a push fails and nobody retries, the Receiver silently drifts out of sync.
Robust implementations do both: push for freshness, plus a periodic pull with date_from as a reconciliation safety net. EVSE status is the obvious case where push matters; a map that shows a charger as available when it has been occupied for ten minutes generates support calls.
A session, end to end
To make the pieces concrete, this is what a typical roaming charge looks like from the CPO's perspective:
- Long before the driver arrives, the CPO and eMSP have registered with each other. The CPO has pushed its
Locationobjects andTariffobjects; the eMSP has pushed itsTokenobjects (or the CPO pulled them). - The driver taps an RFID card. The charger sends OCPP
Authorize. The CSMS checks its local copy of the eMSP's tokens, or makes a real-timePOST .../tokens/{uid}/authorizecall to the eMSP. - Charging starts. The CPO pushes a
Sessionwith statusACTIVEto the eMSP and updates it withPATCHas energy is delivered. The EVSE status changes toCHARGINGand is pushed via Locations. - The driver stops. The session becomes
COMPLETED, and the CPO posts aCDRwith the final energy, time and cost to the eMSP. - The eMSP bills the driver; the CPO invoices the eMSP based on the CDRs, outside OCPI.
Every one of these steps has failure modes, and those are where most of the engineering effort goes. The rest of this series takes them one at a time.
Key takeaways
- OCPI is HTTPS + JSON between backends. It never talks to chargers; your CSMS bridges OCPP and OCPI.
- Parties are identified by
country_code+party_idper role; one platform can hold several roles. - Each module has a Sender (data owner) and Receiver. Only implement and call what has been advertised in version details.
- Target 2.2.1 for new work, but expect to keep 2.1.1 connections alive for some partners.
- Use push for freshness and pull with
date_fromfor reconciliation.
Next: Part 2, versions and the credentials handshake. If you are planning an OCPI integration on top of an OCPP platform, our EV charging team builds and operates exactly this stack.

