PHP2Shell alert: critical PHP flaws CVE-2026-109618 / 109619 — check your servers and get patched fast
HashTech Systems
EV Charging
Part 1 of 10

OCPI 2.2.1 Tutorial: Roles, Modules and How EV Roaming Works

What OCPI is, the roles it defines (CPO, eMSP, Hub, NSP, SCSP), its modules, version history, and the push vs pull and hub vs peer-to-peer choices.

HashTech Engineering · October 26, 2026 · 6 min read

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.

RoleWho it isTypical responsibilities
CPOCharge Point OperatorOwns the chargers. Publishes locations, tariffs, sessions and CDRs; receives tokens and commands.
EMSPe-Mobility Service ProviderOwns the driver relationship. Publishes tokens, authorizes, sends commands, receives sessions and CDRs, bills the driver.
HUBRoaming hubSits between many CPOs and eMSPs and routes messages so each party needs one connection instead of dozens.
NSPNavigation Service ProviderConsumes location and availability data for maps and route planning.
NAPNational Access PointA government data portal that collects location data.
SCSPSmart Charging Service ProviderSends charging profiles to influence how much power a session draws.
OTHERAnything elseCatch-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 IDOwner (Sender)PurposeCovered in
credentials, versionsBothDiscovery and registrationPart 2
locationsCPOSites, EVSEs, connectors, live statusPart 4
tokenseMSPRFID cards and app users, real-time authorizationPart 5
sessionsCPOLive charging sessionsPart 6
cdrsCPOCharge Detail Records for billingPart 6
tariffsCPOPrices the CPO charges the eMSPPart 7
commandseMSP sends, CPO executesRemote start/stop, reservations, unlockPart 8
chargingprofilesSCSP sends, CPO executesPower limits for active sessionsPart 9
hubclientinfoHubWhich parties are reachable via the hubPart 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 GET endpoint, typically with date_from to 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/POST endpoints 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:

  1. Long before the driver arrives, the CPO and eMSP have registered with each other. The CPO has pushed its Location objects and Tariff objects; the eMSP has pushed its Token objects (or the CPO pulled them).
  2. 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-time POST .../tokens/{uid}/authorize call to the eMSP.
  3. Charging starts. The CPO pushes a Session with status ACTIVE to the eMSP and updates it with PATCH as energy is delivered. The EVSE status changes to CHARGING and is pushed via Locations.
  4. The driver stops. The session becomes COMPLETED, and the CPO posts a CDR with the final energy, time and cost to the eMSP.
  5. 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_id per 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_from for 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.

Let's Talk

Ready to build something that lasts?

Whether it's a charging network, an AI rollout, or a core enterprise system — let's talk about what you're building.