Roam OCPI CDRs Module

Push-only Roam OCPI CDR delivery with retry behavior, relaxed field validations, and cost breakdown enrichment.

CDRs module adjustments

DeviationRoam OCPI solution
CDR field validationsRelaxed validations, as specified below, to accept locations that are not fully OCPI compliant
PULL not supportedOnly PUSH, Plugsurfing will push the CDRs to the EMP. If the EMP partner doesn't respond with 200 (and an OCPI success code: 1000), or doesn't respond in time, the request will be retried: the CDR will be resent every night for 7 days.
CDR without corresponding sessionThere may be CDRs for which we have not shared the session object on beforehand.
CDR without auth referenceThere may be CDRs where a real-time authorization has been performed, where the authorization reference is missing in the CDR.
CDRs on unknown locationsCDRs may arrive for locations that have not been shared in advance.
Credit CDRs not supportedCredit CDRs are not supported.
CDR ID = session IDThe CDR ID is always the same as the session ID (when there is a session ID).

CDR field validations

Plugsurfing is a bit more lenient than the OCPI specification for a set of properties when receiving CDRs from CPOs. This means outgoing CDRs from Plugsurfing will also not fully comply with the OCPI specification for these fields.

FieldOCPI specificationPlugsurfing implementation
cdr_location.addressstring(45)string(200)
cdr_location.postal_codestring(10) (optional)string(50) _(optional)

CDR push retry logic

If the EMP partner doesn't respond with 200 (and an OCPI success code: 1000), or doesn't respond in time, the request will be retried: the CDR will be resent every night for 7 days.

CDR without corresponding session

As per OCPI 2.2.1, if Session module is supported, all CDRs should have a session ID. All CDRs sent to you have a session ID, however not all CPOs behind our solution support the session module and therefore there will be cases where there are no session events received from the CPO before the CDR. Hence CDRs might have a session ID for which you have not received a session object.

There might also be cases where the session is very short and the CDR is created prior to the session event being delivered.

CDR without authorization reference

Though the authorization reference provided by the EMP should be added to the CDR according to OCPI 2.2.1, the concept of authorization reference does not exist on OCPI 2.1.1. For 2.1.1 sessions or non-OCPI sessions, Plugsurfing will hence not be able to link all sessions to the corresponding authorization. Therefore, there may be CDRs without an authorization reference even when the tokens are shared with whitelist = never.

CDRs for unknown locations

Due to the nature of OCPI, CDRs may be sent to you referring to locations that have not been shared with you in advance. CDRs for an unknown or previously undisclosed location must not be rejected or denied solely on the basis that the location or tariff was not shared upfront.

To prevent CDRs from being generated for empty or unknown locations, you can create the following implementation:

  • Share all tokens with whitelist = never.
  • Configure real-time authorization towards Plugsurfing with fallback = deny
  • Reject authorization requests where the location is empty or unknown.

CDR cost breakdown

Providing the cost breakdown per tariff element is optional in OCPI, however even when the CPO doesn't share it in their CDR we ensure to populate this to the CDR we share with you. Hence, you will always have a cost breakdown in the CDR we share with you.

CDR example

CDR with just one cost segment.

{
  "country_code": "GB",
  "party_id": "EVC",
  "id": "cAmK3kVovbA",
  "start_date_time": "2025-09-22T18:57:12Z",
  "end_date_time": "2025-09-23T00:05:22Z",
  "session_id": "cAmK3kVovbA",
  "cdr_token": {
    "country_code": "DE",
    "party_id": "EMP",
    "uid": "32435435",
    "type": "RFID",
    "contract_id": "DE-EMP-CR2012345-9"
  },
  "auth_method": "AUTH_REQUEST",
  "cdr_location": {
    "id": "el0021a5bf9603f7499e838b5d626712345",
    "name": "Royal Garden",
    "address": "Royal Garden Street 1",
    "city": "London",
    "postal_code": "12345",
    "country": "GBR",
    "coordinates": {
      "latitude": "50.500708",
      "longitude": "1.02942"
    },
    "evse_uid": "ee000a5810f8f1474b49948a8353779ac123",
    "connector_id": "ec00d160596701464df68b78c0fc17297123",
    "connector_standard": "IEC_62196_T2",
    "connector_format": "SOCKET",
    "connector_power_type": "AC_3_PHASE"
  },
  "currency": "GBP",
  "charging_periods": [
    {
      "start_date_time": "2025-09-22T18:57:12Z",
      "dimensions": [
        {
          "type": "ENERGY",
          "volume": 17.268
        }
      ]
    }
  ],
  "total_cost": {
    "excl_vat": 5.61,
    "incl_vat": 6.73
  },
  "total_fixed_cost": {
    "excl_vat": 5.61,
    "incl_vat": 6.73
  },
  "total_energy": 17.268,
  "total_time": 5.136,
  "total_parking_time": 0,
  "credit": false,
  "home_charging_compensation": false,
  "last_updated": "2025-09-23T06:58:22Z"
}

CDR with multiple cost segments.

{
  "country_code": "SE",
  "party_id": "OUT",
  "id": "2dGoDR51PeV",
  "start_date_time": "2025-10-23T09:37:45Z",
  "end_date_time": "2025-10-23T09:48:41Z",
  "session_id": "2dGoDR51PeV",
  "cdr_token": {
    "country_code": "NL",
    "party_id": "OUT",
    "uid": "14171555417659",
    "type": "RFID",
    "contract_id": "NL-OUT-C37C4880W-M"
  },
  "auth_method": "WHITELIST",
  "cdr_location": {
    "id": "el009b18801b0b83479589094b9b9a625df9",
    "name": "North Mountain - generated 2",
    "address": "Address locgen-2-SE-OUT",
    "city": "Stockholm",
    "postal_code": "117 29",
    "country": "SWE",
    "coordinates": {
      "latitude": "59.18921",
      "longitude": "17.821754"
    },
    "evse_uid": "ee00cf340ac6f54742c0b8a7e509ea3516f0",
    "evse_id": "SE*OUT*E2MbmdP",
    "connector_id": "ec0027e1a54ca9384f35a879265292968e99",
    "connector_standard": "CHADEMO",
    "connector_format": "CABLE",
    "connector_power_type": "DC"
  },
  "currency": "SEK",
  "tariffs": [
    {
      "country_code": "SE",
      "party_id": "OUT",
      "id": "2096bcf2e5e88d5b",
      "currency": "SEK",
      "elements": [
        {
          "price_components": [
            {
              "type": "ENERGY",
              "price": 0.25,
              "step_size": 100
            },
            {
              "type": "PARKING_TIME",
              "price": 2,
              "step_size": 900
            },
            {
              "type": "FLAT",
              "price": 0.5,
              "step_size": 1
            }
          ]
        }
      ],
      "last_updated": "2025-10-23T09:37:45Z"
    }
  ],
  "charging_periods": [
    {
      "start_date_time": "2025-10-23T09:37:45Z",
      "dimensions": [
        {
          "type": "ENERGY",
          "volume": 0.3
        }
      ]
    },
    {
      "start_date_time": "2025-10-23T09:39:41Z",
      "dimensions": [
        {
          "type": "PARKING_TIME",
          "volume": 0.15
        }
      ]
    }
  ],
  "total_cost": {
    "excl_vat": 1.08,
    "incl_vat": 1.36
  },
  "total_fixed_cost": {
    "excl_vat": 0.5,
    "incl_vat": 0.63
  },
  "total_energy": 0.25,
  "total_energy_cost": {
    "excl_vat": 0.08,
    "incl_vat": 0.1
  },
  "total_time": 0.182,
  "total_parking_time": 0.15,
  "total_parking_cost": {
    "excl_vat": 0.5,
    "incl_vat": 0.63
  },
  "credit": false,
  "home_charging_compensation": false,
  "last_updated": "2025-10-23T09:48:49Z"
}