OCPI MODULE · UPDATED 11 AUG 2026

Tariffs: pricing as data

Every price a driver sees before plugging in — and every rupee on the CDR after — traces back to a Tariff object. OCPI models pricing as composable data rather than a number, which makes it powerful and makes it the module most likely to produce a bill nobody can explain.

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

IN ONE PARAGRAPH

A Tariff is a list of elements; each element is a set of price components plus optional restrictions that decide when it applies. Cost is computed by walking the session and applying whichever elements match at each moment. The two fields that cause the most billing disputes are step_size and the ordering of restricted elements.

The structure

Three nested concepts, and the nesting is the whole model.

ConceptWhat it is
TariffThe container: an ID, a currency, and a list of elements
TariffElementA set of price components plus optional restrictions governing when they apply
PriceComponentA type, a price, and a step_size

The four component types

TypeCharged perTypical use
ENERGYkWh consumedThe main charge on DC fast charging
TIMEHour of chargingCommon on AC, where energy rate is slow
PARKING_TIMEHour connected but not chargingIdle fees to free up bays
FLATSessionA fixed connection or session fee

A single tariff can carry several types at once — a flat connection fee, an energy rate, and an idle fee that begins after charging completes. That is normal, and it is why a driver's final bill rarely equals kWh multiplied by a headline rate.

Restrictions decide which element applies

Restrictions are what turn a static price list into time-of-use, power-tiered, or customer-specific pricing.

RestrictionApplies the element when
start_time / end_timeWithin a time of day — the basis of peak/off-peak
start_date / end_dateWithin a date range — promotions, seasonal rates
min_kwh / max_kwhConsumption falls in a band
min_power / max_powerDelivered power falls in a band — tiered DC pricing
min_duration / max_durationSession length falls in a band — how idle fees start after N minutes
day_of_weekOn listed days — weekday vs weekend
reservationThe session came from a reservation
Element order is significant. Elements are evaluated in the order given and the first matching element wins for a given dimension. A tariff whose unrestricted catch-all element is listed first will mask every restricted element after it, producing a flat rate where peak pricing was intended. When a partner's prices look wrong, check ordering before checking arithmetic.

Example: peak/off-peak energy tariff

{
  "country_code": "IN", "party_id": "EFI",
  "id": "TRF-DC-STD",
  "currency": "INR",
  "type": "REGULAR",
  "elements": [
    {
      "price_components": [
        { "type": "ENERGY", "price": 22.00, "step_size": 1 }
      ],
      "restrictions": { "start_time": "09:00", "end_time": "22:00" }
    },
    {
      "price_components": [
        { "type": "ENERGY", "price": 14.50, "step_size": 1 }
      ],
      "restrictions": { "start_time": "22:00", "end_time": "09:00" }
    },
    {
      "price_components": [
        { "type": "PARKING_TIME", "price": 120.00, "step_size": 300 }
      ],
      "restrictions": { "min_duration": 2700 }
    }
  ],
  "last_updated": "2026-08-01T00:00:00Z"
}

Reading it: 22 INR/kWh during the day, 14.50 overnight, and after 45 minutes connected an idle fee of 120 INR per hour billed in 5-minute blocks. A session spanning 22:00 crosses the boundary and is billed at both rates, which is exactly why the CDR splits into charging periods.

step_size is where the money leaks

step_size is the rounding unit, and it is the least understood field in the module. It defines the increment in which a dimension is billed.

Component typestep_size unitExample
ENERGYWatt-hours1 = per Wh; 1000 = rounded up to whole kWh
TIMESeconds60 = per minute; 900 = per 15-minute block
PARKING_TIMESeconds300 = per 5-minute block
FLATNot applicableCharged once

Rounding is upward. A step_size of 1000 on energy means a 24.55 kWh session bills as 25 kWh. On a per-session basis that is trivial; across a network it is a material revenue difference, and across a roaming relationship it is a reconciliation gap if the two sides implement rounding differently.

When your computed total disagrees with a partner's CDR by a small amount, step_size is the first thing to check.

How tariffs reach the driver

Tariffs connect to infrastructure through tariff_ids on the Connector object in the Locations module. That is the only link, and it has consequences:

  • Pricing is per connector, not per site. A location's AC and DC connectors will normally carry different tariffs.
  • A connector can list several tariff IDs, in which case restrictions decide which applies. This is how a CPO offers a member rate and a walk-up rate at the same socket.
  • A missing tariff_ids means you cannot show a price. Displaying “price unavailable” is correct; guessing is not.

OCPI 2.2.1 also supports type values including AD_HOC_PAYMENT, PROFILE_CHEAP, PROFILE_FAST and PROFILE_GREEN, which let a CPO publish alternative tariffs for smart-charging preferences.

Lessons from partner integrations

  • Never cache a tariff without honouring last_updated. Price changes propagate through this field, and a stale cached tariff means quoting a price you cannot honour.
  • Recompute and compare against every CDR. Independently calculating what you expected and diffing against the partner's figure catches tariff misinterpretation before customers do.
  • Check element ordering during certification. A catch-all placed first is a common partner-side error and it silently flattens their own pricing.
  • Show the driver the components, not just a rate. “22 INR/kWh, plus idle fee after 45 min” prevents the complaint that a headline rate cannot.
  • Handle currency explicitly. Tariff currency is authoritative; never assume the driver's home currency.
  • Expect a session to span multiple tariff elements. Any UI showing one price per session will misrepresent time-of-use tariffs.

Frequently asked questions

How does OCPI model EV charging prices?

As composable data. A Tariff contains elements; each element holds price components (ENERGY, TIME, PARKING_TIME or FLAT) plus optional restrictions that decide when it applies. Cost is computed by walking the session and applying whichever elements match at each point.

What is step_size in an OCPI tariff?

The rounding increment for a price component, always rounded upward. For ENERGY it is in watt-hours, so 1000 means billing rounds up to whole kWh. For TIME and PARKING_TIME it is in seconds, so 900 means 15-minute blocks. Mismatched step_size handling is a common cause of small reconciliation gaps.

Why does tariff element order matter in OCPI?

Elements are evaluated in order and the first match wins for a given dimension. An unrestricted catch-all element placed first will mask every restricted element after it, flattening time-of-use pricing into a single rate.

How does a tariff connect to a charger?

Through tariff_ids on the Connector object in the Locations module. Pricing is therefore per connector rather than per site, and a connector can list several tariff IDs with restrictions deciding which applies.

Can one charging session be billed at more than one rate?

Yes. A session that crosses a peak/off-peak boundary is billed at both rates. This is exactly why the CDR splits a session into charging periods, each carrying the dimensions and tariff that applied.