Introduction
API guide
  • AuthenticationSoon
  • Your first API callSoon
  • Interactive API referenceSoon
  • Roles and permissionsSoon
  • Errors, pagination and versioningSoon
  • Live updatesSoon
  • Sending commands to a chargerSoon
  • Rate limitsSoon
Troubleshooting
  • My charger will not connectSoon
  • It connects, then dropsSoon
  • A command timed out or returned 409Soon
  • A card is not being authorizedSoon
  • My transaction looks wrongSoon
  • Meter values are missing or sparseSoon
  • Reading the OCPP message logSoon
  • Known limitationsSoon
Reference
  • GlossarySoon
  • Endpoint indexSoon
  • OCPP message supportSoon
  • Status and error codesSoon
  • ChangelogSoon
Flowion Docs

Lifecycle of a charging session

This page follows one charging session from the station being switched on to the driver walking away, showing the OCPP messages at each step and the objects they become in Flowion.

The message names are OCPP 1.6's, because that is the version most fleets still run and the one whose shape is easiest to read. Where 2.x differs materially, it is called out.

Stage 1 — the station comes online#

A station that has just been powered on does three things, in order.

It opens a WebSocket. Outward, to your gateway URL, authenticating with HTTP Basic credentials over TLS. The username is the station's own identity; the password is a key stored on the station. Version negotiation happens in the same handshake — see OCPP in ten minutes.

It sends BootNotification. This is the station describing itself: vendor, model, serial numbers, firmware version, and modem details if it has one.

[2, "1", "BootNotification",
 {"chargePointVendor": "VendorX",
  "chargePointModel": "SingleSocketCharger",
  "firmwareVersion": "1.4.2"}]

It waits. This is the part people miss: between power-on and a successful boot, a station may not send anything else at all — including messages it had queued up from before the reboot. Nothing happens until you answer.

Your answer decides what the station does next:

AnswerWhat the station does
AcceptedAdopts your heartbeat interval, syncs its clock to your currentTime, and starts operating normally.
PendingStays connected and waits. You may query and reconfigure it; it answers, but sends nothing of its own unless you explicitly trigger it. Remote start and stop are not allowed in this state.
RejectedGoes silent. It sends nothing and answers nothing until the retry interval elapses — and may drop the connection entirely.
[3, "1", {"status": "Accepted",
          "currentTime": "2026-08-10T09:12:44Z",
          "interval": 300}]

The interval field means two different things depending on the status. On Accepted it is the heartbeat interval. On anything else it is the minimum time the station must wait before trying to boot again — and 0 means "pick something sensible yourself".

A Rejected station is genuinely unreachable. It is not merely refusing to charge; it is declining to respond to you at all. If you reject a station by mistake, you cannot command your way out of it — you have to wait for its retry. This is the single most expensive misconfiguration available to you.

In Flowion: the boot report is stored on the charger record and the registration status is a decision you own — it is stored, not recomputed. You can see both on the charger's detail page and through the ChargingStations endpoints.

Stage 2 — idling#

Once accepted, the station settles into a quiet loop.

StatusNotification for each connector, reporting Available. Statuses are per connector, and connector 0 means the station as a whole.

Heartbeat every interval seconds. The response carries the current time, which is how a station without a battery-backed clock stays accurate. That matters more than it sounds: a station with the wrong clock writes wrong timestamps into your transaction records, and you will not notice until you try to bill from them.

MeterValues may arrive even with nobody charging — a main-meter reading on connector 0 belongs to the station, not to any session.

Stage 3 — a driver arrives#

The driver presents an RFID card.

StatusNotification: Preparing. The connector is no longer free but no transaction has begun. This is also what you see when someone plugs a cable in before identifying themselves.

Authorize. The station asks whether this credential may charge:

[2, "42", "Authorize", {"idTag": "045C7A2B4F1D80"}]

You answer with a status:

[3, "42", {"idTagInfo": {"status": "Accepted", "expiryDate": "2026-09-01T00:00:00Z"}}]

Accepted, Blocked, Expired, Invalid or ConcurrentTx. Remember that a refusal is still a CallResult — the exchange succeeded, the answer was no.

A station may skip this message. If the credential is in its local authorization list or its cache, it is allowed to decide by itself — which is exactly what keeps a site working when its network is down. You will not always see an Authorize for a session that starts.

Stage 4 — charging begins#

StartTransaction. The station reports that a session has begun:

[2, "43", "StartTransaction",
 {"connectorId": 1,
  "idTag": "045C7A2B4F1D80",
  "meterStart": 1204500,
  "timestamp": "2026-08-10T09:31:02Z"}]

meterStart is the meter's cumulative reading in Wh at the moment of start — not zero, and not the session's consumption.

Your response allocates the transaction id:

[3, "43", {"transactionId": 90210, "idTagInfo": {"status": "Accepted"}}]

Two things about this message are worth knowing.

You must re-check the credential here, even if you already authorized it. The station may have decided locally from a cached entry that has since been blocked. The StartTransaction response is your last chance to say no.

Always answer. If you do not, the station will retry the same message, and you will end up with duplicates. Sanity-check failures on your side are not a reason to withhold the response.

StatusNotification: Charging. The contactor closes and energy flows. Expect brief detours through SuspendedEV or SuspendedEVSE on the way — a car negotiating its charge curve produces these routinely.

Stage 5 — while charging#

MeterValues, periodically, carrying the transaction id:

[2, "44", "MeterValues",
 {"connectorId": 1,
  "transactionId": 90210,
  "meterValue": [{"timestamp": "2026-08-10T09:36:02Z",
                  "sampledValue": [{"value": "1205900",
                                    "measurand": "Energy.Active.Import.Register",
                                    "unit": "Wh"}]}]}]

The station decides when to sample and what to report; you configure that through its settings. Readings can include energy, power, current, voltage, temperature and state of charge.

The transactionId is what ties a reading to a session — it is not inferred from the connector being busy. A MeterValues without one is a standalone reading, typically from the main meter.

Status may change without the session ending. SuspendedEVSE mid-session usually means load management has throttled this connector to zero. The transaction is still open.

Stage 6 — it ends#

The driver presents their card again, or unplugs.

StopTransaction:

[2, "45", "StopTransaction",
 {"transactionId": 90210,
  "meterStop": 1237400,
  "timestamp": "2026-08-10T10:47:15Z",
  "reason": "Local",
  "idTag": "045C7A2B4F1D80"}]

Energy delivered is meterStop - meterStart — 32.9 kWh here.

You cannot refuse this. The specification is explicit: the CSMS cannot prevent a transaction from stopping. Your response acknowledges it and may carry updated credential information, nothing more. By the time this message arrives the physical event has already happened.

An omitted reason means Local — a normal, driver-initiated stop. Anything abnormal should name itself: EmergencyStop, PowerLoss, Reboot, DeAuthorized.

The idTag may be missing when the station stopped the session itself, for instance because you reset it.

StatusNotification: Finishing, then Available once the cable is out. A connector stays unavailable while a cable is still plugged in, which is why a site can look fully occupied with nobody charging.

The same story in OCPP 2.x#

The stages are identical; the messages are consolidated.

1.62.x
StartTransactionTransactionEvent (eventType: Started)
MeterValues during a sessionTransactionEvent (eventType: Updated)
StopTransactionTransactionEvent (eventType: Ended)

One message type instead of three, each carrying a triggerReason saying why it was sent — something 1.6 never tells you.

The transaction id also changes sides. In 1.6 the CSMS allocates an integer and hands it back. In 2.x the station generates a string id and it is present from the first event. Flowion keeps both forms distinctly rather than coercing one into the other; see How Flowion maps onto OCPP.

What you end up with#

Whichever version the station spoke, Flowion stores the same thing: a transaction with an event log, meter readings hanging off those events, and a connector whose status you can see change over time. Nothing above the gateway knows which OCPP version produced it.

Next: Which OCPP version am I dealing with?