OCPI 2.1.1 to 2.2 Migration: The Step-by-Step Order

OCPI 2.1.1 to 2.2 migration in the order to actually do it: what changed, the Credentials handshake to fix first, and how to upgrade without breaking partners.

OCPI 2.2 was not a normal point release. It changed the connection model, added two new modules, restructured authentication, and tightened the data contracts for several existing modules. If you treat it like a routine upgrade from 2.1.1 you will spend the migration debugging mysterious 401 errors and broken CDR transmissions.

This guide is the migration in the order I would actually do it.

Why OCPI 2.2 happened

OCPI 2.1.1 worked, but two things were holding the protocol back. (If you’re new to the protocol itself, start with what OCPI is before the migration mechanics below.)

First, the architecture quietly assumed hubs. In 2.1.1 it was technically possible to do peer-to-peer roaming, but the Credentials flow made it awkward. Most production deployments used a hub (Hubject’s OICP being one of the bigger ones in Europe, Gireve in France, and OCPI-native hubs such as e-clearing.net; in North America roaming often runs through networks and integrators like EV Connect, ChargeHub, and the interoperability layers that connect ChargePoint, EVgo, and Electrify America). Hubs solved a real problem — N-by-N partner connections quickly become unmanageable — but they also added a layer the protocol couldn’t fully avoid.

Second, the protocol’s data model was getting strained. Tariffs needed more structure to express modern pricing (time-of-use rates, parking fees for cars that linger after charging, distinct tariff variants for the same session). CDRs needed a defined place for cryptographically signed metering data to satisfy regulated billing — Germany’s calibration law is the classic example, with comparable pressure building in other markets on both sides of the Atlantic. And smart-charging coordination needed a real module instead of glue code.

OCPI 2.2 addressed both. The architectural change is the one most people underestimate; the data-model changes are the ones most people actually budget time for.

The headline changes

A short list before we go deep.

  • Roles became explicit. OCPI 2.2 introduced a role field and abstracted roles from the connection itself. Your party declares which roles it operates, and the set of recognized roles expanded well beyond the CPO/eMSP pair of 2.1.1 to include Roaming Hub, NAP, NSP, SCSP, and others.
  • Hub vs peer-to-peer is a first-class choice. The Credentials handshake works the same way structurally for both; what changes is whether the other side of the handshake is the partner directly or a hub representing many partners.
  • HubClientInfo is a new module that lets hubs publish the list of parties they connect on behalf of, so the other side can discover available partners.
  • ChargingProfiles is a new module for smart charging — your eMSP can request that a session charge at a specific power profile.
  • CDR signing is now a defined extension via the SignedData object, which matters for German and other regulated markets.
  • Tariffs got richer. A PARKING_TIME dimension lets you price time plugged in without charging, a TariffType enum distinguishes tariff variants (ad-hoc vs RFID vs charging-preference profiles), and restriction logic was clarified.
  • The Credentials object grew to include business_details, roles, and clearer URL conventions. The token-rotation flow is more explicit.

That’s the surface. Now the details.

The Credentials handshake (start here)

If you fix nothing else first, fix Credentials. Every other module depends on the handshake working, and the failure mode is silent — you get 401s with no real explanation.

In OCPI 2.1.1, your Credentials object looked roughly like this:

{
  "url": "https://your-server/ocpi/2.1.1/",
  "token": "abc123",
  "party_id": "ABC",
  "country_code": "US",
  "business_details": { "name": "Example CPO" }
}

In OCPI 2.2, the same object has more structure and is role-aware:

{
  "token": "abc123",
  "url": "https://your-server/ocpi/2.2/",
  "roles": [
    {
      "role": "CPO",
      "party_id": "ABC",
      "country_code": "US",
      "business_details": { "name": "Example CPO" }
    }
  ]
}

The roles array is what enables a single endpoint to operate as multiple roles (CPO + eMSP, or a Hub representing many parties). For a single-role implementation this looks like overhead, but the second time you need to add a role you’ll be glad it exists.

Token rotation also became explicit. OCPI 2.2 names three registration tokens — CREDENTIALS_TOKEN_A, CREDENTIALS_TOKEN_B, and CREDENTIALS_TOKEN_C. Take the perspective of the party initiating registration (the Sender). The flow is:

  1. The partner (Receiver) generates token A and gives it to you out-of-band — email, ticket, contract attachment. Token A only authorizes the registration itself.
  2. You POST your real credentials, including token B, to the partner’s /credentials endpoint, authenticated with token A.
  3. The partner stores your token B and returns their token C in the response.
  4. From now on, you call the partner with token C; the partner calls you with token B.
  5. Token A is thrown away and may no longer be used.
sequenceDiagram
    participant You
    participant Partner
    Note over You,Partner: Partner generates token A, sends it out-of-band
    You->>Partner: POST /credentials with token B<br/>auth: token A
    Partner-->>You: Response with token C
    Note over You,Partner: token A discarded
    You->>Partner: Calls use token C
    Partner->>You: Calls use token B

OCPI 2.1.1 had the same flow conceptually, but 2.2 names the tokens explicitly and clarifies invalidation rules, which removes a lot of “wait, which token are you using” ambiguity in support tickets.

The Versions endpoint negotiation

GET /ocpi/versions returns the list of versions you support. In 2.2 you simply add 2.2 to the array; your existing 2.1.1 entry stays. Partners pick the highest version both sides support. The mechanics of version discovery and the handshake are covered in depth in our guide to OCPI credentials and versions.

A common mistake during migration: announcing 2.2 before the rest of your implementation is ready. If a partner does the version selection and picks 2.2, they expect every module to behave per the 2.2 spec. Stage the announcement.

Module-by-module changes

Locations

Mostly compatible. The big addition is the EnergyMix object on Location, which lets you describe the energy source (renewable, fossil, mixed, with optional environmental_impact data). Useful for green-certified roaming.

Minor: images is more structured (with width, height, thumbnail). directions clarified. The Connector.terms_and_conditions field is new.

Tariffs

This is where 2.2 changed the most outside of Credentials. The TariffDimensionType enum gained PARKING_TIME — a per-hour charge for being plugged in but not drawing energy — alongside the existing ENERGY, TIME, and FLAT. (Reservation time is priced through the CDR side of the protocol via the RESERVATION_TIME charging-period dimension and the total_reservation_cost field, not through a tariff dimension of the same name; it is a common point of confusion, so check the spec before you model it.)

The new TariffType enum lets you mark a tariff as REGULAR (RFID, no charging preference), AD_HOC_PAYMENT (drive-by payment), PROFILE_CHEAP, PROFILE_FAST, or PROFILE_GREEN — useful for eMSPs choosing between tariff variants for the same session. Tariff restriction logic (day-of-week, time-of-day, energy and power bands) was also clarified.

If your billing logic was hard-coded around 2.1.1’s dimensions, you’ll touch the most code here. For a field-by-field walk through the module itself, see the OCPI Tariffs module deep-dive.

Sessions

Stable. The biggest practical change is that the lifecycle of intermediate updates is clearer — when you must send an interim Session update, what fields are mandatory in each state. Re-read the state-machine table; most teams discover they were sending fewer updates than the spec wants.

CDRs

Two things matter.

The SignedData object is the defined way to attach cryptographically signed metering data to a CDR (or charging period). It matters wherever a market requires tamper-evident, verifiable energy readings for billing — Germany’s calibration law (Eichrecht) is the best-known driver, and similar transparency and consumer-protection expectations are emerging across other European markets and in parts of North America as regulators tighten public-charging billing rules. If you don’t operate in such a market yet, you can defer this — but design your data model so you can add it later.

The CDR totals were extended to match the richer tariff model. total_parking_time breaks out the portion of a session where the EV was plugged in but not charging, and cost totals like total_time_cost, total_parking_cost, and total_reservation_cost let you itemize a bill by dimension. If you charge parking or reservation fees, your CDR has to break them out so the eMSP can reconcile the invoice line by line.

Tokens

Authentication-method enum extended. Token whitelisting clarified. Behavior of AD_HOC_USER and APP_USER distinguished. Otherwise compatible.

Commands

Stable. RemoteStart, RemoteStop, ReserveNow, CancelReservation, UnlockConnector — all work the same way. The async response pattern is unchanged.

Credentials (already covered above)

HubClientInfo (new in 2.2)

If you’re a hub, you publish a list of the parties you connect on behalf of, so other partners can see who’s reachable through you. If you’re a CPO or eMSP working with a hub, you may not implement this at all.

We have a separate deep-dive on HubClientInfo.

ChargingProfiles (new in 2.2)

Smart-charging instruction module — the eMSP can request that an ongoing session charge to a specific power profile (max amps, schedule, etc.). Optional and not yet universally supported. Detailed in our ChargingProfiles guide.

Migration order (CPO perspective)

If you’re operating a CPO and rolling out 2.2, this is the order I would use.

  1. Engineer the new Credentials handshake with token A/B/C and roles. Don’t expose it yet.
  2. Add 2.2 endpoints for the modules that didn’t change much: Locations, Sessions, Tokens, Commands. These are safe wins.
  3. Migrate Tariffs to the new dimensions and types. Test against your own billing engine to confirm nothing changes for existing tariffs (parity test).
  4. Migrate CDRs to include the new optional fields where applicable.
  5. Announce 2.2 on your Versions endpoint to one trusted partner first. Don’t announce broadly until you’ve completed at least one real partner integration.
  6. Roll out to additional partners one by one. Each one will surface one new bug. Expect this; don’t promise a “big bang” cut-over.
  7. Plan deprecation of 2.1.1, but don’t rush. Six to twelve months of dual-support is normal in production.

Migration order (eMSP perspective)

For an eMSP the flow is slightly different.

  1. Engineer the new Credentials handshake (same).
  2. Add the ChargingProfiles client side if you intend to use smart charging. Otherwise skip.
  3. Update your tariff consumer to handle the new dimensions and types — even if you don’t currently offer parking/reservation tariffs, partner CPOs may.
  4. Update your CDR ingestion to handle new optional fields and SignedData if you operate in a regulated market.
  5. Negotiate 2.2 upgrade with each CPO partner.

Common pitfalls

A list of things that have broken real migrations.

  • Token type confusion during the handshake. If your 2.1.1 code calls partners with what it thinks is “the token” but 2.2 expects token C specifically, you get 401s. Be explicit in your code about which token type you’re using.
  • Hard-coded tariff dimensions. If your billing pipeline only knows about ENERGY, TIME, and FLAT, the new dimensions just disappear silently into your DB. Catch them at ingestion or you’ll have under-billed sessions.
  • Versions endpoint advertising 2.2 too early. As noted above — stage the announcement.
  • Different country_code / party_id formats across partners. OCPI is strict (ISO 3166 alpha-2 country, three-character party ID, uppercase). Some hubs were historically lax; be strict yourself.
  • Forgetting Locations.publish. The publish boolean on Location was clarified — if a Location isn’t published it shouldn’t be sent. Some implementations were sending unpublished locations and partners were silently consuming them.

Testing

You need partner-style fixtures, not just your own. Tools and approaches:

  • Mock partner: stand up a fake CPO and fake eMSP that you control, exchange Credentials, and round-trip every module.
  • Schema validation: use the OCPI JSON Schema (community-maintained for 2.2) in your CI pipeline so every outgoing payload is validated before send.
  • Pre-prod with one real partner: there is no substitute for one cooperative partner who is also willing to be on the 2.2 beta with you.
  • Production smoke tests: after each partner goes live on 2.2, do one real session and one real CDR end-to-end before declaring it done.

When NOT to migrate

If your business depends on one partner and that partner is on 2.1.1 indefinitely, you don’t need to move. OCPI 2.1.1 still works.

You should move when:

  • A meaningful number of your partners have moved or are moving.
  • You need a 2.2-only feature (ChargingProfiles for smart charging, signed CDRs for compliance, hub-via-HubClientInfo for discovery).
  • You’re hiring engineers; new hires expect 2.2 and find 2.1.1 quirky.

A pragmatic baseline: by the time 2.3 is widely deployed (currently rolling out among earliest adopters), you should already be on 2.2. Don’t skip versions.

What about 2.2.1 and 2.3?

OCPI 2.2.1 is a patch on 2.2 — a clarification release. If you’ve correctly implemented 2.2 you already do most of what 2.2.1 specifies. We have a separate article on 2.2.1’s specific changes (under “2.2.1”).

OCPI 2.3 is the larger evolution: it continues to refine the data contracts and extend the module set, with a direction toward richer support for scenarios that touch ISO 15118 and how the roaming, charger, and vehicle protocols fit together. Rather than trust any single blog’s field-level summary here, confirm the specifics against the official OCPI changelog before you plan against 2.3. Either way, most teams won’t move to 2.3 immediately — get 2.2 right first, and don’t skip versions.

The honest summary

OCPI 2.2 is worth doing. It cleaned up the protocol meaningfully, the new modules unlocked real use cases, and the architectural change (P2P first-class) gave operators choice. The migration is not trivial but it’s well-bounded — six to twelve weeks of engineering plus a few months of partner-by-partner rollout.

The biggest risk is treating it as a routine upgrade. Start with Credentials, validate everything against the spec rather than your existing 2.1.1 implementation, and stage your Versions announcement. Do those three things right and the rest is just work.

Quick check

Q1. Why is the Credentials handshake the recommended starting point for a 2.2 migration?
Q2. How do two OCPI parties agree on which version to speak?
Q3. Which TariffDimensionType did OCPI 2.2 add for time spent plugged in but not charging?
Q4. What is the safest way to announce 2.2 support to partners?
Q5. How is OCPI 2.2.1 best described relative to 2.2?

Frequently asked questions

Can I support OCPI 2.1.1 and 2.2 at the same time?

Yes. OCPI is designed for this. The Versions endpoint lets partners discover which versions you speak, and you negotiate the highest common version per partner. Most production CPOs and eMSPs run both during a migration window of six to twelve months.

Do I have to use a hub in OCPI 2.2?

No. Peer-to-peer was always allowed, but OCPI 2.2 made it a first-class architecture choice. You can connect directly to partners, go through a hub, or mix both. The Credentials module handles either pattern.

What is the single biggest behavior change to watch for?

The Credentials handshake. The token rotation flow and the structure of the credentials object both changed, and the new role-based design means you need to declare your roles (CPO, eMSP, Hub) explicitly. A successful handshake is the precondition for everything else.

How long does a real OCPI 2.2 migration take?

For a CPO with five to fifteen roaming partners, plan four to eight weeks of focused engineering plus another four weeks of partner-by-partner rollout. Hubs absorb a lot of that work for you. Solo P2P integrations are slower because each partner has its own quirks.

Found this useful? Share it.