Transaction Flows - CSMS Developer Guide
Based on OCPP 2.0.1 Edition 4 Specification (Part 2, Section E). This guide covers all transaction flows from the CSMS (Charging Station Management System) perspective.
1. Overview & Core Concepts
IntroductionTransactions in OCPP 2.0.1 represent a portion of a charging session recorded by the CSMS — a
single time frame with a start and stop time, used for billing. All transaction state changes
are communicated via TransactionEventRequest.
1.1 Transaction Definition
One Transaction per EVSE
At most one transaction can be active on an EVSE at any point in time.
CS-Generated Transaction IDs
Transaction IDs are generated by the Charging Station (not the CSMS). Recommended format: UUIDs. A TransactionId MUST be unique for the lifetime of a Charging Station — it survives reboots and firmware updates.
Billing Unit
Each transaction is the billing unit — the CSMS calculates cost based on meter values provided within transaction events.
CSMS as Passive Receiver
The Charging Station drives transaction lifecycle. The CSMS receives events and responds;
it cannot force-start a transaction without using RequestStartTransactionRequest.
1.2 Flexible Transaction Start/Stop
Two configuration variables control exactly when transactions start and stop, enabling different billing models:
| Variable | Possible Values | Description |
|---|---|---|
TxStartPoint | ParkingBayOccupancy, EVConnected, Authorized, DataSigned, PowerPathClosed, EnergyTransfer | Defines when a transaction starts |
TxStopPoint | ParkingBayOccupancy, EVConnected, Authorized, PowerPathClosed, EnergyTransfer | Defines when a transaction ends |
Common billing model configurations:
| Billing Model | TxStartPoint | TxStopPoint |
|---|---|---|
| Connection time | EVConnected | EVConnected |
| Time of use | EVConnected, Authorized | EVConnected |
| Charging time | PowerPathClosed | PowerPathClosed |
| OCPP 1.6 compat | PowerPathClosed | EVConnected, Authorized |
1.3 Core Message: TransactionEventRequest
All transaction state changes are communicated via TransactionEventRequest sent from CS to CSMS. The eventType field has three values:
Started
First event of a transaction
Updated
Intermediate events during a transaction
Ended
Last event of a transaction
1.4 Sequence Numbers
The CS maintains a per-EVSE counter (seqNo) for TransactionEventRequest messages. It starts at 0 for the first message
of a transaction and increments by 1 for each subsequent message. The CSMS can verify completeness
by checking that it received all integer values from start to end.
1.5 Optional Fields Behavior
Some optional fields in TransactionEventRequest are sent only once, not with every message:
| Field | When Sent |
|---|---|
evse (id + connectorId) | First TransactionEventRequest after EV connects (not repeated) |
idToken | Once after authorization is obtained; once when authorization ends |
reservationId | First TransactionEventRequest after authorization, if reservation exists |
transactionInfo.remoteStartId | In the next TransactionEventRequest after RequestStartTransactionRequest |
transactionInfo.chargingState | Whenever charging state changes (always present in eventType=Started) |
transactionInfo.stoppedReason | In the final eventType=Ended message (may be omitted if Local) |
meterValue | Based on meter value configuration; context = Transaction.Begin / Transaction.End / Sample.Periodic |
2. Message Schemas Reference
Protocol2.1 TransactionEventRequest (CS → CSMS)
Sent by the Charging Station to report all transaction lifecycle events — start, updates, and end.
{
"eventType": "Started" | "Updated" | "Ended", // Required
"timestamp": "2024-01-15T10:30:00Z", // Required (ISO 8601)
"triggerReason": "<TriggerReasonEnumType>", // Required
"seqNo": 0, // Required (integer)
"transactionInfo": { // Required
"transactionId": "string (max 36)", // Required
"chargingState": "Charging" | "EVConnected" | "SuspendedEV" | "SuspendedEVSE" | "Idle",
"timeSpentCharging": 3600, // seconds of actual energy flow
"stoppedReason": "<ReasonEnumType>", // Only for eventType=Ended
"remoteStartId": 123 // Links to RequestStartTransaction
},
"offline": false, // default false
"numberOfPhasesUsed": 3,
"cableMaxCurrent": 32, // Amperes
"reservationId": 456,
"evse": {
"id": 1, // Required (> 0)
"connectorId": 1
},
"idToken": {
"idToken": "string (max 36)", // Required
"type": "<IdTokenEnumType>" // Required
},
"meterValue": [
{
"timestamp": "2024-01-15T10:30:00Z", // Required
"sampledValue": [
{
"value": 1234.5, // Required (number)
"context": "Transaction.Begin" | "Transaction.End" | "Sample.Periodic" | ...,
"measurand": "Energy.Active.Import.Register", // default
"phase": "L1" | "L2" | "L3" | ...,
"location": "Outlet", // default
"unitOfMeasure": {
"unit": "Wh", // default
"multiplier": 0 // default (10^0 = 1)
}
}
]
}
]
}TriggerReasonEnumType values
| Value | Description |
|---|---|
Authorized | EV Driver authorized |
CablePluggedIn | Cable plugged in |
ChargingRateChanged | Charging rate changed |
ChargingStateChanged | Charging state changed |
Deauthorized | EV Driver deauthorized |
EnergyLimitReached | Energy limit reached |
EVCommunicationLost | Communication with EV lost |
EVConnectTimeout | EV connection timeout |
EVDetected | EV detected (parking bay) |
EVDeparted | EV departed (parking bay) |
MeterValueClock | Clock-aligned meter value |
MeterValuePeriodic | Periodic meter value |
TimeLimitReached | Time limit reached |
Trigger | Triggered by TriggerMessageRequest |
UnlockCommand | Unlock command received |
StopAuthorized | Stop authorization received |
RemoteStop | Remote stop received |
RemoteStart | Remote start received |
AbnormalCondition | Abnormal error or fault |
SignedDataReceived | Signed meter data received |
ResetCommand | Reset command received |
ReasonEnumType (stoppedReason) values
DeAuthorizedEmergencyStopEnergyLimitReachedEVDisconnectedGroundFaultImmediateResetLocalLocalOutOfCreditMasterPassOtherOvercurrentFaultPowerLossPowerQualityRebootRemoteSOCLimitReachedStoppedByEVTimeLimitReachedTimeoutChargingStateEnumType values
| Value | Description |
|---|---|
Charging | Energy is flowing to the EV |
EVConnected | EV is connected but not charging |
SuspendedEV | Charging suspended by the EV |
SuspendedEVSE | Charging suspended by the EVSE |
Idle | No EV connected |
IdTokenEnumType values
CentraleMAIDISO14443ISO15693KeyCodeLocalMacAddressNoAuthorization2.2 TransactionEventResponse (CSMS → CS)
The CSMS responds to acknowledge the transaction event. Must always be sent — even an empty response is valid.
{
"totalCost": 12.50, // Only when eventType=Ended; omit = not free; 0.00 = free
"chargingPriority": 0, // -9 to 9 (overrides IdTokenInfo priority temporarily)
"idTokenInfo": { // Include when idToken was in request
"status": "<AuthorizationStatusEnumType>", // Required
"cacheExpiryDateTime": "2024-02-01T00:00:00Z",
"chargingPriority": 0,
"language1": "en",
"language2": "nl",
"evseId": [1, 2], // Restrict token to specific EVSEs
"groupIdToken": {
"idToken": "group-token-123",
"type": "Central"
},
"personalMessage": {
"format": "UTF8",
"content": "Welcome, John!"
}
},
"updatedPersonalMessage": {
"format": "UTF8",
"content": "Updated message during transaction"
}
}CSMS implementation logic:
- Always acknowledge receipt — an empty
{}is valid - If
idTokenis present in the request: validate the token and returnidTokenInfo - On
eventType=Ended: optionally includetotalCost(omit = not free, 0.00 = free) - If token has a groupIdToken: include it in the response
| AuthorizationStatus | Description |
|---|---|
Accepted | Token is valid, charging allowed |
Blocked | Token is blocked |
ConcurrentTx | Token already in use in another transaction |
Expired | Token has expired |
Invalid | Token is unknown/invalid |
NoCredit | Token has no credit |
NotAllowedTypeEVSE | Token not allowed on this EVSE type |
NotAtThisLocation | Token not valid at this location |
NotAtThisTime | Token not valid at this time |
Unknown | Token is unknown |
2.3 RequestStartTransactionRequest (CSMS → CS)
The CSMS can remotely initiate a charging session on a specific EVSE.
{
"remoteStartId": 123, // Required (integer)
"idToken": { // Required
"idToken": "string (max 36)",
"type": "<IdTokenEnumType>"
},
"evseId": 1, // Optional (> 0)
"groupIdToken": { // Optional
"idToken": "group-123",
"type": "Central"
},
"chargingProfile": { // Optional (purpose MUST be TxProfile)
"id": 1,
"stackLevel": 0,
"chargingProfilePurpose": "TxProfile",
"chargingProfileKind": "Relative",
"chargingSchedule": [ "..." ]
}
}2.4 RequestStartTransactionResponse (CS → CSMS)
{
"status": "Accepted" | "Rejected", // Required
"transactionId": "existing-tx-id-123", // Optional: if transaction already started (e.g. cable first)
"statusInfo": {
"reasonCode": "string (max 20)",
"additionalInfo": "string (max 512)"
}
}2.5 RequestStopTransactionRequest (CSMS → CS)
The CSMS can remotely stop an ongoing transaction by its transaction ID.
{
"transactionId": "string (max 36)" // Required
}2.6 RequestStopTransactionResponse (CS → CSMS)
{
"status": "Accepted" | "Rejected", // Required
"statusInfo": {
"reasonCode": "string (max 20)",
"additionalInfo": "string (max 512)"
}
}2.7 GetTransactionStatusRequest (CSMS → CS)
Allows the CSMS to query whether a specific transaction is still ongoing and whether there are
pending messages. Omit transactionId to check for ANY pending transaction messages.
{
"transactionId": "string (max 36)" // Optional: omit to check for any pending messages
}2.8 GetTransactionStatusResponse (CS → CSMS)
{
"messagesInQueue": true, // Required: CS has pending transaction messages
"ongoingIndicator": true // Optional: transaction is still active
}| Field Value | Meaning |
|---|---|
messagesInQueue = true | CS still has messages to deliver — wait for them before finalizing billing |
messagesInQueue = false | All messages delivered — safe to finalize billing |
ongoingIndicator = true | Transaction is still active on the CS |
ongoingIndicator = false | Transaction has ended on the CS |
3. Transaction Flows
Use Cases E01-E15Start Transaction Options
Understand the different moments a Charging Station can start a transaction based on its TxStartPoint configuration.
Start Triggers by TxStartPoint:
| TxStartPoint Config | Trigger | triggerReason | Notes |
|---|---|---|---|
ParkingBayOccupancy | Parking bay detector triggers | EVDetected | Driver may not be known yet |
EVConnected | Cable plugged in / EV connection established | CablePluggedIn | Driver may not be known yet |
Authorized | EV Driver authorized | Authorized | idToken always present |
DataSigned | Signed meter value retrieved | SignedDataReceived | Requires meter with signing capability |
PowerPathClosed | All preconditions met (authorized + connected) | Authorized or CablePluggedIn | Energy may not flow yet |
EnergyTransfer | Energy flow starts | ChargingStateChanged | chargingState = Charging |
CSMS Implementation Steps:
- Store the transaction — Create a new transaction record using
transactionInfo.transactionId. Record thetimestamp,evse.id,evse.connectorId, andseqNo. - Validate the IdToken (if present) — Look up the
idTokenin your authorization system. - Respond with
TransactionEventResponsecontainingidTokenInfowith status andgroupIdTokenif one exists. - Track sequence numbers — Store
seqNoto verify all messages are received.
Key Requirements:
| ID | Requirement |
|---|---|
E01.FR.11 | CSMS MUST verify the validity of the identifier |
E01.FR.12 | Response SHALL include idTokenInfo with authorization status and groupIdToken if one exists |
{}{
"idTokenInfo": {
"status": "Accepted",
"groupIdToken": {
"idToken": "group-abc",
"type": "Central"
}
}
}Start Transaction: Cable Plugin First
Handle the flow where the EV Driver plugs in the cable before authenticating.
CS -> CSMS: StatusNotificationRequest (connectorStatus = Occupied)
CSMS -> CS: StatusNotificationResponse
CS -> CSMS: TransactionEventRequest(eventType=Started, triggerReason=CablePluggedIn,
chargingState=EVConnected, transactionId=AB1234, evse.id=1,
evse.connectorId=1, meterValues, seqNo=0)
CSMS -> CS: TransactionEventResponse()
... EV Driver authenticates ...
CS -> CSMS: TransactionEventRequest(eventType=Updated, triggerReason=Authorized,
transactionId=AB1234, idToken={...}, seqNo=1)
CSMS -> CS: TransactionEventResponse(idTokenInfo={status: Accepted})
... Energy starts flowing ...
CS -> CSMS: TransactionEventRequest(eventType=Updated, triggerReason=ChargingStateChanged,
chargingState=Charging, transactionId=AB1234, seqNo=2, meterValues)
CSMS -> CS: TransactionEventResponse()CSMS Implementation Steps:
- On StatusNotificationRequest — Update connector status to
Occupied. Respond with emptyStatusNotificationResponse. - On first TransactionEventRequest (Started) — Create transaction record. Note:
idTokenis NOT present yet. StorechargingState = EVConnectedand meter values withcontext = Transaction.Begin. - On Updated (triggerReason=Authorized) — Validate the
idTokenagainst your authorization system. Check for reservations ifreservationIdis present. Respond withidTokenInfo. - On Updated (chargingState=Charging) — Update transaction state and store periodic meter values.
Key Requirements:
| ID | Requirement |
|---|---|
E02.FR.02 | CSMS SHALL send TransactionEventResponse with authorization status value |
E02.FR.04 | CSMS SHALL verify validity of identifier (might be authorized locally with outdated info) |
Start Transaction: IdToken First
Handle the flow where the EV Driver authenticates before plugging in the cable.
... EV Driver authenticates first (via AuthorizeRequest or locally) ...
CS -> CSMS: TransactionEventRequest(eventType=Started, triggerReason=Authorized,
transactionId=AB1234, idToken.id=1234, seqNo=N)
CSMS -> CS: TransactionEventResponse(idTokenInfo={status: Accepted})
... EV Driver plugs in cable ...
CS -> CSMS: StatusNotificationRequest(status=Occupied)
CSMS -> CS: StatusNotificationResponse()
CS -> CSMS: TransactionEventRequest(eventType=Updated, triggerReason=CablePluggedIn,
chargingState=EVConnected, transactionId=AB1234, seqNo=N+1)
CSMS -> CS: TransactionEventResponse()
... Energy starts flowing ...
CS -> CSMS: TransactionEventRequest(eventType=Updated, triggerReason=ChargingStateChanged,
chargingState=Charging, transactionId=AB1234, seqNo=N+2, meterValues)
CSMS -> CS: TransactionEventResponse()CSMS Implementation Steps:
- On Started (triggerReason=Authorized) —
idTokenIS present in this first message. Validate the token, respond withidTokenInfo. IfreservationIdis present, link the transaction to the reservation. - On Updated (triggerReason=CablePluggedIn) — First time
evse.idandevse.connectorIdappear. Update transaction with EVSE assignment. - Handle EVConnectionTimeout — If the driver does not plug in within
EVConnectionTimeOut, the CS may sendTransactionEventRequest(eventType=Ended, triggerReason=EVConnectTimeout, stoppedReason=Timeout). CSMS should close the transaction.
Key Requirements:
| ID | Requirement |
|---|---|
E03.FR.01 | TransactionEventRequest SHALL contain IdTokenType info |
E03.FR.02 | CSMS SHALL respond with TransactionEventResponse including authorization status |
E03.FR.03 | If transaction ends a reservation, TransactionEventRequest SHALL contain reservationId |
E03.FR.05 | If EV not plugged in before EVConnectionTimeOut AND TxStopPoint doesn't contain ParkingBayOccupancy, CS SHOULD end the transaction |
Transaction Started While Charging Station is Offline
Handle transactions that were started while the Charging Station had no connection to the CSMS.
... CS is offline, starts transaction using local auth ...
... Connection restored ...
CS -> CSMS: HeartbeatRequest()
CSMS -> CS: HeartbeatResponse()
CS -> CSMS: TransactionEventRequest(eventType=Started, offline=true,
transactionId=XY5678, seqNo=0, ...)
CSMS -> CS: TransactionEventResponse(...)
CS -> CSMS: TransactionEventRequest(eventType=Updated, offline=true,
transactionId=XY5678, seqNo=1, ...)
CSMS -> CS: TransactionEventResponse(...)
... more queued messages ...CSMS Implementation Steps:
- Accept all queued messages — When
offline = true, the CSMS receives messages that may be hours or days old. Process them chronologically. - Validate tokens retroactively — The CS authorized locally. The CSMS should still validate the token and include
idTokenInfoin responses to update the CS authorization cache. - Handle out-of-order delivery — Messages may arrive in a different order. Use
seqNoandtimestampto reconstruct the correct sequence. - Always respond — The CSMS MUST always respond to TransactionEventRequest, even for offline transactions. Not responding causes the CS to retry (see E13).
Key Requirements:
| ID | Requirement |
|---|---|
E04.FR.01 | CS MUST queue TransactionEventRequest when offline |
E04.FR.02 | CS MUST send queued messages after connection restored |
E04.FR.03 | offline flag SHALL be TRUE for any message that occurred while offline |
Start Transaction: Id Not Accepted
Handle transactions where the CSMS rejects the IdToken that was authorized locally by the Charging Station.
CS -> CSMS: TransactionEventRequest(eventType=Started, transactionId=AB1234,
evse.id=1, evse.connectorId=1, seqNo=N, meterValues)
CSMS -> CS: TransactionEventResponse(idTokenInfo={status: Blocked|Invalid|Expired|Unknown})
... CS stops energy based on StopTxOnInvalidId / MaxEnergyOnInvalidId config ...CSMS Implementation Steps:
- Validate the IdToken (CRITICAL) — The identifier might have been authorized locally using outdated information. The CSMS MUST verify validity (E05.FR.01).
- Respond with non-Accepted status when the token is invalid.
- What happens next depends on CS configuration (not CSMS controlled):
| CS Config | CS Behavior | What CSMS Receives |
|---|---|---|
| StopTxOnInvalidId = false | Suspends energy, keeps transaction | TransactionEventRequest(Updated, ChargingStateChanged, SuspendedEVSE) |
| StopTxOnInvalidId = false + MaxEnergyOnInvalidId set | Allows limited energy | Nothing until limit reached |
| StopTxOnInvalidId = true, TxStopPoint has Authorized/PowerPathClosed/EnergyTransfer | Ends transaction | TransactionEventRequest(Ended, Deauthorized, stoppedReason=DeAuthorized) |
| StopTxOnInvalidId = true, TxStopPoint = EVConnected | Deauthorizes but keeps tx | TransactionEventRequest(Updated, Deauthorized, chargingState=EVConnected) |
{
"idTokenInfo": {
"status": "Blocked" // or Invalid, Expired, Unknown, etc.
}
}Stop Transaction Options
Understand the different moments a Charging Station can stop a transaction based on its TxStopPoint configuration.
| TxStopPoint Config | Trigger | triggerReason in Ended | stoppedReason |
|---|---|---|---|
ParkingBayOccupancy | EV departs parking bay | EVDeparted | Local |
EVConnected | Cable unplugged / EV disconnected | EVCommunicationLost | EVDisconnected |
Authorized | Driver presents token to stop / CSMS revokes auth | StopAuthorized or Deauthorized | Local or DeAuthorized |
PowerPathClosed | EV disconnected or auth revoked | varies | varies |
EnergyTransfer | Energy transfer stops | ChargingStateChanged | varies |
CSMS Implementation Steps:
- Close the transaction — Mark it as completed with the
timestampfrom the Ended message. - Store final meter values — The
meterValuearray withcontext = Transaction.Endcontains final readings for billing. - Calculate and return cost (optional) — Return
totalCostin the response. Omit = not free; 0.00 = free. - Record stoppedReason — Important for billing disputes and analytics.
- Validate idToken (if present) — The stopping token may differ from the starting token (same GroupId). Still respond with
idTokenInfo.
Key Requirements:
| ID | Requirement |
|---|---|
E06.FR.08 | stoppedReason SHALL be included if not ended by EV Driver |
E06.FR.09 | stoppedReason MAY be omitted if ended by EV Driver (CSMS interprets as Local) |
E06.FR.11 | Ended message SHALL include meter values with context = Transaction.End |
E06.FR.16 | If stopped by abnormal condition, triggerReason = AbnormalCondition |
Transaction Locally Stopped by IdToken
Handle the flow where an EV Driver presents their IdToken to stop an ongoing transaction.
CS -> CSMS: TransactionEventRequest(
eventType=Ended,
triggerReason=StopAuthorized,
stoppedReason=Local,
idToken.id=1234,
transactionId=AB1234,
chargingState=EVConnected,
seqNo=N+1,
meterValues
)
CSMS -> CS: TransactionEventResponse(idTokenInfo={status: ...})CS -> CSMS: TransactionEventRequest(
eventType=Updated,
triggerReason=StopAuthorized,
idToken.id=1234,
transactionId=AB1234,
seqNo=N+1
)
CSMS -> CS: TransactionEventResponse(idTokenInfo={status: ...})
... CS stops energy, unlocks cable ...
CS -> CSMS: TransactionEventRequest(
eventType=Ended,
triggerReason=ChargingStateChanged,
chargingState=EVConnected,
stoppedReason=Local,
seqNo=N+2,
meterValues
)
CSMS -> CS: TransactionEventResponse(totalCost=15.75)CSMS Implementation Steps:
- On Updated with triggerReason=StopAuthorized — The stopping
idTokenmay differ from the starting token (they share the same GroupId). Validate the token and respond withidTokenInfo. Note: the CSMS CANNOT prevent a transaction from stopping. - On Ended — Close the transaction, store final meter values, optionally return
totalCost, respond withidTokenInfoifidTokenwas included.
Key Requirements:
| ID | Requirement |
|---|---|
E07.FR.01 | CS SHALL end authorization without sending AuthorizeRequest first (if same token or same GroupId) |
E07.FR.02 | CS SHALL send TransactionEventRequest with triggerReason=StopAuthorized and include the stopping idToken |
E07.FR.04 | stoppedReason MAY be omitted if stopped locally by EV driver |
E07.FR.05 | stoppedReason SHOULD be Local if stopped by EV driver at CS |
Transaction Stopped While Charging Station is Offline
Handle transactions that were stopped while the Charging Station had no connection to the CSMS.
... CS is offline, EV Driver presents token to stop ...
... CS validates locally (same token or same GroupId) ...
... CS stores TransactionEventRequest(eventType=Ended, offline=true) ...
... Connection restored ...
CS -> CSMS: TransactionEventRequest(eventType=Ended, offline=true,
transactionId=AB1234, stoppedReason=Local, meterValues)
CSMS -> CS: TransactionEventResponse(totalCost=12.50)CSMS Implementation Steps:
- Process identically to online stop — The transaction end is valid; process meter values and calculate costs.
- Note the
offline = trueflag — The stop happened in the past, potentially hours or days ago. - Respond normally — Include
totalCostif applicable.
Key Requirements:
| ID | Requirement |
|---|---|
E08.FR.04 | CS SHALL generate TransactionEventRequest(eventType=Ended) |
E08.FR.05 | CS MUST queue messages when offline |
E08.FR.06 | CS MUST send queued messages after connection restored |
E08.FR.07 | offline SHALL be TRUE |
Cable Disconnected on EV-side: Stop Transaction
Handle transactions that are stopped when the EV Driver unplugs the cable on the EV side.
Prerequisite: StopTxOnEVSideDisconnect = true
CS -> CSMS: TransactionEventRequest(eventType=Ended, transactionId=AB1234,
triggerReason=EVCommunicationLost, stoppedReason=EVDisconnected,
seqNo=N+1, meterValues)
CSMS -> CS: TransactionEventResponse(totalCost=10.00)
... EV Driver authenticates and unplugs cable from CS side ...
CS -> CSMS: StatusNotificationRequest(status=Available)
CSMS -> CS: StatusNotificationResponse()CSMS Implementation Steps:
- Close the transaction immediately upon receiving the Ended event.
- Store meter values — These are the final readings for billing.
- Return totalCost if applicable.
- Update connector status when
StatusNotificationRequest(Available)arrives.
Key Requirements:
| ID | Requirement |
|---|---|
E09.FR.01 | Transaction SHALL be deauthorized when cable disconnected. If EV reconnects, energy not allowed until re-authorized |
Cable Disconnected on EV-side: Suspend Transaction
Handle the flow where the cable is disconnected on the EV side but the transaction is NOT stopped — just suspended.
Prerequisite: StopTxOnEVSideDisconnect = false
CS -> CSMS: TransactionEventRequest(eventType=Updated, transactionId=AB1234,
chargingState=SuspendedEV, triggerReason=EVCommunicationLost,
seqNo=N+1, meterValues)
CSMS -> CS: TransactionEventResponse()CS -> CSMS: TransactionEventRequest(eventType=Updated, transactionId=AB1234,
chargingState=Charging, triggerReason=CablePluggedIn, seqNo=N+2)
CSMS -> CS: TransactionEventResponse()
... Continues with E02 Cable Plugin First flow ... ... EV Driver authenticates ...
CS -> CSMS: TransactionEventRequest(eventType=Ended, transactionId=AB1234,
triggerReason=StopAuthorized, seqNo=N+2, meterValues)
CSMS -> CS: TransactionEventResponse(totalCost=...)CSMS Implementation Steps:
- On Updated (SuspendedEV) — Update transaction state to suspended. Do NOT close the transaction. Continue tracking seqNo.
- On subsequent Updated (Charging, CablePluggedIn) — Transaction resumes. Update state back to active.
- On Ended — Close the transaction and calculate total cost based on all meter values received during the full transaction.
Connection Loss During Transaction
Handle the scenario where the connection between CS and CSMS is lost while a transaction is ongoing.
... Transaction is ongoing, connection is lost ...
... CS continues charging, queues TransactionEventRequest messages ...
... Connection restored ...
CS -> CSMS: TransactionEventRequest(eventType=Updated, offline=true,
transactionId=AB1234, seqNo=3, meterValues, ...)
CSMS -> CS: TransactionEventResponse(...)
CS -> CSMS: TransactionEventRequest(eventType=Updated, offline=true,
transactionId=AB1234, seqNo=4, meterValues, ...)
CSMS -> CS: TransactionEventResponse(...)CSMS Implementation Steps:
- Detect missing messages — Use
seqNoto identify gaps. If you received seqNo 2 and then receive seqNo 5, messages 3 and 4 are missing. - Process queued messages — All messages with
offline = trueshould be processed and stored. - Use GetTransactionStatus (E14) to check if all messages have been delivered.
- Validate tokens retroactively — Include
idTokenInfoin responses to update the CS authorization cache.
Key Behavior:
- CS MUST queue all TransactionEventRequest messages while offline
- CS MUST send queued messages once connection is restored
- CS sets
offline = trueon all queued messages - Transaction continues on the CS regardless of connection state
Inform CSMS of an Offline Occurred Transaction
Handle a complete transaction (started AND stopped) that occurred entirely while the Charging Station was offline.
... Connection restored ...
CS -> CSMS: TransactionEventRequest(eventType=Started, offline=true,
transactionId=OFF123, seqNo=0, timestamp=<past>, ...)
CSMS -> CS: TransactionEventResponse(...)
CS -> CSMS: TransactionEventRequest(eventType=Updated, offline=true,
transactionId=OFF123, seqNo=1, ...)
CSMS -> CS: TransactionEventResponse(...)
CS -> CSMS: TransactionEventRequest(eventType=Ended, offline=true,
transactionId=OFF123, seqNo=2, stoppedReason=Local, meterValues)
CSMS -> CS: TransactionEventResponse(totalCost=8.25)CSMS Implementation Steps:
- Process the full transaction retroactively — Create the transaction, process all events, close it.
- All messages will have
offline = true— The entire transaction happened while offline. - Calculate costs — Based on the meter values provided in the Started and Ended messages.
- Always respond — Even if the transaction already ended, always respond to allow the CS to clear its queue.
Key Requirements:
| ID | Requirement |
|---|---|
E12.FR.01 | CS MUST queue TransactionEventRequest when offline |
E12.FR.02 | CS MUST send queued messages after connection restored |
E12.FR.03 | offline SHALL be TRUE for any message that occurred while offline |
Transaction-related Message Not Accepted by CSMS
Define behavior when the CSMS fails to respond to a TransactionEventRequest or responds with an error.
This use case is primarily about CS retry behavior
The CSMS must understand it to handle duplicates and retry scenarios correctly.
CSMS Implementation Steps:
- If CSMS doesn't respond within the timeout — The CS will retry the message. The CSMS may receive duplicate messages with the same
transactionId + seqNo. - Handle duplicates gracefully — Use
transactionId + seqNoas an idempotency key. If the same pair is received again, respond normally but don't process it twice. - Never fail to respond — The outcome of sanity checks SHALL NOT cause the CSMS to not respond. Always send a
TransactionEventResponse, even if the data seems invalid. Failing to respond only causes the CS to retry. - CALLERROR responses — If the CSMS sends a CALLERROR, the CS should not retry. Use this only for malformed messages that can never be accepted.
Idempotency Pattern:
The combination of transactionId + seqNo uniquely identifies a message.
Store processed pairs and skip re-processing if the same pair arrives again.
Check Transaction Status
Allow the CSMS to query the Charging Station about the status of a transaction and whether there are pending messages.
CSMS -> CS: GetTransactionStatusRequest(transactionId="AB1234")
CS -> CSMS: GetTransactionStatusResponse(
ongoingIndicator=true,
messagesInQueue=true
)CSMS -> CS: GetTransactionStatusRequest() // no transactionId
CS -> CSMS: GetTransactionStatusResponse(
messagesInQueue=false
)CSMS Implementation Steps:
- When to use — After a connection restoration to check for pending messages; to verify if a specific transaction is still ongoing; to confirm all messages for a completed transaction have been delivered.
- With transactionId — Check a specific transaction's pending status and whether it's still active.
- Without transactionId — Check if ANY transaction messages are pending on the CS.
| Scenario | Request | Expected Response |
|---|---|---|
| After connection restore, check pending | {} (no transactionId) | {messagesInQueue: true/false} |
| Verify all messages received for billing | {transactionId: "X"} | {ongoingIndicator: false, messagesInQueue: false} |
| Check if transaction still active | {transactionId: "X"} | {ongoingIndicator: true, messagesInQueue: true} |
End of Charging Process
Handle the final steps of the charging process, including interrupting and stopping charging.
Interrupting Charging (CSMS-initiated):
| Method | Action | Result |
|---|---|---|
| Change availability | Send ChangeAvailabilityRequest to make EVSE unavailable | CS stops energy delivery but may not end the transaction |
| Revoke authorization | Respond to any TransactionEventRequest with idTokenInfo.status != Accepted | CS suspends or ends transaction based on StopTxOnInvalidId config |
| Remote Stop (E15) | Send RequestStopTransactionRequest(transactionId=...) | CS responds Accepted/Rejected, then sends TransactionEventRequest(Ended, stoppedReason=Remote) |
The charging process ends when:
- All transaction events are delivered — CSMS has received
TransactionEventRequest(eventType=Ended) - All messages are delivered — Verified via GetTransactionStatus (
messagesInQueue=false) - Connector becomes available — CS sends
StatusNotificationRequest(Available)
CSMS Finalization Checklist:
- Received TransactionEventRequest(eventType=Ended) with final meter values
- Verified all seqNo values are present (no gaps)
- Optionally confirmed via GetTransactionStatusRequest that messagesInQueue=false
- Calculated final cost and included totalCost in the last TransactionEventResponse
- Updated connector status based on StatusNotificationRequest
- Updated authorization cache based on idTokenInfo sent in responses
4. Configuration Variables
ReferenceThese configuration variables control transaction behavior. They are set on the Charging
Station, but the CSMS can read and write them via the GetVariables / SetVariables messages.
TxCtrlr Component
| Variable | Description |
|---|---|
TxStartPoint | Defines when transaction starts. Values: ParkingBayOccupancy, EVConnected, Authorized, DataSigned, PowerPathClosed, EnergyTransfer |
TxStopPoint | Defines when transaction ends. Values: ParkingBayOccupancy, EVConnected, Authorized, PowerPathClosed, EnergyTransfer |
StopTxOnInvalidId | Whether to stop the transaction when the Id becomes invalid (CSMS returns non-Accepted). Default behavior varies. |
MaxEnergyOnInvalidId | Maximum Wh to deliver with an invalid Id (only when StopTxOnInvalidId=false). Transaction suspends after this limit. |
StopTxOnEVSideDisconnect | Stop the transaction when the cable is disconnected at the EV side (E09 vs E10). |
UnlockOnEVSideDisconnect | Unlock the connector when the cable is disconnected at the EV side. |
EVConnectionTimeOut | Timeout (seconds) for the EV to connect after authorization. If exceeded, CS may send eventType=Ended with stoppedReason=Timeout. |
AuthCtrlr Component
| Variable | Description |
|---|---|
AuthorizeRemoteStart | Whether the CS should send an AuthorizeRequest to the CSMS before starting a transaction initiated by RequestStartTransactionRequest. |
MasterPassGroupId | GroupId for Master Pass tokens. Tokens in this group can stop any transaction (emergency use). |
5. Decision Tree & State Machine
ReferenceAppendix A: CSMS Response Decision Tree
How to respond to TransactionEventRequest based on eventType and whether an idToken is
present:
On receiving TransactionEventRequest:
|
|-- eventType = Started?
| |-- idToken present?
| | |-- Validate token
| | |-- Return idTokenInfo with status + groupIdToken
| |-- No idToken?
| |-- Return empty response {}
|
|-- eventType = Updated?
| |-- idToken present (triggerReason = Authorized or StopAuthorized)?
| | |-- Validate token
| | |-- Return idTokenInfo with status
| |-- No idToken?
| |-- Return empty response {}
| |-- Optionally return updatedPersonalMessage or chargingPriority
|
|-- eventType = Ended?
|-- Calculate totalCost (optional)
|-- idToken present?
| |-- Validate token
| |-- Return idTokenInfo with status
|-- Return { totalCost: X.XX }
|-- NOTE: totalCost omitted = NOT free; totalCost: 0.00 = freeAppendix B: Transaction State Machine (CSMS Perspective)
TransactionEventRequest
(eventType=Started)
|
v
+-----------+
| ACTIVE |
+-----------+
| |
Updated | | Updated
(Charging) | | (SuspendedEV/EVSE)
| | | |
v | | v
+-----------+ | | +-----------+
| CHARGING |<--+ +-->| SUSPENDED |
+-----------+ +-----------+
| |
| TransactionEventRequest |
| (eventType=Ended) |
| | |
v v v
+-----------------------------------+
| COMPLETED |
+-----------------------------------+
|
v
Verify seqNo completeness
Calculate totalCost
Finalize billingAppendix D: Common CSMS Error Scenarios
1. Duplicate TransactionEventRequest
Cause
CS retried because CSMS response was lost.
Solution
Use transactionId + seqNo as an idempotency key. Respond normally, don't double-process.
2. Out-of-order Messages
Cause
Messages queued offline arrive in unexpected order.
Solution
Use seqNo and timestamp to reconstruct ordering. Process in seqNo order.
3. Missing seqNo Gap
Cause
Messages lost in transit.
Solution
Use GetTransactionStatusRequest to check for pending messages. If messagesInQueue=false and gaps exist, the messages were truly lost.
4. Transaction Started Without IdToken
Cause
TxStartPoint is EVConnected or ParkingBayOccupancy — transaction starts before authentication.
Solution
Normal behavior. IdToken arrives in a subsequent Updated message. Do not reject the transaction.
5. Unknown TransactionId
Cause
CS generated the ID. CSMS has no prior knowledge of it.
Solution
Always accept and store. The CS is the source of truth for transaction IDs.