OCPI MODULE · UPDATED 11 AUG 2026

Sessions: the live view of a charge

The Session object is what a driver's app watches while their car charges — pushed from CPO to eMSP as energy accumulates. It is the only real-time window one company gets into another company's hardware, and it is explicitly not the billing record.

PART OF THE OCPI 2.2.1 COMPLETE GUIDE · 15 MODULE & COMMAND GUIDES

IN ONE PARAGRAPH

A Session is the mutable, live view of a charge in progress: status, energy so far, and a running cost estimate. It updates throughout and then stops. The immutable financial record that follows is the CDR, and the single most expensive mistake in this module is treating a Session total as the amount to charge a customer.

Lifecycle and status

A Session is created when charging is authorised and updated as things change. Its status drives what the driver sees.

StatusMeaningWhat the app should show
ACTIVESession running — energy may or may not be flowingLive progress
COMPLETEDFinished normally; a CDR should followSummary, cost pending
INVALIDThe CPO has voided this sessionDo not bill; investigate
PENDINGCreated but not yet startedStarting…
RESERVATIONA reservation, not yet a chargeReserved
ACTIVE does not mean current is flowing. A session stays ACTIVE while a fully charged car sits plugged in drawing nothing. If your UI equates ACTIVE with “charging”, drivers will see a progress animation that never advances. Compare kwh across successive updates to know whether energy is actually moving.

Worked example: a live session update

PATCH /ocpi/2.2.1/sessions/IN/EMS/sess-77c1
Authorization: Token <base64 token>
OCPI-from-country-code: IN
OCPI-from-party-id: EFI
OCPI-to-country-code: IN
OCPI-to-party-id: EMS

{
  "kwh": 18.400,
  "total_cost": { "excl_vat": 340.40, "incl_vat": 401.67 },
  "last_updated": "2026-08-11T09:47:10Z"
}

And the full object it is patching:

{
  "country_code": "IN", "party_id": "EFI",
  "id": "sess-77c1",
  "start_date_time": "2026-08-11T09:12:00Z",
  "kwh": 18.400,
  "cdr_token": {
    "country_code": "IN", "party_id": "EMS",
    "uid": "DR-88231", "type": "APP_USER",
    "contract_id": "IN-EMS-C0142"
  },
  "auth_method": "COMMAND",
  "authorization_reference": "auth-4471",
  "location_id": "LOC-ND-014",
  "evse_uid": "EVSE-014-02",
  "connector_id": "1",
  "currency": "INR",
  "total_cost": { "excl_vat": 340.40, "incl_vat": 401.67 },
  "status": "ACTIVE",
  "last_updated": "2026-08-11T09:47:10Z"
}

auth_method tells you how the session was authorised — COMMAND for a remote start you initiated, WHITELIST for an offline-authorised RFID tap, AUTH_REQUEST for a real-time authorisation call. It is how you distinguish a session your app started from one the same driver started by tapping a card at the charger.

The running cost is an estimate

total_cost on a Session is the CPO's best current calculation. It is genuinely useful — drivers want to see the meter running — and it is genuinely not the invoice. It can and does change:

  • Time-based components accrue after the update was sent.
  • A tariff with time-of-use pricing crosses a boundary mid-session.
  • Parking or idle fees begin once charging completes.
  • The CPO corrects meter values during CDR finalisation.

Label it. “Estimated” next to a live figure costs nothing and prevents the support ticket that begins “the app said 340 and you charged me 401”.

Push, pull, and staying honest

The CPO is the Sender. Updates arrive by PUT for a full object or PATCH for changed fields, and you can GET with pagination and date_from to reconcile.

Update frequency is not specified by OCPI and varies enormously — some CPOs push every 30 seconds, some every few minutes, some only on state change. Do not build UI that assumes a cadence. Show the last-updated time and let the driver see the data is a minute old rather than pretending it is live.

Two failure modes to design around:

  • Sessions that never complete. A charger that goes offline mid-session leaves an ACTIVE session that never transitions. Age them out on your side and reconcile against CDRs, or your “currently charging” count drifts upward forever.
  • Sessions you never saw start. A CDR can arrive for a session that was never pushed, typically because the charger was offline throughout. Your CDR ingestion must not assume a matching session exists.

Reconciling sessions against CDRs

Every completed session should produce exactly one CDR. Tracking that relationship is one of the highest-value pieces of instrumentation you can build:

  • Sessions completed without a CDR — after a reasonable window, this is unbilled revenue. Chase it.
  • CDRs with no prior session — offline charging. Expected in small volumes, a problem in large ones.
  • Energy mismatch between session and CDR — a small delta is normal from final meter reconciliation; a consistent one indicates a metering or tariff issue worth raising with the partner.

The ratio of these three, per partner, is a more honest measure of integration quality than any uptime number.

Production lessons

  • Never bill from a Session. The CDR is the financial record. This is the one rule in the module that costs real money to break.
  • Always label live cost as estimated.
  • Do not equate ACTIVE with charging. Compare kwh between updates.
  • Enforce last_updated ordering. Same rule as Locations — reject stale updates.
  • Age out stalled sessions rather than letting them accumulate.
  • Store authorization_reference. It is the join key from your original command through to the CDR.

Frequently asked questions

What is the difference between an OCPI Session and a CDR?

A Session is the mutable, live view of a charge in progress, updated as energy accumulates, with a running cost estimate. A CDR is the single immutable record issued after completion, carrying the binding tariff snapshot and the totals used for billing and settlement. Never bill from a Session.

Does ACTIVE status mean the car is charging?

No. A session remains ACTIVE while a fully charged vehicle sits plugged in drawing no power. To know whether energy is actually flowing, compare the kwh value across successive session updates.

Is the total_cost on an OCPI Session final?

No, it is an estimate. It can change as time-based components accrue, as time-of-use tariffs cross a boundary, as idle fees begin, or when the CPO corrects meter values during CDR finalisation. Always label it as estimated in the UI.

How often do OCPI sessions update?

OCPI does not specify a cadence and it varies widely between CPOs — some push every 30 seconds, others only on state change. Do not build UI that assumes a frequency; display the last-updated time instead.

What causes a CDR to arrive with no matching session?

Offline charging. If the charger lost connectivity for the whole session, the CPO may never have pushed a Session object but will still issue a CDR once the charger reconnects. CDR ingestion must not assume a session exists.