Local Authorization List Management - CSMS Developer Guide
Based on OCPP 2.1 Edition 2 Specification (Part 2), Section D (Local Authorization List Management). This guide covers all flows for managing the Local Authorization List on Charging Stations, including sending full and differential updates, querying list versions, synchronization strategies, and error handling from the CSMS perspective using OCPP-J (JSON over WebSocket).
1. Overview
IntroductionThe Local Authorization List is a list of identifier tokens (idTokens) that the CSMS pushes to Charging Stations. It enables offline authorization (Charging Stations can authorize users without CSMS connectivity) and reduced latency (when online, local list lookups are faster than round-trip CSMS authorization).
Key Point: The CSMS is the initiator of both operations in this functional block. The Charging Station never sends unsolicited list updates.
Operations Summary
| Operation | Direction | Initiated By | Purpose |
|---|---|---|---|
GetLocalListVersion | CSMS → CS → CSMS | CSMS | Query current list version on Charging Station |
SendLocalList | CSMS → CS → CSMS | CSMS | Push a full or differential list update to Charging Station |
Prerequisites
The Charging Station must have LocalAuthListEnabled configuration variable set to true for the list to
be active. The CSMS can read/write this variable via the GetVariables / SetVariables messages
(Provisioning functional block).
2. Data Types & Schemas
ReferenceSendLocalListRequest Schema
Schema ID: urn:OCPP:Cp:2:2025:1:SendLocalListRequest
Direction: CSMS → Charging Station
{
"versionNumber": "integer (> 0)", // REQUIRED - version to assign after update
"updateType": "Full | Differential", // REQUIRED - type of update
"localAuthorizationList": [ // OPTIONAL - array of AuthorizationData
{
"idToken": { // REQUIRED - IdTokenType
"idToken": "string (max 255)", // REQUIRED - case insensitive identifier
"type": "string (max 20)", // REQUIRED - e.g. "ISO14443", "eMAID"
"additionalInfo": [ // OPTIONAL
{
"additionalIdToken": "string (max 255)", // REQUIRED
"type": "string (max 50)" // REQUIRED
}
]
},
"idTokenInfo": { // OPTIONAL - presence controls operation
"status": "AuthorizationStatusEnumType", // REQUIRED
"cacheExpiryDateTime": "date-time", // OPTIONAL
"chargingPriority": "integer (-9..9)", // OPTIONAL - default 0
"groupIdToken": { ... }, // OPTIONAL - IdTokenType
"language1": "string (max 8)", // OPTIONAL - RFC 5646
"language2": "string (max 8)", // OPTIONAL - must differ from language1
"evseId": [1, 2], // OPTIONAL - restrict to specific EVSEs
"personalMessage": { // OPTIONAL - MessageContentType
"format": "ASCII|HTML|URI|UTF8|QRCODE", // REQUIRED
"content": "string (max 1024)", // REQUIRED
"language": "string (max 8)" // OPTIONAL
}
}
}
]
}SendLocalListResponse Schema
Direction: Charging Station → CSMS
{
"status": "Accepted | Failed | VersionMismatch", // REQUIRED
"statusInfo": { // OPTIONAL
"reasonCode": "string (max 20)", // REQUIRED
"additionalInfo": "string (max 1024)" // OPTIONAL
}
}GetLocalListVersionRequest Schema
Schema ID: urn:OCPP:Cp:2:2025:1:GetLocalListVersionRequest
Direction: CSMS → Charging Station
{
// No required fields - simple query request
"customData": { ... } // OPTIONAL - vendor-specific custom data
}Note: This is a simple request with no required fields. An empty JSON {} is a valid request.
GetLocalListVersionResponse Schema
Direction: Charging Station → CSMS
{
"versionNumber": "integer" // REQUIRED - current list version on CS
// 0 = no list installed
// > 0 = version of installed list
}3. D01 — Send Local Authorization List
Use CaseUse Case ID: D01
Direction: CSMS sends SendLocalListRequest →
Charging Station responds with SendLocalListResponse
CSMS Role: Initiator (sender of the request)
Sequence Diagram
┌──────┐ ┌───────────────────┐
│ CSMS │ │ Charging Station │
└──┬───┘ └────────┬──────────┘
│ │
│ SendLocalListRequest(versionNumber, │
│ updateType, localAuthorizationList) │
│ ───────────────────────────────────────────────> │
│ │
│ SendLocalListResponse(status) │
│ <─────────────────────────────────────────────── │
│ │SendLocalListRequest — Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
versionNumber | integer | Yes | The version number to assign to the list after this update is applied. Must be > 0 (value 0 is reserved to mean "no list installed"). For differential updates, must be strictly greater than the current version on the CS. |
updateType | UpdateEnumType | Yes | The type of update: "Full" or "Differential". |
localAuthorizationList | AuthorizationData[] | No | Array of authorization entries (min 1 item if provided). Can be omitted for a Full update (to clear the list) or for a Differential update (no-op, only version bumps). |
UpdateEnumType Values
| Value | Description |
|---|---|
"Full" | Replace the entire Local Authorization List on the Charging Station with the provided list. If localAuthorizationList is omitted/empty, this clears the entire list. |
"Differential" | Apply incremental changes (add, update, delete) to the existing list. |
AuthorizationData Object
Each entry in the localAuthorizationList array:
| Field | Type | Required | Description |
|---|---|---|---|
idToken | IdTokenType | Yes | The identifier token. |
idTokenInfo | IdTokenInfoType | No | Authorization status/metadata for this token. Presence or absence controls the operation in differential updates. |
Differential Update Behavior Based on idTokenInfo Presence
| idTokenInfo present? | Effect in Differential Update |
|---|---|
| Yes | Add or Update the entry in the Charging Station's list. If the idToken already exists, its info is replaced. If it doesn't exist, it is added. |
| No | Delete the entry matching this idToken from the Charging Station's list. |
IdTokenType Object
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
idToken | string | Yes | 255 | The identifier value (case insensitive). Can be an RFID UID, UUID, etc. |
type | string | Yes | 20 | Token type (e.g. "Central", "eMAID", "ISO14443", "ISO15693", "KeyCode", "Local", "MacAddress", "NoAuthorization"). |
additionalInfo | AdditionalInfoType[] | No | — | Array of additional identifier info (min 1 item if provided). |
AdditionalInfoType Object
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
additionalIdToken | string | Yes | 255 | The additional identifier token value. |
type | string | Yes | 50 | Custom type string agreed upon by all parties. Unknown types MAY be ignored. |
IdTokenInfoType Object
| Field | Type | Required | Description |
|---|---|---|---|
status | AuthorizationStatusEnumType | Yes | Current authorization status of this token. |
cacheExpiryDateTime | string (date-time) | No | Expiration timestamp. After this time the token is considered invalid. Used for caching only — do not stop active charging sessions based on expiry. |
chargingPriority | integer | No | Business priority. Range: -9 to 9. Default: 0. Higher = higher priority. |
groupIdToken | IdTokenType | No | Group/parent token for group authorization scenarios. |
language1 | string (max 8) | No | Preferred UI language (RFC 5646 language code, e.g. "en", "nl"). |
language2 | string (max 8) | No | Second preferred UI language. Must differ from language1. |
evseId | integer[] | No | If present, restricts this token to specific EVSE IDs only (not the entire station). Array of integers >= 0, min 1 item. |
personalMessage | MessageContentType | No | Personal message to display to the user. |
AuthorizationStatusEnumType Values
| Value | Description |
|---|---|
"Accepted" | Token is authorized. |
"Blocked" | Token is blocked (e.g. reported lost/stolen). |
"ConcurrentTx" | Token is already in use in another transaction. |
"Expired" | Token authorization has expired. |
"Invalid" | Token is unknown/invalid. |
"NoCredit" | Token has insufficient credit. |
"NotAllowedTypeEVSE" | Token not allowed on this EVSE type. |
"NotAtThisLocation" | Token not authorized at this location. |
"NotAtThisTime" | Token not authorized at this time. |
"Unknown" | Token status is unknown. |
MessageContentType Object
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
format | MessageFormatEnumType | Yes | — | Message format: "ASCII", "HTML", "URI", "UTF8", "QRCODE". |
content | string | Yes | 1024 | The message text. |
language | string | No | 8 | Language code (RFC 5646). |
Request Examples
Full List Replacement
{
"versionNumber": 5,
"updateType": "Full",
"localAuthorizationList": [
{
"idToken": {
"idToken": "AA12BB34",
"type": "ISO14443"
},
"idTokenInfo": {
"status": "Accepted",
"cacheExpiryDateTime": "2026-12-31T23:59:59Z"
}
},
{
"idToken": {
"idToken": "CC56DD78",
"type": "ISO14443"
},
"idTokenInfo": {
"status": "Accepted",
"cacheExpiryDateTime": "2026-06-30T23:59:59Z",
"chargingPriority": 3,
"language1": "en",
"personalMessage": {
"format": "UTF8",
"content": "Welcome, Premium Member!"
}
}
},
{
"idToken": {
"idToken": "EE90FF12",
"type": "ISO14443"
},
"idTokenInfo": {
"status": "Blocked"
}
}
]
}Differential Update — Add/Update Entries
{
"versionNumber": 6,
"updateType": "Differential",
"localAuthorizationList": [
{
"idToken": {
"idToken": "NEWTOKEN01",
"type": "ISO14443"
},
"idTokenInfo": {
"status": "Accepted",
"cacheExpiryDateTime": "2026-12-31T23:59:59Z"
}
},
{
"idToken": {
"idToken": "AA12BB34",
"type": "ISO14443"
},
"idTokenInfo": {
"status": "Blocked"
}
}
]
}Differential Update — Delete Entries
To delete an entry, include the idToken without idTokenInfo:
{
"versionNumber": 7,
"updateType": "Differential",
"localAuthorizationList": [
{
"idToken": {
"idToken": "EE90FF12",
"type": "ISO14443"
}
}
]
}Clear Entire List
Send a Full update with no localAuthorizationList field to remove all entries. The version is updated to the specified number.
{
"versionNumber": 8,
"updateType": "Full"
}Differential Update — Mixed Add/Update and Delete
{
"versionNumber": 9,
"updateType": "Differential",
"localAuthorizationList": [
{
"idToken": {
"idToken": "NEWTOKEN02",
"type": "eMAID"
},
"idTokenInfo": {
"status": "Accepted",
"evseId": [1, 2]
}
},
{
"idToken": {
"idToken": "OLDTOKEN99",
"type": "ISO14443"
}
}
]
}SendLocalListResponse — Status Values
| Value | Meaning | CSMS Action |
|---|---|---|
"Accepted" | The Charging Station successfully applied the update and set the version number. | Success. Update your internal tracking of this Charging Station's list version. |
"Failed" | The Charging Station could not apply the update (generic failure). | Retry. If the updateType was "Differential", fall back to sending the full list. |
"VersionMismatch" | The versionNumber in a Differential request was <= the Charging Station's current version. | The CSMS's view of the CS version is stale. Query with GetLocalListVersion, then resend with correct version or send a Full update. |
Response Examples
{
"status": "Accepted"
}{
"status": "VersionMismatch",
"statusInfo": {
"reasonCode": "VersionConflict",
"additionalInfo": "Current list version is 10, received version 8"
}
}StatusInfoType Object
| Field | Type | Required | Max Length | Description |
|---|---|---|---|---|
reasonCode | string | Yes | 20 | Predefined reason code (case-insensitive). |
additionalInfo | string | No | 1024 | Human-readable additional details. |
CSMS Response Handling Logic
Receive SendLocalListResponse:
├── status == "Accepted"
│ └── Update internal record: CS version = versionNumber from request
│ Done.
│
├── status == "Failed"
│ ├── Was updateType "Differential"?
│ │ ├── Yes → Retry with updateType "Full" (send complete list)
│ │ │ If list is too large for one message:
│ │ │ 1. Send initial subset with updateType "Full"
│ │ │ 2. Send remaining entries with updateType "Differential"
│ │ │ (incrementing version each time)
│ │ └── No (was already "Full") → Log error, alert operator, retry later
│ └── Done.
│
└── status == "VersionMismatch"
├── Was updateType "Differential"?
│ ├── Yes → Send GetLocalListVersionRequest to get current version
│ │ Then either:
│ │ a) Resend differential with corrected versionNumber (> CS version)
│ │ b) Send full list to force sync
│ └── No → Should not normally occur for Full. Log and investigate.
└── Done.Requirement Traceability (CSMS Responsibilities)
| Req ID | CSMS Responsibility |
|---|---|
D01.FR.01 | CSMS must include updateType and versionNumber in every SendLocalListRequest. |
D01.FR.02 | CSMS must check the status field in the response to determine success/failure. |
D01.FR.03 | If response is Failed or VersionMismatch and updateType was Differential, CSMS should resend as a full list. If the full list is too large, send an initial Full batch then Differential batches. |
D01.FR.04 | To clear the list: send updateType: "Full" with no localAuthorizationList field. |
D01.FR.05 | A Differential update with no localAuthorizationList is a no-op (only the version number is updated on the CS). |
D01.FR.06 | CSMS must ensure all idToken values in the list are unique. No duplicates in a single request. |
D01.FR.11 | CSMS must respect the Charging Station's ItemsPerMessageSendLocalList and BytesPerMessageSendLocalList configuration variables to limit message size. |
D01.FR.15 | For initial sync or full replacement: use updateType: "Full" with a non-empty list. |
D01.FR.16 | For adding/updating entries in differential mode: include idTokenInfo in the AuthorizationData. |
D01.FR.17 | For deleting entries in differential mode: omit idTokenInfo from the AuthorizationData (include only idToken). |
D01.FR.18 | CSMS must always use versionNumber > 0. |
D01.FR.19 | For differential updates, CSMS must use a versionNumber strictly greater than the CS's current version, or the CS will reject with VersionMismatch. |
4. D02 — Get Local List Version
Use CaseUse Case ID: D02
Direction: CSMS sends GetLocalListVersionRequest →
Charging Station responds with GetLocalListVersionResponse
CSMS Role: Initiator (sender of the request)
Sequence Diagram
┌──────┐ ┌───────────────────┐
│ CSMS │ │ Charging Station │
└──┬───┘ └────────┬──────────┘
│ │
│ GetLocalListVersionRequest() │
│ ───────────────────────────────────────────────────> │
│ │
│ GetLocalListVersionResponse(versionNumber) │
│ <─────────────────────────────────────────────────── │
│ │GetLocalListVersionRequest
This is a simple request with no required fields. The CSMS sends it to query the current version of the Local Authorization List on a Charging Station.
{}{
"customData": {
"vendorId": "com.example.vendor"
}
}GetLocalListVersionResponse
| Field | Type | Required | Description |
|---|---|---|---|
versionNumber | integer | Yes | Current version of the Local Authorization List on the Charging Station. |
Version Number Semantics
| Value | Meaning |
|---|---|
0 | No Local Authorization List exists. Either LocalAuthListEnabled is false, or no list has ever been sent to this Charging Station. |
> 0 | The version of the currently installed list. This matches the versionNumber from the last successfully applied SendLocalListRequest. An emptied list (cleared via a Full update with no entries) still has a version > 0. |
Response Examples
{
"versionNumber": 5
}{
"versionNumber": 0
}CSMS Response Handling Logic
Receive GetLocalListVersionResponse:
├── versionNumber == 0
│ └── No list exists on the Charging Station.
│ Action: Send a Full list via SendLocalListRequest (versionNumber: 1).
│
├── versionNumber == CSMS's expected version
│ └── Lists are in sync. No action needed.
│
├── versionNumber < CSMS's expected version
│ └── Charging Station is behind (e.g. it was reset, or a previous update failed).
│ Action: Send a Full list to re-sync.
│
└── versionNumber > CSMS's expected version
└── CSMS tracking is stale (should not normally happen since CSMS is sole sender).
Action: Update internal tracking to match CS version.
Consider sending a Full list to ensure correctness.Requirement Traceability (CSMS Responsibilities)
| Req ID | CSMS Responsibility |
|---|---|
D02.FR.01 | CSMS should expect a valid version number when LocalAuthListEnabled is true. |
D02.FR.02 | CSMS should interpret versionNumber == 0 as "no list has been sent yet" (when enabled but never initialized). |
D02.FR.03 | CSMS should interpret versionNumber == 0 as "list not enabled" (when LocalAuthListEnabled is false). |
5. CSMS Implementation Strategy: Synchronization Workflow
StrategyRecommended Sync Algorithm
The CSMS should maintain an internal record of each Charging Station's list version. Here is the recommended synchronization flow:
On Charging Station (re)connect or periodic sync:
1. Send GetLocalListVersionRequest
2. Receive GetLocalListVersionResponse(versionNumber)
3. IF versionNumber == 0:
→ CS has no list. Send Full list with versionNumber = CSMS_current_version
4. ELSE IF versionNumber == CSMS_current_version:
→ In sync. Nothing to do.
5. ELSE IF versionNumber < CSMS_current_version:
→ CS is behind.
→ Option A: Send Differential update (if delta is small)
Use versionNumber = CSMS_current_version (must be > CS version)
→ Option B: Send Full update (if delta is large or unknown)
6. ELSE (versionNumber > CSMS_current_version):
→ Unexpected. Log warning. Send Full list to force re-sync.Handling Large Lists (Chunked Sending)
When the full list is too large to fit in a single message (constrained by ItemsPerMessageSendLocalList and BytesPerMessageSendLocalList),
use this strategy:
Given: total_entries = all entries to send
max_per_msg = ItemsPerMessageSendLocalList from CS config
version = starting version number
1. First message:
- updateType: "Full"
- localAuthorizationList: first chunk (up to max_per_msg entries)
- versionNumber: version
→ This REPLACES the entire list with just this chunk
2. Subsequent messages (for remaining entries):
- updateType: "Differential"
- localAuthorizationList: next chunk (up to max_per_msg entries)
- versionNumber: ++version (must increment for each differential)
→ Each includes idTokenInfo for all entries (add operation)
3. Repeat step 2 until all entries are sent.
4. After final response, verify: CS version == last versionNumber sent.Important: The first message with updateType: "Full" replaces the entire list with just that chunk. All subsequent chunks must use "Differential" to add to the list rather than replace it again.
CSMS Internal Data Model (Suggested)
The following data model is recommended for tracking Local Authorization List state on the CSMS side:
Per Charging Station, track:
- charging_station_id: string
- local_list_version: integer // last successfully applied version
- local_list_enabled: boolean // mirrors LocalAuthListEnabled config var
- max_items_per_message: integer // from ItemsPerMessageSendLocalList
- max_bytes_per_message: integer // from BytesPerMessageSendLocalList
- max_list_entries: integer // from LocalAuthListEntries (max capacity)
- last_sync_timestamp: datetimePer authorization entry (the master list), track:
- id_token: string
- id_token_type: string
- authorization_status: AuthorizationStatusEnumType
- cache_expiry: datetime (optional)
- charging_priority: integer (optional)
- group_id_token: IdTokenType (optional)
- language1: string (optional)
- language2: string (optional)
- evse_ids: integer[] (optional)
- personal_message: MessageContentType (optional)6. Configuration Variables Reference
ReferenceThese Charging Station configuration variables are relevant to Local Authorization List
Management. The CSMS should query them via GetVariables (Provisioning functional block) before performing list operations.
| Variable | Description | CSMS Usage |
|---|---|---|
LocalAuthListEnabled | Whether the Local Authorization List feature is enabled on the CS. | Check before sending list updates. If false, list operations are meaningless. Can be set via SetVariables. |
LocalAuthListEntries | Reports the current number AND maximum possible number of entries in the CS's list. | Use to know the capacity limit. Do not send more entries than the CS can store. |
ItemsPerMessageSendLocalList | Maximum number of AuthorizationData entries allowed in a single SendLocalListRequest. | Use to chunk large lists into multiple messages. |
BytesPerMessageSendLocalList | Maximum byte size of a single SendLocalListRequest message. | Use alongside ItemsPerMessageSendLocalList to ensure messages don't exceed limits. |
7. Error Handling & Edge Cases
ReferenceDifferential Update Fails or Version Mismatch
| Scenario | CSMS Behavior |
|---|---|
status == "Failed" with updateType == "Differential" | Resend as a Full update. If too large for one message, use the chunked strategy (section 5.2). |
status == "VersionMismatch" with updateType == "Differential" | The CSMS's versionNumber was <= the CS's current version. Send GetLocalListVersionRequest to get the actual version, then resend with a corrected versionNumber > CS version, or send a Full update to force sync. |
status == "Failed" with updateType == "Full" | Genuine failure (storage error, etc). Log the error, alert operator, retry after a delay. |
Charging Station Reconnection
When a Charging Station reconnects (after reboot, network loss, etc.):
- Send
GetLocalListVersionRequestto verify the list state. - Compare with CSMS's expected version for this Charging Station.
- If out of sync, resend the appropriate update (Full or Differential).
Version Number Management
Always use versionNumber > 0
The CSMS is the sole authority for version numbers. Value 0 is reserved to mean "no list installed".
Increment monotonically
Increment the version number with each update to maintain a clear update history.
Differential version constraint
For differential updates, the versionNumber sent must be strictly greater than the CS's current version, or the CS will reject with VersionMismatch (D01.FR.19).
Full update flexibility
For full updates, there is no version comparison constraint, but you should still use a higher version to maintain consistency.
Uniqueness Constraint
All idToken values in the Local Authorization List must be unique (D01.FR.06). When building the localAuthorizationList array:
- Do not include duplicate idToken values in a single request.
- When sending a Full update, ensure the entire list has no duplicates.
- When sending a Differential update, each idToken should appear at most once in the request.
Empty/No-op Operations
| Operation | Result |
|---|---|
Full update with no localAuthorizationList field | Clears the entire list on the CS. Version is updated to versionNumber. |
Differential update with no localAuthorizationList field | No-op. List is unchanged. Version is updated to versionNumber. |
Full update with an empty localAuthorizationList array | Not valid per schema (minItems: 1). Either omit the field entirely or provide at least 1 entry. |
OCPP-J Message Framing
All messages are sent over the OCPP-J (JSON over WebSocket) protocol using the standard CALL/CALLRESULT/CALLERROR framing:
CSMS sending SendLocalListRequest (CALL)
[2, "msg-uuid-001", "SendLocalList", {
"versionNumber": 5,
"updateType": "Full",
"localAuthorizationList": [
{
"idToken": {"idToken": "AA12BB34", "type": "ISO14443"},
"idTokenInfo": {"status": "Accepted"}
}
]
}]Charging Station responding (CALLRESULT)
[3, "msg-uuid-001", {
"status": "Accepted"
}]CSMS sending GetLocalListVersionRequest (CALL)
[2, "msg-uuid-002", "GetLocalListVersion", {}]Charging Station responding (CALLRESULT)
[3, "msg-uuid-002", {
"versionNumber": 5
}]8. Message Direction Summary
ReferenceOutgoing from CSMS (CSMS → Charging Station)
| Message | When to Send | Use Cases |
|---|---|---|
SendLocalListRequest | Push a full or differential list update to the Charging Station | D01 |
GetLocalListVersionRequest | Query the current version of the Local Authorization List on the Charging Station | D02 |
Incoming to CSMS (Charging Station → CSMS)
| Message | Description | Use Cases |
|---|---|---|
SendLocalListResponse | Response to CSMS SendLocalListRequest — contains status (Accepted, Failed, VersionMismatch) | D01 |
GetLocalListVersionResponse | Response to CSMS GetLocalListVersionRequest — contains current versionNumber | D02 |
Related Messages (Other Functional Blocks)
The following messages from other functional blocks are relevant to Local Authorization List Management:
| Message | Functional Block | Relevance |
|---|---|---|
GetVariables | Provisioning | Query CS configuration variables (LocalAuthListEnabled, LocalAuthListEntries, etc.) |
SetVariables | Provisioning | Set LocalAuthListEnabled to enable/disable the feature |