PHP2Shell alert: critical PHP flaws CVE-2026-109618 / 109619 — check your servers and get patched fast
HashTech Systems
EV Charging
Part 5 of 12

OCPP 1.6 StartTransaction and StopTransaction Deep Dive

OCPP 1.6 transactions in detail: StartTransaction, transactionId, meterStart/meterStop, stop reasons, transactionData and offline message queuing.

HashTech Engineering · October 26, 2026 · 5 min read

Transactions are the part of OCPP that turns into money. Every invoice, every roaming CDR and every energy report depends on StartTransaction and StopTransaction arriving, being matched correctly, and carrying sensible meter readings. This part walks through both messages, the stop reasons, and how transaction messages behave when the charger is offline. It is part 5; part 4 covered authorization.

StartTransaction

json
[
  2,
  "tx-5501",
  "StartTransaction",
  {
    "connectorId": 1,
    "idTag": "04A2B3C4D5E680",
    "meterStart": 1284560,
    "timestamp": "2026-10-26T10:02:11Z"
  }
]
FieldTypeRequiredNotes
connectorIdinteger > 0YesConnector 0 is not valid here
idTagCiString20YesIdentifier that started the transaction
meterStartintegerYesEnergy register value in Wh at start
timestampdateTimeYesWhen the transaction started on the charger
reservationIdintegerNoPresent when the transaction consumes a reservation

meterStart is the absolute register reading of the connector's energy meter in Wh, not zero. Energy delivered is meterStop - meterStart. Do not assume the register starts at zero or increases across meter replacements.

The CSMS responds with a transactionId and the authorization result:

json
[
  3,
  "tx-5501",
  {
    "transactionId": 880213,
    "idTagInfo": { "status": "Accepted" }
  }
]

The transactionId

The transactionId is an integer generated by the CSMS. The charger uses it in every later MeterValues and in StopTransaction for that session, and the CSMS uses it in RemoteStopTransaction. Generate it from a database sequence or similar source that will not repeat. It is an integer in the JSON schema, so keep it within 32-bit signed range: some charger firmware stores it as a 32-bit int, and IDs above 2,147,483,647 will break those chargers in confusing ways.

Always accept the transaction record

The CSMS cannot prevent a transaction by rejecting StartTransaction. By the time it arrives, the charger may already be delivering energy, and if the message was queued offline the session may already be over. The idTagInfo.status in the response tells the charger whether the identifier is valid, and the charger then acts according to its configuration:

  • StopTransactionOnInvalidId true: the charger stops the transaction, and StopTransaction follows with reason DeAuthorized.
  • StopTransactionOnInvalidId false: the transaction stays open but the charger stops delivering energy (MaxEnergyOnInvalidId, where supported, caps how much energy may still be delivered after the identifier is invalidated).

Either way, always return a transactionId. A CSMS that responds with a CALLERROR to an unknown tag leaves the charger retrying the same message, and blocks the transaction queue behind it.

StopTransaction

json
[
  2,
  "tx-5530",
  "StopTransaction",
  {
    "transactionId": 880213,
    "idTag": "04A2B3C4D5E680",
    "meterStop": 1311020,
    "timestamp": "2026-10-26T11:14:52Z",
    "reason": "Local",
    "transactionData": [
      {
        "timestamp": "2026-10-26T10:02:11Z",
        "sampledValue": [
          {
            "value": "1284560",
            "context": "Transaction.Begin",
            "measurand": "Energy.Active.Import.Register",
            "unit": "Wh"
          }
        ]
      },
      {
        "timestamp": "2026-10-26T11:14:52Z",
        "sampledValue": [
          {
            "value": "1311020",
            "context": "Transaction.End",
            "measurand": "Energy.Active.Import.Register",
            "unit": "Wh"
          }
        ]
      }
    ]
  }
]
FieldRequiredNotes
transactionIdYesFrom the StartTransaction response
meterStopYesEnergy register in Wh at stop
timestampYesWhen the transaction stopped
idTagNoIdentifier that stopped it, if one was presented
reasonNoDefaults to Local when omitted
transactionDataNoArray of MeterValue; contents configured by StopTxnSampledData and StopTxnAlignedData

The response contains an optional idTagInfo, relevant when an idTag was included, and is otherwise empty: [3, "tx-5530", {}]. As with start, respond even if the transactionId is unknown to you. Log it, create a reconciliation record, and answer, or the charger will keep retrying.

Stop reasons

reasonWhen it is used
DeAuthorizedThe idTag was found invalid after the transaction started (for example, on the StartTransaction response)
EmergencyStopEmergency stop button pressed
EVDisconnectedCable unplugged from the vehicle
HardResetHard reset command
LocalStopped locally by the user, for example by presenting the card again. Default when reason is omitted
OtherAny other reason
PowerLossComplete loss of power
RebootReboot not caused by a reset command (for example, a watchdog)
RemoteStopped by RemoteStopTransaction
SoftResetSoft reset command
UnlockCommandStopped because of an UnlockConnector command

Reasons are useful for analytics and support. A spike in EVDisconnected without a prior SuspendedEV on a particular model often points at a vehicle compatibility problem, and frequent Reboot stops point at firmware stability. Whether unplugging ends the transaction at all is controlled by StopTransactionOnEVSideDisconnect; if it is false, the session stays open while the cable is out until the user stops it.

Offline queuing and ordering

Transaction-related messages (StartTransaction, StopTransaction and MeterValues that belong to a transaction) are the messages the spec requires the charger to keep and deliver once it reconnects. Other messages, such as StatusNotification, may be discarded or summarised. Queued transaction messages must be delivered in the order they were generated.

If the charger is offline when a session starts, it does not have a transactionId yet. It keeps the session locally, and when it reconnects it sends StartTransaction first, gets the transactionId, and then uses it for the queued MeterValues and StopTransaction. A CSMS can therefore receive a start and a stop seconds apart for a session that took place hours ago. Use the charger's timestamp values for billing, not your receive times.

Delivery failures are governed by two keys: TransactionMessageAttempts (how many times to try) and TransactionMessageRetryInterval (seconds between attempts, with the spec describing an increasing back-off). A transaction message counts as failed if the CSMS returns a CALLERROR or does not answer; after the attempts are exhausted, the charger may drop it. That is another reason never to reject transaction messages with a CALLERROR for business reasons.

Common CSMS mistakes

  • Opening duplicate sessions on retransmitted StartTransaction messages.
  • Rejecting StopTransaction for an unknown transactionId instead of storing it for reconciliation.
  • Calculating energy from MeterValues samples instead of meterStop - meterStart. Samples are useful for graphs and smart charging; the start and stop registers are the billing source. (Part 6 covers meter values.)
  • Using receive time instead of charger timestamps, which makes offline sessions look like they lasted seconds.
  • Leaving sessions open forever when a stop never arrives. Close stale sessions with an explicit status after a period, and keep them correctable if the stop turns up later.

Key takeaways

  • The CSMS issues the transactionId; keep it unique and inside 32-bit range.
  • Always respond to start and stop with a CALLRESULT. Use idTagInfo.status to communicate authorization, not errors.
  • Billing energy is meterStop - meterStart in Wh.
  • Transaction messages are queued offline and delivered in order. Design for late, bunched-up delivery.

Previous: Part 4, Authorize and id tags. Next: Part 6, MeterValues. For how these records become roaming sessions and CDRs, see OCPI sessions and CDRs.

Let's Talk

Ready to build something that lasts?

Whether it's a charging network, an AI rollout, or a core enterprise system — let's talk about what you're building.