Tariffs & pricing

Public API — Tariffs & Pricing

How charging is priced: defining tariffs, attaching them to infrastructure (directly or via tariff rules), and where the resulting prices surface (sessions, CDRs). Field-level schemas live in the Swagger UI (/api-docs).

Permissions: tariff endpoints need tariff:read|create|update|delete; rule endpoints need tariff-rule:read|create|update|delete (see Authentication).

The pricing model

A tariff defines what to charge. Which tariff applies to a session is resolved at charging time:

flowchart LR
    S[Session starts<br/>source: who charges<br/>target: where] --> R{Tariff rules<br/>highest priority first}
    R -- first rule whose criteria<br/>ALL match --> T1[Rule's tariff]
    R -- no rule matches --> T2[Connector's default tariff]
    T1 --> P[Session price → CDR]
    T2 --> P
  • Connector default — every connector carries a tariffId (set at onboarding or via PUT /api/v1/connectors/{id}). Used when no rule matches.
  • Tariff rules — condition-based overrides: "OCPI roaming sessions on DC connectors get tariff X". Only rules currently inside their validFrom/validTo window participate; they are evaluated by descending priority, and the first rule whose criteria all match wins. A rule with no criteria matches everything.

The resolved tariff is recorded on the session (price, tariffId on SessionDto). The final billed amount lands in the CDR — the billing source of truth (see Session Flows).

Tariff endpoints

OperationEndpointPermission
List (optional ?tag= filter)GET /api/v1/tariffstariff:read
Get oneGET /api/v1/tariffs/{tariffId}tariff:read
CreatePOST /api/v1/tariffs201 + TariffDtotariff:create
ReplacePUT /api/v1/tariffs/{tariffId}tariff:update
DeleteDELETE /api/v1/tariffs/{tariffId}204tariff:delete

PUT is a full replace — send the complete tariff, not a patch.

Tariff anatomy

A tariff is a list of elements; each element holds price components and optional restrictions that say when the element applies:

{
  "name": "Standard AC",
  "currency": "EUR",
  "tags": ["public", "ac"],
  "elements": [
    {
      "priceComponents": [
        { "type": "FLAT",   "price": 0.50, "vat": 19 },
        { "type": "ENERGY", "price": 0.49, "vat": 19, "stepSize": 100 }
      ]
    },
    {
      "priceComponents": [
        { "type": "PARKING_TIME", "price": 6.00, "vat": 19, "stepSize": 300 }
      ],
      "restrictions": { "minDuration": 10800 }
    }
  ]
}

This bills €0.50 per session plus €0.49/kWh (rounded up to 0.1 kWh), and — once the session exceeds 3 hours — €6/h idle fee in 5-minute steps.

Price components

typeprice is perstepSize unitNotes
ENERGYkWhWh
TIMEhoursecondscharging time
PARKING_TIMEhoursecondstime plugged in but not charging
FLATsession— (ignored)billed once
  • stepSize rounds the billed volume up to the next multiple (e.g. ENERGY with stepSize: 1000 bills whole kWh).
  • vat is a percentage; when omitted, the environment's default VAT applies.

Restrictions

All optional; an element applies only while every restriction holds:

FieldUnit / format
startTime / endTimetime of day, "HH:MM"
startDate / endDatedate
dayOfWeekarray of weekday names
minKwh / maxKwhkWh consumed so far
minDuration / maxDurationseconds
minPower / maxPower, minCurrent / maxCurrentkW / A

Parking-time flags

Top-level tariff booleans controlling the PARKING_TIME dimension:

FlagEffect
calculateParkingTimeBeforeTransactioncount time between plug-in and transaction start
calculateParkingTimeAfterTransactioncount time between transaction end and unplug
applyParkingTimeMinDurationAsGracePeriodtreat the parking element's minDuration as a free grace period

Tariff rules

Endpoints

OperationEndpointPermission
List (paginated, filters/sort query)GET /api/v1/tariffs-rulestariff-rule:read
Next free priorityGET /api/v1/tariffs-rules/priority{ "next": n }tariff-rule:read
Get oneGET /api/v1/tariffs-rules/{id}tariff-rule:read
Matching sources (roaming partners)GET /api/v1/tariffs-rules/{id}/sourcestariff-rule:read
Matching targets (connectors)GET /api/v1/tariffs-rules/{id}/targetstariff-rule:read
CreatePOST /api/v1/tariffs-rules201 + { id }tariff-rule:create
UpdatePATCH /api/v1/tariffs-rules/{id}{ id }tariff-rule:update
DeleteDELETE /api/v1/tariffs-rules/{id}204tariff-rule:delete
Source criteria catalogGET /api/v1/tariffs-rules-dictionaries/sourcestariff-rule:read
Target criteria catalogGET /api/v1/tariffs-rules-dictionaries/targetstariff-rule:read

Creating a rule

curl -s -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
        "name": "OCPI roaming on DC",
        "tariffId": "<tariff-uuid>",
        "priority": 100,
        "validFrom": "2026-08-01T00:00:00Z",
        "criteria": [
          { "key": "source.roamingProtocol",      "operator": "in", "value": ["OCPI"] },
          { "key": "target.connector.powerType",  "operator": "in", "value": ["DC"] }
        ]
      }' \
  "https://<host>/api/v1/tariffs-rules"
  • priority ≥ 0; higher wins. GET /tariffs-rules/priority returns the next free slot.
  • validFrom/validTo are optional; they drive the rule's status in list responses: PLANNEDACTIVEEXPIRED. Only ACTIVE rules are evaluated.
  • Each criterion is { key, operator, value }. For the in operator, value is an array; string comparisons are case-insensitive.
  • Verify a rule's actual scope before relying on it: GET /tariffs-rules/{id}/sources and /{id}/targets list the roaming partners and connectors it currently matches.

Criteria catalog

Source — who is charging:

KeyOperatorsValues
source.roamingProtocolinOCPI, OICP
source.tokenTypeintoken types
source.tokenUidintoken UIDs
source.terminalProvidereqpayment terminal providers
source.ocpiPartnerIdinOCPI partner IDs
source.roamingPartnerIdinroaming partner IDs
source.roamingPartnerGroupsinroaming partner group IDs
source.roamingPartnerConnectionTypeinconnection types

Target — where the charging happens:

KeyOperatorsValues
target.connector.idinconnector UUIDs
target.connector.powerTypeinAC_1_PHASE, AC_2_PHASE, AC_2_PHASE_SPLIT, AC_3_PHASE, DC
target.connector.powereq, lt, lte, gt, gtenumber (kW)
target.connector.groupsinconnector group IDs
target.chargingPoint.ideq, incharging point UUIDs
target.chargingPoint.ocppEvseIdinEVSE IDs
target.chargingPoint.groupsincharging point group IDs
target.chargingStation.ideq, incharging station UUIDs
target.chargingStation.groupsincharging station group IDs
target.chargingPool.idincharging pool UUIDs
target.chargingPool.groupsincharging pool group IDs

The dictionaries endpoints return this catalog machine-readable, including the currently valid values (entity IDs with human-readable labels) — use them to build rule editors or validate rules before creation.

Where prices surface

  1. Live sessionSessionDto.price (with tariffId) updates as the session progresses.
  2. CDR — created after completion; the final billed cost. Pull via /api/v1/cdrs, or push via a CDR webhook.

Wrong tariff on a session? Check, in order: (1) another rule with higher priority matched; (2) the rule's validity window (status must be ACTIVE); (3) the rule's real scope via /{id}/sources + /{id}/targets; (4) the connector's default tariffId (used when no rule matched).

Related



Did this page help you?