Locations is the module everyone needs first. It tells eMSPs, navigation apps and hubs where your chargers are, what connectors they have, which tariffs apply and whether they are free right now. It is also the module with the highest message volume, because every status change on every EVSE is a potential update.
This is part 4 of the OCPI 2.2.1 series. Transport conventions such as pagination and last_updated are explained in Part 3.
The three-level model
OCPI models a site as a hierarchy:
- Location: a site with an address and coordinates, such as a car park or a forecourt. Owns operator details, opening times, facilities and time zone.
- EVSE: one point that can charge one vehicle at a time. This is the level status lives at.
- Connector: a physical plug or socket on an EVSE. An EVSE with a CCS cable and a CHAdeMO cable has two connectors, but can still only charge one car at a time.
This maps reasonably well to OCPP 2.0.1's Charging Station / EVSE / Connector model and less neatly to OCPP 1.6, where a "connector" is often what OCPI would call an EVSE. Part 10 covers that mapping in detail.
A complete example
{
"country_code": "DE",
"party_id": "CPX",
"id": "LOC-0042",
"publish": true,
"name": "Riverside Car Park",
"address": "Hafenstrasse 12",
"city": "Hamburg",
"postal_code": "20457",
"country": "DEU",
"coordinates": { "latitude": "53.543210", "longitude": "9.987650" },
"parking_type": "PARKING_LOT",
"operator": { "name": "Example CPO" },
"time_zone": "Europe/Berlin",
"opening_times": { "twentyfourseven": true },
"evses": [
{
"uid": "3256",
"evse_id": "DE*CPX*E0042*1",
"status": "AVAILABLE",
"capabilities": ["RFID_READER", "REMOTE_START_STOP_CAPABLE", "UNLOCK_CAPABLE"],
"connectors": [
{
"id": "1",
"standard": "IEC_62196_T2_COMBO",
"format": "CABLE",
"power_type": "DC",
"max_voltage": 920,
"max_amperage": 200,
"max_electric_power": 150000,
"tariff_ids": ["DC-STANDARD"],
"last_updated": "2026-10-26T08:00:00Z"
}
],
"physical_reference": "1",
"last_updated": "2026-10-26T08:00:00Z"
}
],
"last_updated": "2026-10-26T08:00:00Z"
}Some details worth noticing:
countryon the Location is ISO 3166-1 alpha-3 (DEU), whilecountry_codeidentifying the party is alpha-2 (DE). Mixing them up is a common validation failure.coordinatesare strings, not numbers, with a decimal point. Some receivers validate the format strictly.time_zoneis required in 2.2.1 and must be an IANA name such asEurope/Berlin. Tariff restrictions and opening times are interpreted in it.max_electric_poweris in watts.max_voltageandmax_amperagedescribe the connector's limits; for AC, the voltage is line-to-neutral.publish: falsehides the location from general listing;publish_allowed_tocan then list the tokens or visual numbers that may see it (useful for private or fleet-only sites).
uid vs evse_id
Each EVSE has two identifiers, and they serve different purposes. uid is the CPO's internal, stable identifier, unique within the CPO; it is what appears in URLs, sessions, CDRs and commands. evse_id is the public, human-facing identifier in the eMI3 format (<country>*<operator ID>*E<identifier>, e.g. DE*CPX*E0042*1) printed on the charger and used by drivers and roaming partners for lookup. evse_id is optional in the object, but in roaming you should treat it as required: many eMSPs and hubs refuse or hide EVSEs without one, and in several countries operator IDs are issued by a national registry.
Enumerations you will use constantly
| Field | Values |
|---|---|
EVSE status | AVAILABLE, BLOCKED, CHARGING, INOPERATIVE, OUTOFORDER, PLANNED, REMOVED, RESERVED, UNKNOWN |
Connector format | SOCKET, CABLE |
Connector power_type | AC_1_PHASE, AC_2_PHASE, AC_2_PHASE_SPLIT, AC_3_PHASE, DC |
Connector standard (common) | IEC_62196_T1, IEC_62196_T1_COMBO, IEC_62196_T2, IEC_62196_T2_COMBO, CHADEMO, GBT_DC, plus domestic and other types |
parking_type | ALONG_MOTORWAY, PARKING_GARAGE, PARKING_LOT, ON_DRIVEWAY, ON_STREET, UNDERGROUND_GARAGE |
A few status semantics trip people up. BLOCKED means the EVSE cannot be used because something is physically in the way (a non-EV parked in the bay, for example), not that it is in use. INOPERATIVE means temporarily unavailable but not broken (maintenance, administratively disabled), while OUTOFORDER means broken. REMOVED is how you delete an EVSE: OCPI has no DELETE for locations, so you set the status to REMOVED and keep the object. For a broader look at connector standards, see EV charging connector types.
CPO pushing to the eMSP
The eMSP implements the Locations Receiver interface. The CPO sends a full object with PUT when a location is created or substantially changed:
PUT /ocpi/emsp/2.2.1/locations/DE/CPX/LOC-0042 HTTP/1.1
Host: emsp.example.com
Authorization: Token ZWJmM2IzOTktNzc5Zi00NDk3LTliOWQtYWM2YWQzY2M0NGQy
Content-Type: application/json
X-Request-ID: 2b8f1a7c-5d4e-4c3b-9a8f-7e6d5c4b3a21
X-Correlation-ID: 2b8f1a7c-5d4e-4c3b-9a8f-7e6d5c4b3a21For status changes, PATCH the EVSE with just the fields that changed, always including last_updated:
PATCH /ocpi/emsp/2.2.1/locations/DE/CPX/LOC-0042/3256 HTTP/1.1
Host: emsp.example.com
Authorization: Token ZWJmM2IzOTktNzc5Zi00NDk3LTliOWQtYWM2YWQzY2M0NGQy
Content-Type: application/json
X-Request-ID: 8d7c6b5a-4f3e-4d2c-8b1a-0f9e8d7c6b5a
X-Correlation-ID: 8d7c6b5a-4f3e-4d2c-8b1a-0f9e8d7c6b5a{
"status": "CHARGING",
"last_updated": "2026-10-26T10:15:04Z"
}The same URL pattern works one level down for connectors (.../LOC-0042/3256/1) and one level up for the location itself. PUT on an EVSE URL replaces that EVSE, including its connectors; PUT on a location replaces everything below it.
Ordering and stale updates
Pushes can arrive out of order, especially behind retries or hubs. A receiver should compare the incoming last_updated with what it has stored and ignore older updates. A CPO should not let a slow retry of an old CHARGING patch overwrite a newer AVAILABLE one. This is the main reason last_updated is mandatory on every PATCH.
PATCH on an unknown object
If the eMSP receives a PATCH for an EVSE or location it does not have, for example because the original PUT failed, it cannot apply it. The robust CPO behaviour is to respond to that error by sending a full PUT of the location. The robust eMSP behaviour is to schedule a GET of that location from the CPO's Sender interface.
eMSP pulling from the CPO
The CPO's Sender interface offers a paginated list and single-object lookups:
GET /ocpi/cpo/2.2.1/locations?date_from=2026-10-26T09:00:00Z&limit=100 HTTP/1.1
Host: cpo.example.com
Authorization: Token OWU4MGE1OWMtNmEyZi00YjVkLThlMWMtMmYzYTRiNWM2ZDdl
X-Request-ID: 1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6b
X-Correlation-ID: 1e2d3c4b-5a69-4788-9a0b-1c2d3e4f5a6bSingle objects are fetched with GET {locations_url}/{location_id}, .../{location_id}/{evse_uid} and .../{location_id}/{evse_uid}/{connector_id}. Note that the Sender interface URLs do not include country_code/party_id: the CPO already knows they are its own.
A typical eMSP strategy is a full pull on first connection, an incremental pull with date_from every few minutes as a safety net, and pushes for real-time status. Do not rely on pulls alone for status; a charger can go from available to occupied and back between two polls.
Other fields that matter in practice
opening_times: eithertwentyfourseven: trueorregular_hoursper weekday, withexceptional_openingsandexceptional_closings.charging_when_closedsays whether a session can continue after closing time.status_scheduleon the EVSE: planned future status changes, e.g. scheduled maintenance.capabilities: drives what eMSPs offer in their apps. If you do not listREMOTE_START_STOP_CAPABLE, expect eMSPs not to offer app start; ifSTART_SESSION_CONNECTOR_REQUIREDis listed, the eMSP must specify a connector inSTART_SESSION(see Part 8).tariff_idson the connector: links to Tariff objects from Part 7. Leaving it empty while publishing tariffs makes prices impossible to display.directions,images,facilities,energy_mix: optional, but navigation partners use them.
Common mistakes
- Using the database primary key as
uidand later changing it during a migration.uidmust be stable forever; sessions and CDRs refer to it. - Publishing EVSEs that are installed but not commissioned as
AVAILABLEinstead ofPLANNED. - Flooding partners with
PATCHcalls for meter-level noise. Push status changes, not every OCPP message. Debounce rapid flapping (a connector reportingPreparing/Availablerepeatedly) before it reaches OCPI. - Sending
UNKNOWNfor every EVSE whose charger is offline for a few seconds. A short grace period before switching toUNKNOWNreduces noise considerably.
Key takeaways
- Location, EVSE and Connector form a strict hierarchy; status lives on the EVSE.
uidis the stable internal key used everywhere else;evse_idis the public eMI3 ID. Publish both.- Push with
PUTfor full objects andPATCHfor status, always withlast_updated; never delete, useREMOVED. - Receivers discard older updates, and eMSPs back pushes up with incremental pulls.

