Most OCPP writing treats each message in isolation: here is BootNotification, here is MeterValues, here is StopTransaction. That is useful as reference, but it hides the thing that actually matters when you are debugging a live charger, which is the order. A charging session is not one message. It is an ordered conversation of roughly a dozen messages between the charge point and the CSMS, and when a session misbehaves the fault is almost always in the sequence: a message that arrived out of order, never arrived, or arrived before the one it depends on.

This article walks the entire flow start to finish, in order, for both OCPP 1.6 and 2.0.1. Once you have the sequence in your head, try the interactive OCPP simulator and replay it live. You can watch each message leave the charge point, see the CSMS response, and step through a full session at your own pace. If the protocol itself is new to you, what is OCPP sets up the vocabulary this article assumes.
The two layers: connection and session
Before any energy flows, the charger and CSMS establish a working connection. That is a distinct phase from the session itself, and separating the two in your mind clears up most of the confusion:
- Connection layer. Everything that keeps the charger online and reporting:
BootNotification,StatusNotification,Heartbeat. These happen at startup and then continue in the background for the life of the connection. - Session layer. Everything bracketed by a single charging transaction:
Authorize,StartTransaction,MeterValues,StopTransaction, or their 2.0.1 equivalents. These repeat for every car that plugs in.
A charger can sit connected and idle for hours, sending nothing but Heartbeat and the occasional StatusNotification, without a single session. The session messages only appear when someone actually charges.
The full OCPP 1.6 sequence
Here is a complete session from cold boot to unplug. Because chargers speak the version they were commissioned against, and 1.6 hardware has been going into the ground since 2015, this is the flow you end up debugging most often in the field.
sequenceDiagram
participant CP as Charge Point
participant CS as CSMS
CP->>CS: BootNotification
CS-->>CP: Accepted (interval, currentTime)
CP->>CS: StatusNotification (Available)
loop Every interval
CP->>CS: Heartbeat
CS-->>CP: currentTime
end
Note over CP: User taps RFID card
CP->>CS: Authorize (idTag)
CS-->>CP: Accepted
CP->>CS: StatusNotification (Preparing)
CP->>CS: StartTransaction (meterStart, idTag)
CS-->>CP: transactionId, Accepted
CP->>CS: StatusNotification (Charging)
loop During session
CP->>CS: MeterValues (energy, power...)
end
Note over CP: User unplugs
CP->>CS: StopTransaction (meterStop, reason)
CS-->>CP: Accepted
CP->>CS: StatusNotification (Available)
Step by step:
BootNotification. The charger announces itself with vendor, model, and firmware. The CSMS repliesAcceptedalong with acurrentTime, which the charger uses to sync its clock, and aninterval, which sets the Heartbeat cadence. Nothing else can happen until this returnsAccepted. The full field breakdown lives in the BootNotification message article.StatusNotification (Available). The charger reports each connector’s state. Until the CSMS sees a connector asAvailable, it will not let a session start on it. Missing this message is a classic reason a healthy-looking charger refuses to serve cars. The StatusNotification deep dive covers the full state machine.Heartbeat. Sent at the interval from the boot response, this is the keep-alive that tells the CSMS the charger is still there. It runs continuously in the background for the rest of the session and beyond. See Heartbeat and the connection lifecycle for what happens when it stops.Authorize. When the user presents a credential, the charger asks the CSMS whether theidTagis valid. The CSMS may check its own cache or query an eMSP over OCPI before answeringAcceptedorInvalid.StatusNotification (Preparing). The connector moves out ofAvailableas the vehicle prepares to charge.StartTransaction. The pivotal session message. It carriesmeterStart, the meter reading at the beginning, plus theconnectorIdand theidTag. The CSMS responds with atransactionIdthat identifies this session everywhere downstream. The mechanics of this and its closing partner are covered in StartTransaction and StopTransaction.StatusNotification (Charging). The connector confirms energy is flowing.MeterValues. Periodic readings of energy, power, voltage, current, and whatever else the charger samples. These are what your billing and dashboards consume. The details, and the wide variation across OEMs, are in the MeterValues message guide.StopTransaction. When the user unplugs or the session ends, the charger sendsmeterStop, the final reading, and areasonsuch asLocal,Remote, orEVDisconnected. Session energy ismeterStopminusmeterStart.StatusNotification (Available). The connector returns toAvailableand the cycle can begin again.
Notice how StatusNotification threads through the whole sequence. It is not a one-time message. It fires on every state change, and it is your single best signal for what the connector is actually doing at any moment.
Where remote-start changes the order
Not every session begins with a card tap. When a driver starts charging from a mobile app, the CSMS is the initiator:
- The CSMS sends
RemoteStartTransactionwith theidTagalready inside it. - The charger accepts, and because the credential is already supplied, it can skip a separate
Authorizeround trip. - From there the flow rejoins the normal path:
StartTransaction,MeterValues,StopTransaction.
This is why “does a session always start with Authorize?” has no single answer. It depends on how the session was initiated. Remote start, cached local authorization, and plug-and-charge each shortcut or relocate the authorization step. The RemoteStart and RemoteStop article covers the app-initiated path in full.
The OCPP 2.0.1 sequence
The connection layer in 2.0.1 looks almost identical: BootNotification, StatusNotification, Heartbeat. The session layer is where 2.0.1 makes its big simplification. Instead of three distinct messages, everything transaction-related flows through a single TransactionEvent message with an eventType field:
TransactionEvent (Started)replacesStartTransaction.TransactionEvent (Updated)replaces the stream ofMeterValues, and the meter readings now ride inside the event.TransactionEvent (Ended)replacesStopTransaction.
sequenceDiagram
participant CP as Charge Point
participant CS as CSMS
CP->>CS: BootNotification
CS-->>CP: Accepted
CP->>CS: StatusNotification (Available)
CP->>CS: Authorize (optional)
CS-->>CP: Accepted
CP->>CS: TransactionEvent (Started)
CS-->>CP: idTokenInfo
loop During session
CP->>CS: TransactionEvent (Updated, meterValue)
end
CP->>CS: TransactionEvent (Ended, meterValue, reason)
CS-->>CP: Accepted
The payoff is that a single, well-defined message envelope carries the entire life of a transaction, with each event tied together by a shared transactionId and an incrementing seqNo so the CSMS can detect gaps or reordering. The trade-offs, the sequencing rules, and the offline behaviour are covered in the TransactionEvent in OCPP 2.0.1 deep dive, and the full version comparison covers what else changed between 1.6 and 2.0.1 beyond the transaction model.
The important mental shift: in 1.6 you reason about three message types, and in 2.0.1 you reason about three event types of one message. The information carried is broadly the same. What changed is the envelope and the discipline around ordering.
What actually goes wrong in the sequence
Once you see a session as an ordered flow, the common failure modes name themselves:
- Boot accepted but no sessions. The charger connected and heartbeats fine, but never sent
StatusNotification (Available)after boot, so the CSMS treats every connector as unknown and refuses to start. - StartTransaction with no matching Authorize. In deployments that require pre-authorization, a session that skips the auth step gets rejected downstream even though the transaction opened locally.
- MeterValues before StartTransaction. A reading arrives referencing a
transactionIdthe CSMS has not created yet, usually because messages were queued offline and replayed out of order. - Missing StopTransaction. The charger lost connectivity mid-session and never sent the close, leaving a stuck open transaction that has to be reconciled or force-closed.
- StatusNotification lag. The connector physically changed state but the notification was delayed, so the dashboard shows
Chargingon a connector that is actually free.
Every one of these is a sequencing problem rather than a single-message problem. You cannot diagnose them by reading the spec for one message. You have to watch the whole conversation.
See the whole flow live
Reading the sequence is one thing and watching it unfold is another. The interactive OCPP simulator lets you replay a full session message by message, covering boot, status, authorize, transaction start, the MeterValues stream, and the close, for both 1.6 and 2.0.1.
Step through it once end to end and the ordering stops being a diagram you memorize and becomes something you have actually watched happen.
If you want to drive the same sequence yourself rather than read it, the OCPP simulator guide walks through doing exactly that. If you are still building the foundations, the individual message guides linked above go one level deeper on each step. But keep coming back to the sequence, because in production the order is the thing that breaks.