OCPP 1.6J Protocol Reference
The complete technical reference for the Open Charge Point Protocol, version 1.6J. All 28 messages documented from the official Open Charge Alliance specification.
What is OCPP?
The Open Charge Point Protocol (OCPP) is a free, open communication standard maintained by the Open Charge Alliance (OCA). It defines how EV chargers (Charge Points) talk to management software (a Central System) over a network connection.
OCPP 1.6J uses WebSocket with JSON payloads. A charger connects to a Central System URL, and both sides exchange structured messages: the charger reports its status, meter readings, and transaction events, while the Central System sends commands like starting sessions, updating firmware, or adjusting charging profiles.
Because OCPP is an open standard, any charger that supports it can connect to any compatible Central System. This prevents vendor lock-in and gives operators the freedom to mix hardware from different manufacturers under a single management platform.
Looking to switch your OCPP backend or run Plugchoice alongside an existing one? The Plugchoice OCPP proxy handles backend migrations and layered setups without disconnecting chargers.
Four things people mean by OCPP
OCPP? Plugchoice
Plugchoice is a free charger management platform that acts as an OCPP Central System. Connect any OCPP-compatible charger and manage it through our web portal, mobile app, or REST API.
We support the full OCPP 1.6J specification, including Smart Charging profiles for load balancing and solar integration. Hardware-agnostic, no lock-in, no subscription fees for core features.
Plugchoice also acts as an OCPP Proxy, so you can run it alongside any existing billing or management platform without migration or charger reconfiguration.
Protocol Reference
All 28 OCPP 1.6J messages organized by feature profile. Each message shows direction, required and optional fields, types, and valid enum values.
Core
Required. Fundamental charge point operations: authorization, transactions, status reporting, configuration, and basic remote control.
Firmware Management
Firmware updates and diagnostics upload for remote maintenance of charge points.
Local Auth List
Manage a local authorization cache on the charge point for offline authentication.
Reservation
Reserve a connector for a specific user ahead of time.
Smart Charging
Set and manage charging profiles for load balancing, solar integration, and scheduled charging.
Remote Trigger
Request the charge point to send a specific message on demand.
Configuration keys
ChangeConfiguration sets one key at a time on a charger, GetConfiguration reads them back. This is where most real-world charger behaviour is decided: how often meter values arrive, what is in them, whether a session survives a network outage, how a cable unlocks. Below are the standard OCPP 1.6 keys, what each one actually does, and a deep dive on the one that causes the most trouble.
How the call behaves
- Every value is sent as a string, including numbers and booleans: "60", "true". Sending a JSON number is a common reason for a Rejected.
- Key names are case sensitive and must match the spec exactly. MeterValuesSampledData works, metervaluessampleddata does not.
- Many keys only take effect after a restart. The charger tells you by answering RebootRequired, and it is on you to send a Reset.
- Manufacturers add their own keys beyond this list. GetConfiguration with an empty key list returns everything a charger supports, including those, plus an unknownKey array for anything it does not recognise.
What the four responses mean
- Accepted
- the key is set and active now.
- Rejected
- the key exists but the value was refused: out of range, wrong format, or read-only on this charger.
- RebootRequired
- the value is stored but does not apply until the charger restarts.
- NotSupported
- the charger does not implement this key at all.
Standard keys
Core
| Key | Type | Access | What it does |
|---|---|---|---|
| AllowOfflineTxForUnknownIdoptional in spec | boolean | RW | Whether the charger starts a transaction for an unknown idTag while it is offline.True keeps a site charging through a network outage, at the cost of sessions you may not be able to bill to anyone. |
| AuthorizationCacheEnabledoptional in spec | boolean | RW | Whether the charger caches the outcome of earlier Authorize calls.Different from the Local Auth List: the cache fills itself from real authorizations, the list is pushed by the backend. |
| AuthorizeRemoteTxRequests | boolean | R | Whether the charger authorizes the idTag in a RemoteStartTransaction before starting.If true, a remote start from the app still needs an Authorize round trip, so a bad idTag fails the start. |
| BlinkRepeatoptional in spec | integer (times) | RW | How often the charger blinks the lamp during a blink sequence. |
| ClockAlignedDataInterval | integer (seconds) | RW | Interval for clock-aligned meter values, measured from midnight. 0 disables them.900 gives a reading on every quarter hour. This is the one that feeds MeterValuesAlignedData, and it runs whether or not a car is plugged in. |
| ConnectionTimeOut | integer (seconds) | RW | How long the charger waits for the driver to plug in after authorizing before it releases the connector. |
| ConnectorPhaseRotation | CSL | RW | Phase rotation per connector, as connectorId.RST notation, for example 0.RST, 1.RST, 2.RTS.Wrong here means wrong phase-level meter values and wrong single-phase load balancing. Worth checking on any three-phase site. |
| ConnectorPhaseRotationMaxLengthoptional in spec | integer | R | Maximum number of items in ConnectorPhaseRotation. |
| GetConfigurationMaxKeys | integer | R | Maximum number of keys a single GetConfiguration request may ask for.Ask for more than this and the charger may answer with an error. Send GetConfiguration with no keys at all to get everything it supports. |
| HeartbeatInterval | integer (seconds) | RW | How often the charger sends a Heartbeat when nothing else is happening.The BootNotification response can override this. On a metered SIM, a short heartbeat is the quiet data cost that adds up. |
| LightIntensityoptional in spec | integer (%) | RW | Brightness of the charger's lighting, as a percentage. |
| LocalAuthorizeOffline | boolean | RW | Whether the charger authorizes from its local list or cache while offline. |
| LocalPreAuthorize | boolean | RW | Whether the charger starts a transaction from the local list or cache while online, without waiting for the backend.Cuts the wait at the charger, at the cost of the backend not having the final say on every start. |
| MaxEnergyOnInvalidIdoptional in spec | integer (Wh) | RW | Energy the charger still delivers after an authorization turns out to be invalid. |
| MeterValuesAlignedData | CSL | RW | Measurands sent in the clock-aligned MeterValues, on the ClockAlignedDataInterval.Same measurand vocabulary as MeterValuesSampledData, different trigger: the clock, not the session. |
| MeterValuesAlignedDataMaxLengthoptional in spec | integer | R | Maximum number of measurands in MeterValuesAlignedData. |
| MeterValuesSampledData | CSL | RW | Measurands sampled during a transaction and sent in MeterValues, on the MeterValueSampleInterval.The single most useful key on this page. See the deep dive below. |
| MeterValuesSampledDataMaxLengthoptional in spec | integer | R | Maximum number of measurands in MeterValuesSampledData.Read this before you build a long list: exceed it and the charger rejects the whole thing. |
| MeterValueSampleInterval | integer (seconds) | RW | How often, during a transaction, the charger samples MeterValuesSampledData. 0 disables sampling.60 is a sane default. Below 30, with a long measurand list, you are generating a lot of traffic for very little extra insight. |
| MinimumStatusDurationoptional in spec | integer (seconds) | RW | Minimum time the charger stays in a status before it reports the next one.Useful against status flapping on chargers that bounce between Preparing and Available. |
| NumberOfConnectors | integer | R | Number of physical connectors on this charger. |
| ResetRetries | integer (times) | RW | How often the charger retries a failed reset. |
| StopTransactionOnEVSideDisconnect | boolean | RW | Whether unplugging at the car side stops the running transaction.False means the session stays open after the cable is pulled, which is how you end up with sessions that never end. |
| StopTransactionOnInvalidId | boolean | RW | Whether the charger stops the transaction when the backend rejects the idTag. |
| StopTxnAlignedData | CSL | RW | Clock-aligned measurands added to the transactionData of StopTransaction. |
| StopTxnAlignedDataMaxLengthoptional in spec | integer | R | Maximum number of measurands in StopTxnAlignedData. |
| StopTxnSampledData | CSL | RW | Sampled measurands added to the transactionData of StopTransaction.Keep this short. Everything in it is repeated for every sample of the whole session inside one StopTransaction payload, which is how chargers produce megabyte stop messages that backends reject. |
| StopTxnSampledDataMaxLengthoptional in spec | integer | R | Maximum number of measurands in StopTxnSampledData. |
| SupportedFeatureProfiles | CSL | R | Feature profiles this charger implements, for example Core, FirmwareManagement, SmartCharging.The honest answer to what a charger can do. Read it before you promise a customer smart charging. |
| SupportedFeatureProfilesMaxLengthoptional in spec | integer | R | Maximum number of items in SupportedFeatureProfiles. |
| TransactionMessageAttempts | integer (times) | RW | How often the charger retries sending a transaction message the backend did not acknowledge. |
| TransactionMessageRetryInterval | integer (seconds) | RW | Base wait between those retries. The charger multiplies it by the attempt number. |
| UnlockConnectorOnEVSideDisconnect | boolean | RW | Whether the cable unlocks at the charger when it is unplugged at the car. |
| WebSocketPingIntervaloptional in spec | integer (seconds) | RW | Interval for WebSocket ping frames on the OCPP-J connection.The quiet fix for chargers that keep dropping offline behind a NAT or firewall that closes idle connections. |
Local Auth List Management
| Key | Type | Access | What it does |
|---|---|---|---|
| LocalAuthListEnabled | boolean | RW | Whether the local authorization list is in use. |
| LocalAuthListMaxLength | integer | R | How many entries the local authorization list holds. |
| SendLocalListMaxLength | integer | R | How many entries fit in a single SendLocalList message.A list bigger than this has to be sent in several differential updates. |
Reservation
| Key | Type | Access | What it does |
|---|---|---|---|
| ReserveConnectorZeroSupportedoptional in spec | boolean | R | Whether the charger accepts a reservation on connector 0, meaning any connector. |
Smart Charging
| Key | Type | Access | What it does |
|---|---|---|---|
| ChargeProfileMaxStackLevel | integer | R | Highest stack level the charger accepts on a charging profile. Higher levels win. |
| ChargingScheduleAllowedChargingRateUnit | CSL | R | Which units the charger accepts in a charging schedule: Current (A), Power (W), or both.Send a profile in watts to a charger that only takes amps and it is rejected. This key tells you which to use. |
| ChargingScheduleMaxPeriods | integer | R | Maximum number of periods in one charging schedule. |
| ConnectorSwitch3to1PhaseSupportedoptional in spec | boolean | R | Whether the charger can switch between three-phase and single-phase charging.Decides whether you can throttle below the three-phase minimum instead of pausing altogether. |
| MaxChargingProfilesInstalled | integer | R | How many charging profiles can be installed at once. |
MeterValuesSampledData
This one key decides what telemetry you get out of a charging session. It is a comma separated list of measurands, and the charger samples every item on it at the interval set by MeterValueSampleInterval, then sends them in a MeterValues message. Leave it at the default and you get one cumulative energy reading, enough to bill, and nothing else: no power curve, no per-phase current, no voltage, no way to see whether a smart charging limit landed.
It never works alone. MeterValueSampleInterval sets how often it fires, MeterValuesSampledDataMaxLength caps how many measurands you may list, StopTxnSampledData decides which of them get repeated inside StopTransaction, and MeterValuesAlignedData is the same vocabulary on a clock schedule instead of a session.
The measurand vocabulary
Any of these may appear in the list. Which ones a charger actually supports is hardware dependent: an AC wallbox will not report SoC, and a basic charger may only have the energy register.
| Measurand | Unit | Typically on | What it gives you |
|---|---|---|---|
| Energy.Active.Import.Registerbilling | Wh | AC and DC | Cumulative energy drawn from the grid, read straight off the meter register. The default when MeterValuesSampledData is empty, and the value nearly every billing calculation is built on. |
| Energy.Active.Export.Register | Wh | V2G capable | Cumulative energy fed back, for bidirectional setups. |
| Energy.Active.Import.Interval | Wh | AC and DC | Energy imported since the previous sample, rather than a running total. |
| Energy.Reactive.Import.Register | varh | Meter dependent | Cumulative reactive energy imported. |
| Power.Active.Import | W | AC and DC | Instantaneous active power. What you plot to see a charging curve, and what load management reacts to. |
| Power.Offered | W | AC and DC | Power the charger is currently offering the car. Compare it with Power.Active.Import to see whether a limit is actually being applied. |
| Power.Reactive.Import | var | Meter dependent | Instantaneous reactive power. |
| Power.Factor | ratio | Meter dependent | Ratio of active to apparent power. |
| Current.Import | A | AC and DC | Current drawn. Usually reported per phase, so this one measurand can become three values per sample. |
| Current.Offered | A | AC and DC | Current limit the charger is signalling to the car. The direct read-out of whether your smart charging profile landed. |
| Current.Export | A | V2G capable | Current fed back, for bidirectional setups. |
| Voltage | V | AC and DC | Supply voltage, per phase on three-phase chargers. Useful for spotting a dropped phase or a weak connection. |
| Frequency | Hz | Meter dependent | Grid frequency. Note that the spec gives it no unit field. |
| Temperature | Celsius | Charger dependent | Temperature at the charger or connector. Explains derating on a hot day before anyone calls it a fault. |
| SoC | % | DC, or ISO 15118 on AC | State of charge of the car's battery. Only present when the car actually reports it, which on plain AC charging it usually does not. |
| RPM | rpm | Rare | Fan speed, on chargers that report it. |
Lists that work
Copy one of these into the value of a ChangeConfiguration call. Start narrow and add measurands once you have confirmed the charger accepts them.
Billing only
The minimum that still produces a correct invoice. The default on most chargers.
Energy.Active.Import.RegisterStandard telemetry
Sensible default for a managed AC site: a power curve, per-phase current and voltage for diagnosis.
Energy.Active.Import.Register,Power.Active.Import,Current.Import,VoltageSmart charging
Adds the offered values, so you can see whether a profile is actually being applied rather than assuming.
Energy.Active.Import.Register,Power.Active.Import,Power.Offered,Current.Import,Current.Offered,VoltageDC fast charging
Adds state of charge and temperature, which DC chargers do report and which explain most derating.
Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage,SoC,TemperatureWhat goes wrong
One bad measurand rejects the whole list
Chargers validate the list as a unit. If a single item is unsupported you get Rejected or NotSupported for everything, with no indication which one failed. Set the list one measurand at a time to find the offender, or read MeterValuesSampledDataMaxLength and the vendor documentation first.
One measurand can be three values
Current.Import and Voltage are reported per phase, so on a three-phase charger they arrive as three sampledValue entries each, tagged L1, L2, L3 or L1-N and so on. A list of six measurands can easily be a dozen values per sample. Payload size grows faster than the list length suggests.
Wh against kWh
The energy register is Wh by default, but the unit field can say kWh and some firmware does exactly that. Read the unit rather than assuming it, or you get an invoice that is off by a factor of a thousand.
A short interval is not free
MeterValueSampleInterval at 10 seconds with a long list means hundreds of messages per session. On a metered SIM that is real money, and on a large site it is real backend load. 60 seconds is a good default, 300 for sites where you only need the shape of the curve.
StopTxnSampledData is the payload trap
Everything listed there is repeated, for every sample of the entire session, inside a single StopTransaction message. A long list on a long session produces a payload big enough for backends to reject or truncate, and the transaction then fails to close cleanly. Keep it to the energy register unless you have a specific reason.
Drop the energy register and billing breaks
If Energy.Active.Import.Register is not in the list, the periodic meter values carry no cumulative total. Most backends then fall back to meterStart and meterStop from the transaction messages, and any session where StopTransaction goes missing becomes unbillable. Keep it in the list, always.
SoC is usually not available on AC
State of charge comes from the car, over ISO 15118 or the DC charging protocol. On a plain AC wallbox without Plug and Charge the car does not report it, so requesting SoC either gets rejected or returns nothing useful.
Read the context field
Each sampledValue carries a context: Sample.Periodic, Transaction.Begin, Transaction.End, Sample.Clock or Trigger. A value at Transaction.Begin is a starting register, not consumption. Treating every sample as periodic is how sessions end up double counted.
A full round tripSetting the list, and the MeterValues that follow on a three-phase charger.
// Set the list
[2,"19223201","ChangeConfiguration",{
"key": "MeterValuesSampledData",
"value": "Energy.Active.Import.Register,Power.Active.Import,Current.Import,Voltage"
}]
[3,"19223201",{"status":"RebootRequired"}]
// What the charger then sends, every MeterValueSampleInterval
[2,"19223202","MeterValues",{
"connectorId": 1,
"transactionId": 8123,
"meterValue": [{
"timestamp": "2026-07-23T11:04:00Z",
"sampledValue": [
{"value":"14230","measurand":"Energy.Active.Import.Register",
"unit":"Wh","context":"Sample.Periodic"},
{"value":"7360","measurand":"Power.Active.Import","unit":"W"},
{"value":"10.7","measurand":"Current.Import","unit":"A","phase":"L1"},
{"value":"10.6","measurand":"Current.Import","unit":"A","phase":"L2"},
{"value":"10.8","measurand":"Current.Import","unit":"A","phase":"L3"},
{"value":"231.2","measurand":"Voltage","unit":"V","phase":"L1-N"}
]
}]
}]This page for AI assistants
The whole reference, every message and every configuration key, as one plain-text file an LLM can read in a single fetch.
plugchoice.com/help/ocpp/llms.txt