OCPP Message Flow: A Full Charging Session, Step by Step

The full OCPP message flow for a charging session, in order: boot, status, authorize, transaction start, MeterValues, stop. Plus a live simulator to replay it.

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.

The OCPP simulator stepping through a full charging session, frame by frame

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:

  1. BootNotification. The charger announces itself with vendor, model, and firmware. The CSMS replies Accepted along with a currentTime, which the charger uses to sync its clock, and an interval, which sets the Heartbeat cadence. Nothing else can happen until this returns Accepted. The full field breakdown lives in the BootNotification message article.
  2. StatusNotification (Available). The charger reports each connector’s state. Until the CSMS sees a connector as Available, 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.
  3. 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.
  4. Authorize. When the user presents a credential, the charger asks the CSMS whether the idTag is valid. The CSMS may check its own cache or query an eMSP over OCPI before answering Accepted or Invalid.
  5. StatusNotification (Preparing). The connector moves out of Available as the vehicle prepares to charge.
  6. StartTransaction. The pivotal session message. It carries meterStart, the meter reading at the beginning, plus the connectorId and the idTag. The CSMS responds with a transactionId that identifies this session everywhere downstream. The mechanics of this and its closing partner are covered in StartTransaction and StopTransaction.
  7. StatusNotification (Charging). The connector confirms energy is flowing.
  8. 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.
  9. StopTransaction. When the user unplugs or the session ends, the charger sends meterStop, the final reading, and a reason such as Local, Remote, or EVDisconnected. Session energy is meterStop minus meterStart.
  10. StatusNotification (Available). The connector returns to Available and 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:

  1. The CSMS sends RemoteStartTransaction with the idTag already inside it.
  2. The charger accepts, and because the credential is already supplied, it can skip a separate Authorize round trip.
  3. 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) replaces StartTransaction.
  • TransactionEvent (Updated) replaces the stream of MeterValues, and the meter readings now ride inside the event.
  • TransactionEvent (Ended) replaces StopTransaction.
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 transactionId the 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 Charging on 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.

Quick check

Q1. Which message must succeed before anything else in an OCPP session can happen?
Q2. In OCPP 2.0.1, what replaces the stream of MeterValues messages during a session?
Q3. A CSMS receives MeterValues referencing a transactionId it has never seen. What is the most likely cause?
Q4. Why might a session legitimately contain no Authorize message at all?

Frequently asked questions

What is the order of OCPP messages in a charging session?

At the connection level: BootNotification, then StatusNotification and Heartbeat. At the session level: Authorize (optional), StartTransaction (1.6) or a TransactionEvent Started (2.0.1), a stream of MeterValues or TransactionEvent Updated messages, then StopTransaction or TransactionEvent Ended. StatusNotification fires whenever the connector changes state throughout.

Does every session start with an Authorize message?

No. Authorize is used when the charger needs the CSMS to validate a credential before energy flows, such as a tapped RFID card. Remote-start sessions carry the idToken in the RemoteStartTransaction request, so a separate Authorize is often skipped. Plug-and-charge and cached local authorization can also bypass an online Authorize round trip.

How is the OCPP 2.0.1 message flow different from 1.6?

The connection setup is nearly identical. The session itself changes: 1.6 uses three separate messages (StartTransaction, MeterValues, StopTransaction), while 2.0.1 folds all of them into one TransactionEvent message sent with eventType Started, Updated, or Ended. Meter readings ride inside those events instead of a standalone message.

Which side sends which messages?

The charge point initiates most session messages: BootNotification, StatusNotification, Authorize, StartTransaction, MeterValues, StopTransaction. The CSMS responds to each and initiates its own commands such as RemoteStartTransaction, RemoteStopTransaction, and SetChargingProfile. OCPP is bidirectional, so both ends can be the requester depending on the message.

Found this useful? Share it.