Module Identifier: cdrs
Data owner: CPO
Type: Functional Module
A Charge Detail Record is the description of a concluded charging session. The CDR is the only billing-relevant object. CDRs are sent from the CPO to the eMSP after the charging session has ended. Although there is no requirement to send CDRs in (semi-) realtime, it is seen as good practice to send them as soon as possible. But if there is an agreement between parties to send them, for example, once a month, that is also allowed by OCPI.
CDRs are created by the CPO. They most likely will be sent only to the eMSP that needs to pay the bill of the underlying charging session. Because a CDR is for billing purposes, it cannot be changed or replaced once sent to the eMSP. Changes are simply not allowed. Instead, a Credit CDR can be sent.
CDRs may be sent for charging locations that have not been published via the Location module. This is typically for home chargers.
As CDRs are used for billing and can be seen as a kind of invoice, they cannot be deleted. Instead, they have to be credited.
When a CPO wants to make changes to a CDR that was already sent to the eMSP, the CPO has to send a Credit CDR for the first CDR.
This credit CDR SHALL have a different CDR.id which can be a completely different number,
or it can be the id of the original CDR with something appended like for example: -C
to make it unique again.
To indicate that a CDR is a Credit CDR, the credit
field has to be set to true
.
The Credit CDR references the old CDR via the credit_reference_id
field,
which SHALL contain the id
of the original CDR.
The Credit CDR will contain all the data of the original CDR.
Only the values in the total_cost
field SHALL contain the negative amounts of the original CDR.
After having sent the Credit CDR, the CPO can send a new CDR with a new unique ID
and the fields: credit
and credit_reference_id
omitted.
Note
|
How far back in time a CPO can send a Credit CDR is not defined by OCPI. It is up the business contracts between the different parties involved, as there might be local laws involved etc. |
When the CPO creates CDR(s) they push them to the relevant eMSP by calling POST on the eMSPs CDRs endpoint with the newly created CDR(s). A CPO is not required to send all CDRs to all eMSPs, it is allowed to only send CDRs to the eMSP that a CDR is relevant to.
CDRs should contain enough information (dimensions) to allow the eMSP to validate the total cost. It is advised to send enough information to the eMSP so that they can calculate their own costs for billing their customers. An eMSP might have a very different contract/pricing model with their EV drivers than the tariff structure of the CPO.
eMSPs who do not support the Push model need to call GET on the CPO’s CDRs endpoint to receive a list of CDRs.
This GET can also be used in combination with the Push model to retrieve CDRs after the system (re-)connects to a CPO, to get a list of CDRs missed during a downtime of the eMSP’s system.
A CPO is not required to return all known CDRs, the CPO is allowed to return only the CDRs that are relevant for the requesting eMSP.
There are both, a Sender and a Receiver interface for CDRs. Depending on business requirements, parties can decide to use the Sender Interface (Pull model), or the Receiver Interface (Push model), or both. Push is the preferred model to use, because the Receiver will receive CDRs in semi-realtime when they are created by the CPO.
Typically implemented by market roles like: CPO.
The CDRs endpoint can be used to retrieve CDRs.
Endpoint structure definition:
{cdr_endpoint_url}?[date_from={date_from}]&[date_to={date_to}]&[offset={offset}]&[limit={limit}]
Examples:
https://www.server.com/ocpi/cpo/2.2.1/cdrs/?date_from=2019-01-28T12:00:00&date_to=2019-01-29T12:00:00
https://ocpi.server.com/2.2.1/cdrs/?offset=50
https://www.server.com/ocpi/2.2.1/cdrs/?date_from=2019-01-29T12:00:00&limit=100
https://www.server.com/ocpi/cpo/2.2.1/cdrs/?offset=50&limit=100
Method | Description |
---|---|
Fetch CDRs last updated (which in the current version of OCPI can only be the creation Date/Time) between the |
|
POST |
n/a |
PUT |
n/a |
PATCH |
n/a |
DELETE |
n/a |
Fetch CDRs from the CPO’s system.
If additional parameters: {date_from}
and/or {date_to}
are provided, only CDRs with last_updated
between the given {date_from}
(including) and {date_to}
(excluding) will be returned.
This request is paginated, it supports the pagination related URL parameters.
Parameter | Datatype | Required | Description |
---|---|---|---|
date_from |
no |
Only return CDRs that have |
|
date_to |
no |
Only return CDRs that have |
|
offset |
int |
no |
The offset of the first object returned. Default is 0. |
limit |
int |
no |
Maximum number of objects to GET. |
The endpoint returns a list of CDRs matching the given parameters in the GET request, the header will contain the pagination related headers.
Any older information that is not specified in the response is considered no longer valid. Each object must contain all required fields. Fields that are not specified may be considered as null values.
Datatype | Card. | Description |
---|---|---|
* |
List of CDRs. |
Typically implemented by market roles like: eMSP.
The CDRs endpoint can be used to create and retrieve CDRs.
Method | Description |
---|---|
Retrieve an existing CDR. |
|
Send a new CDR. |
|
PUT |
n/a (CDRs cannot be replaced) |
PATCH |
n/a (CDRs cannot be updated) |
DELETE |
n/a (CDRs cannot be removed) |
Fetch CDRs from the receivers system.
Endpoint structure definition:
No structure defined. This is open to the eMSP to define, the URL is provided to the CPO by the eMSP in the result of the POST request. Therefore, OCPI does not define variables.
Example:
https://www.server.com/ocpi/2.2.1/cdrs/1234
To retrieve an existing URL from the eMSP’s system, the URL, returned in the response to a POST of a new CDR, has to be used.
The endpoint returns the requested CDR, if it exists.
Datatype | Card. | Description |
---|---|---|
1 |
Requested CDR object. |
Creates a new CDR.
The POST method should contain the full and final CDR object.
Endpoint structure definition:
{cdr_endpoint_url}
Example:
https://www.server.com/ocpi/2.2.1/cdrs/
In the POST request the new CDR object is sent.
Type | Card. | Description |
---|---|---|
1 |
New CDR object. |
The response should contain the URL to the just created CDR object in the eMSP’s system.
HTTP Header | Datatype | Required | Description |
---|---|---|---|
Location |
yes |
URL to the newly created CDR in the eMSP’s system, can be used by the CPO system to perform a GET on the same CDR. |
The eMSP returns the URL where the newly created CDR can be found. OCPI does not define a specific structure for this URL.
Example:
https://www.server.com/ocpi/emsp/2.2.1/cdrs/123456
The CDR object describes the charging session and its costs, how these costs are composed, etc.
The CDR object is different from the Session object. The Session object is dynamic as it reflects the current state of the charging session. The information is meant to be viewed by the driver while the charging session is ongoing.
The CDR on the other hand can be thought of as sealed, preserving the information valid at the moment in time the underlying session was started. This is a requirement of the main use case for CDRs, namely invoicing. If e.g. a street is renamed the day after a session took place, the driver should be presented with the name valid at the time the session was started. This guarantees that the CDR will be recognized as correct by the driver and is not going to be contested.
The CDR object shall always contain information like Location, EVSE, Tariffs and Token as they were at the start of the charging session.
ChargingPeriod: A CPO SHALL at least start (and add) a ChargingPeriod every moment/event that has relevance for the total costs of a CDR. During a charging session, different parameters change all the time, like the amount of energy used, or the time of day. These changes can result in another Price Component of the Tariff becoming active. When another Price Component becomes active, the CPO SHALL add a new Charging Period with at least all the relevant information for the change to the other Price Component. The CPO is allowed to add more in-between Charging Periods to a CDR though.
Examples of additional Charging Periods that are required to be added because another Price Component is becoming active:
-
When an energy changes in price after 17:00. The CPO has to start a new Charging Period at 17:00. The CPO also has to list the energy in kWh consumed until 17:00 in the Charging Period that ends at 17:00.
-
When the price of a energy is higher when the EV is charging faster than 32A, a new Charging Period has to be added the moment the charging power goes over 32A. This may be a moment that is calculated by the CPO, as the Charge Point might not send the information to the CPO, but it can be interpolated by the CPO using the metering information before and after that moment.
step_size:
When calculating the cost of a charging session, step_size
SHALL only be taken into account once per session for
the TariffDimensionType ENERGY
and once for PARKING_TIME
and TIME
combined.
step_size
is not taken into account when switching time based paying for charging to paying for parking (charging has stopped but EV still connected).
Example: step_size
for both charging (TIME
) and parking is 5 minutes. After 21 minutes of charging,
the EV is full but remains connected for 7 more minutes. The cost of charging will be calculated based on 21 minutes (not 25).
The cost of parking will be calculated based on 10 minutes (step_size
is 5).
step_size
is not taken into account when switching from (for example) one ENERGY
based tariff element to another. This is also true when switch from one (TIME
) based tariff element to another (TIME
) based tariff element, and one PARKING_TIME
tariff element to another PARKING_TIME
based tariff element.
Example: when charging is more expensive after 17:00. The step_size
of the tariff before 17:00 will not be used when charging starts before 17:00 and ends after 17:00.
Only the step_size
of the tariff (PriceComponent) after 17:00 is taken into account, for the total of the same amount for the session.
The step_size
for the PriceComponent that is used to calculate the cost of such
a 'last' ChargingPeriod SHALL be used. If the step_size
differs for
the different TariffElements,
the step_size
of the last relevant PriceComponent is used.
The step_size
is not taken into account when switching between two Tariffs
Example: A driver selects a different Charging Preference
profile_type
during an ongoing charging session, the different profile might have a different tariff.
The step_size
uses the total amount of a certain unit used during a session, not only the last ChargingPeriod.
In other words, when the price of energy per kWh or the price of time per hour differs during a session,only the total amount of energy or time is used in calculations with step_size
.
Example: Energy costs € 0.20 perkWh before 17:00 and € 0.27 per kWh after 17:00. Both Price Components have a step_size
of 500 Wh.
If a driver charges 4.3 kWh before 17:00 and 1.1 kWh after 17:00, a total of 5.4 kWh is charged. The step_size
rounds this up to 5.5 kWh total.
It does NOT round the energy used after 17:00 to 1.5 kWh.
Example: Time costs € 5 per hour before 17:00 and € 7 per hour after 17:00. Both Price Components have a step_size
of 10 minutes.
If a driver charges 6 minutes before 17:00 and 22 minutes after 17:00, this makes a total of 28 minutes charging. The step_size
rounds this up to 30 minutes total, so 24 minutes after 17:00 will be billed.
It does NOT round the minutes after 17:00 to 30 minutes, which would have made a total of 36 minutes.
In the cases that TIME
and PARKING_TIME
Tariff Elements are both used, step_size
is only taken into account for the total parking duration`
Example: Time spent charging costs € 1.00 per hour and time spent parking (not charging) costs € 2.00 per hour. Both Price Components have a step_size
of 10 minutes.
If a driver charges 21 minutes, and keeps his EV connected while it is full for another 16 minutes, then the step_size rounds the parking duration up to 20 minutes, making it a total of 41 minutes. Note that the charging duration is not rounded up, as it is followed by another time based period.
Property | Type | Card. | Description |
---|---|---|---|
country_code |
CiString(2) |
1 |
ISO-3166 alpha-2 country code of the CPO that 'owns' this CDR. |
party_id |
CiString(3) |
1 |
ID of the CPO that 'owns' this CDR (following the ISO-15118 standard). |
id |
CiString(39) |
1 |
Uniquely identifies the CDR, the ID SHALL be unique per |
start_date_time |
1 |
Start timestamp of the charging session, or in-case of a reservation (before the start of a session) the start of the reservation. |
|
end_date_time |
1 |
The timestamp when the session was completed/finished, charging might have finished before the session ends, for example: EV is full, but parking cost also has to be paid. |
|
session_id |
CiString(36) |
? |
Unique ID of the Session for which this CDR is sent. Is only allowed to be omitted when the CPO has not implemented the Sessions module or this CDR is the result of a reservation that never became a charging session, thus no OCPI Session. |
cdr_token |
1 |
Token used to start this charging session, including all the relevant information to identify the unique token. |
|
auth_method |
1 |
Method used for authentication. Multiple <mod_cdrs_authmethod_enum,AuthMethods>> are possible during a charging sessions, for example when the session was started with a reservation: ReserveNow: |
|
authorization_reference |
CiString(36) |
? |
Reference to the authorization given by the eMSP.
When the eMSP provided an |
cdr_location |
1 |
Location where the charging session took place, including only the relevant EVSE and Connector. |
|
meter_id |
string(255) |
? |
Identification of the Meter inside the Charge Point. |
currency |
string(3) |
1 |
Currency of the CDR in ISO 4217 Code. |
tariffs |
* |
List of relevant Tariffs, see: Tariff. When relevant, a Free of Charge tariff should also be in this list, and point to a defined Free of Charge Tariff. |
|
charging_periods |
+ |
List of Charging Periods that make up this charging session. |
|
signed_data |
? |
Signed data that belongs to this charging Session. |
|
total_cost |
1 |
Total sum of all the costs of this transaction in the specified currency. |
|
total_fixed_cost |
? |
Total sum of all the fixed costs in the specified currency, except fixed price components of parking and reservation. The cost not depending on amount of time/energy used etc. Can contain costs like a start tariff. |
|
total_energy |
1 |
Total energy charged, in kWh. |
|
total_energy_cost |
? |
Total sum of all the cost of all the energy used, in the specified currency. |
|
total_time |
1 |
Total duration of the charging session (including the duration of charging and not charging), in hours. |
|
total_time_cost |
? |
Total sum of all the cost related to duration of charging during this transaction, in the specified currency. |
|
total_parking_time |
? |
Total duration of the charging session where the EV was not charging (no energy was transferred between EVSE and EV), in hours. |
|
total_parking_cost |
? |
Total sum of all the cost related to parking of this transaction, including fixed price components, in the specified currency. |
|
total_reservation_cost |
? |
Total sum of all the cost related to a reservation of a Charge Point, including fixed price components, in the specified currency. |
|
remark |
string(255) |
? |
Optional remark, can be used to provide additional human readable information to the CDR, for example: reason why a transaction was stopped. |
invoice_reference_id |
CiString(39) |
? |
This field can be used to reference an invoice, that will later be send for this CDR. Making it easier to link a CDR to a given invoice. Maybe even group CDRs that will be on the same invoice. |
credit |
boolean |
? |
When set to |
credit_reference_id |
CiString(39) |
? |
Is required to be set for a Credit CDR. This SHALL contain the |
home_charging_compensation |
boolean |
? |
When set to |
last_updated |
1 |
Timestamp when this CDR was last updated (or created). |
Note
|
The actual charging duration (energy being transferred between EVSE and EV) of a charging session can be calculated: total_charging_time = total_time - total_parking_time .
|
Note
|
Having both a credit and a credit_reference_id might seem redundant.
But it is seen as an advantage as a boolean flag used in queries is much faster than simple string comparison of references.
|
Note
|
Different authorization_reference values might happen when for example a ReserveNow had a different
authorization_reference then the value returned by a real-time authorization.
|
Note
|
When no start_date_time and/or end_date_time is known to the CPO, normally the CPO cannot send the CDR.
If the MSP and CPO both agree that they accept CDRs that miss either or both the start_date_time and end_date_time ,
and local legislation allows billing of sessions where start_date_time and/or end_date_time are missing.
Then, and only then, the CPO could send a CDR where the start_date_time and/or end_date_time are set to: "1970-1-1T00:00:00Z.
|
Value | Description |
---|---|
AUTH_REQUEST |
Authentication request has been sent to the eMSP. |
COMMAND |
Command like StartSession or ReserveNow used to start the Session, the Token provided in the Command was used as authorization. |
WHITELIST |
Whitelist used for authentication, no request to the eMSP has been performed. |
Property | Type | Card. | Description |
---|---|---|---|
type |
1 |
Type of CDR dimension. |
|
volume |
1 |
Volume of the dimension consumed, measured according to the dimension type. |
This enumeration contains allowed values for CdrDimensions, which are used to define dimensions of ChargingPeriods in both CDRs
and Sessions
.
Some of these values are not useful for CDRs
, and SHALL therefor only be used in Sessions
, these are marked in the column: Session Only
Value | Session Only |
Description |
---|---|---|
CURRENT |
Y |
Average charging current during this ChargingPeriod: defined in A (Ampere). When negative, the current is flowing from the EV to the grid. |
ENERGY |
Total amount of energy (dis-)charged during this ChargingPeriod: defined in kWh. When negative, more energy was feed into the grid then charged into the EV. Default step_size is 1. |
|
ENERGY_EXPORT |
Y |
Total amount of energy feed back into the grid: defined in kWh. |
ENERGY_IMPORT |
Y |
Total amount of energy charged, defined in kWh. |
MAX_CURRENT |
Sum of the maximum current over all phases, reached during this ChargingPeriod: defined in A (Ampere). |
|
MIN_CURRENT |
Sum of the minimum current over all phases, reached during this ChargingPeriod, when negative, current has flowed from the EV to the grid. Defined in A (Ampere). |
|
MAX_POWER |
Maximum power reached during this ChargingPeriod: defined in kW (Kilowatt). |
|
MIN_POWER |
Minimum power reached during this ChargingPeriod: defined in kW (Kilowatt), when negative, the power has flowed from the EV to the grid. |
|
PARKING_TIME |
Time during this ChargingPeriod not charging: defined in hours, default step_size multiplier is 1 second. |
|
POWER |
Y |
Average power during this ChargingPeriod: defined in kW (Kilowatt). When negative, the power is flowing from the EV to the grid. |
RESERVATION_TIME |
Time during this ChargingPeriod Charge Point has been reserved and not yet been in use for this customer: defined in hours, default step_size multiplier is 1 second. |
|
STATE_OF_CHARGE |
Y |
Current state of charge of the EV, in percentage, values allowed: 0 to 100. See note below. |
TIME |
Time charging during this ChargingPeriod: defined in hours, default step_size multiplier is 1 second. |
Note
|
OCPI makes it possible to provide SoC in the Session object. This information can be useful to show the current State of Charge to an EV driver during charging. Implementers should be aware that SoC is only available at some DC Chargers. Which is currently a small amount of the total amount of Charge Points. Of these DC Chargers, only a small percentage currently provides SoC via OCPP to the CPO. Then there is also the question if SoC is allowed to be provided to third-parties as it can be seen as privacy-sensitive information. So if an implementer wants to show SoC in, for example an App, care should be taken, to make the App work without SoC, as this will probably not always be available. |
The CdrLocation class contains only the relevant information from the Location object that is needed in a CDR.
Property | Type | Card. | Description |
---|---|---|---|
id |
CiString(36) |
1 |
Uniquely identifies the location within the CPO’s platform (and suboperator platforms). This field can never be changed, modified or renamed. |
name |
string(255) |
? |
Display name of the location. |
address |
string(45) |
1 |
Street/block name and house number if available. |
city |
string(45) |
1 |
City or town. |
postal_code |
string(10) |
? |
Postal code of the location, may only be omitted when the location has no postal code: in some countries charging locations at highways don’t have postal codes. |
state |
string(20) |
? |
State only to be used when relevant. |
country |
string(3) |
1 |
ISO 3166-1 alpha-3 code for the country of this location. |
coordinates |
1 |
Coordinates of the location. |
|
evse_uid |
CiString(36) |
1 |
Uniquely identifies the EVSE within the CPO’s platform (and suboperator platforms). For example a database unique ID or the actual EVSE ID. This field can never be changed, modified or renamed. This is the technical identification of the EVSE, not to be used as human readable identification, use the field: |
evse_id |
CiString(48) |
1 |
Compliant with the following specification for EVSE ID from "eMI3 standard version V1.0" (http://emi3group.com/documents-links/) "Part 2: business objects.". Allowed to be set to: |
connector_id |
CiString(36) |
1 |
Identifier of the connector within the EVSE. Allowed to be set to: |
connector_standard |
1 |
The standard of the installed connector. When this CDR is created for a reservation that never resulted in a charging session, this field can be set to any value and should be ignored by the Receiver. |
|
connector_format |
1 |
The format (socket/cable) of the installed connector. When this CDR is created for a reservation that never resulted in a charging session, this field can be set to any value and should be ignored by the Receiver. |
|
connector_power_type |
1 |
When this CDR is created for a reservation that never resulted in a charging session, this field can be set to any value and should be ignored by the Receiver. |
Property | Type | Card. | Description |
---|---|---|---|
country_code |
CiString(2) |
1 |
ISO-3166 alpha-2 country code of the MSP that 'owns' this Token. |
party_id |
CiString(3) |
1 |
ID of the eMSP that 'owns' this Token (following the ISO-15118 standard). |
uid |
CiString(36) |
1 |
Unique ID by which this Token can be identified. |
type |
1 |
Type of the token |
|
contract_id |
CiString(36) |
1 |
Uniquely identifies the EV driver contract token within the eMSP’s platform (and suboperator platforms). Recommended to follow the specification for eMA ID from "eMI3 standard version V1.0" (http://emi3group.com/documents-links/) "Part 2: business objects." |
A Charging Period consists of a start timestamp and a list of possible values that influence this period, for example: amount of energy charged this period, maximum current during this period etc.
Property | Type | Card. | Description |
---|---|---|---|
start_date_time |
1 |
Start timestamp of the charging period. A period ends when the next period starts. The last period ends when the session ends. |
|
dimensions |
+ |
List of relevant values for this charging period. |
|
tariff_id |
CiString(36) |
? |
Unique identifier of the Tariff that is relevant for this Charging Period. If not provided, no Tariff is relevant during this period. |
This class contains all the information of the signed data. Which encoding method is used, if needed, the public key and a list of signed values.
Property | Type | Card. | Description |
---|---|---|---|
encoding_method |
CiString(36) |
1 |
The name of the encoding used in the SignedData field. This is the name given to the encoding by a company or group of companies. See note below. |
encoding_method_version |
int |
? |
Version of the EncodingMethod (when applicable) |
public_key |
string(512) |
? |
Public key used to sign the data, base64 encoded. |
signed_values |
+ |
One or more signed values. |
|
url |
string(512) |
? |
URL that can be shown to an EV driver. This URL gives the EV driver the possibility to check the signed data from a charging session. |
Note
|
For the German Eichrecht, different solutions are used, all have (somewhat) different encodings. Below the table with known implementations and the contact information for more information. |
Name | Description | Contact |
---|---|---|
OCMF |
Proposed by SAFE |
|
Alfen Eichrecht |
Alfen Eichrecht encoding / implementation. |
|
EDL40 E-Mobility Extension |
eBee smart technologies implementation |
|
EDL40 Mennekes |
Mennekes implementation |
This class contains the signed and the plain/unsigned data. By decoding the data, the receiver can check if the content has not been altered.
Property | Type | Card. | Description |
---|---|---|---|
nature |
CiString(32) |
1 |
Nature of the value, in other words, the event this value belongs to. |
plain_data |
string(512) |
1 |
The un-encoded string of data. The format of the content depends on the EncodingMethod field. |
signed_data |
string(5000) |
1 |
Blob of signed data, base64 encoded. The format of the content depends on the EncodingMethod field. |