OCPP 2.1 Edition 2 Section P

DataTransfer - CSMS Developer Guide

Based on OCPP 2.1 Edition 2 Specification (Part 2), Section P (DataTransfer). This guide covers both DataTransfer flows (P01–P02), including sending and receiving vendor-specific data between the CSMS and Charging Stations.

7 Sections
2 Use Cases
P01 – P02

1. Overview

Introduction

The DataTransfer functional block enables exchange of vendor-specific, non-standard data between the CSMS and Charging Stations. It is a bidirectional message: either party can initiate a DataTransferRequest.

CSMS Roles in DataTransfer

Initiator (P01)

CSMS sends custom data to a Charging Station. Constructs and sends DataTransferRequest, then processes the DataTransferResponse.

Responder (P02)

Charging Station sends custom data to CSMS. CSMS receives DataTransferRequest, validates, and returns DataTransferResponse.

Role Use Case CSMS Action
Initiator (P01) CSMS sends custom data to Charging Station Constructs and sends DataTransferRequest, processes DataTransferResponse
Responder (P02) Charging Station sends custom data to CSMS Receives DataTransferRequest, validates, and returns DataTransferResponse

Extension Mechanisms in OCPP 2.1

Two extension mechanisms exist in OCPP 2.1:

DataTransferRequest / Response

Full custom message exchange for vendor-specific functionality. This is the mechanism covered in this guide.

CustomData Element

Optional element in all JSON schemas that allows additional properties on any standard type. Not covered here (see OCPP 2.1 Part 4).

Important Caveat

Use DataTransfer with extreme caution and only for optional functionality. It impacts compatibility with systems that do not implement the same vendor-specific extensions. Always verify that the functionality you need doesn't already exist in the standard OCPP 2.1 messages before resorting to DataTransfer.

2. Data Types & Enumerations

Reference

DataTransferStatusEnumType

Defines the possible status values in a DataTransferResponse.

Enum values
"Accepted" | "Rejected" | "UnknownMessageId" | "UnknownVendorId"
Value When Used (CSMS as Responder) When Received (CSMS as Initiator)
Accepted CSMS recognized the vendorId (and messageId if used) and successfully processed the request. Charging Station processed the request successfully. Read data for results.
Rejected CSMS recognized the vendorId/messageId but is refusing the specific request (e.g., invalid data, business rule violation). Charging Station understood but rejected the request. Check statusInfo for reason.
UnknownVendorId CSMS has no handler registered for the provided vendorId. Mandatory per P02.FR.06. Charging Station does not support the vendor extension. Do not retry.
UnknownMessageId CSMS recognizes the vendorId but does not recognize the messageId. Mandatory per P02.FR.07. Charging Station knows the vendor but not this message. Check messageId.

StatusInfoType

Optional additional information returned with response statuses.

StatusInfoType Schema
{
  "reasonCode": string,     // REQUIRED - Predefined code (max 20 chars, case-insensitive)
  "additionalInfo": string  // OPTIONAL - Human-readable detail text (max 1024 chars)
}
Field Type Required Max Length Description
reasonCode string Yes 20 Predefined code for the reason. Case-insensitive.
additionalInfo string No 1024 Human-readable detail text.

CustomDataType

Optional custom extension data. Note that CustomDataType intentionally does not have additionalProperties: false, allowing arbitrary extra fields.

CustomDataType Schema
{
  "vendorId": string   // REQUIRED - Vendor identifier (max 255 chars)
  // ... additional arbitrary fields allowed
}

Constraints Summary

Constraint Value
vendorId max length 255 characters
messageId max length 50 characters
data max length Undefined (agree between parties)
statusInfo.reasonCode max length 20 characters
statusInfo.additionalInfo max length 1024 characters
CustomDataType.vendorId max length 255 characters
Required fields in request vendorId only
Required fields in response status only

3. P01 — CSMS Sends DataTransfer to Charging Station

CSMS-Initiated
Use Case ID P01
Direction CSMS → Charging Station
CSMS Role Initiator (sends request, processes response)
OCPP Messages DataTransferRequest / DataTransferResponse

Flow Diagram

Sequence Diagram (CSMS perspective)
CSMS                                          Charging Station
 |                                                     |
 |  DataTransferRequest(vendorId, [messageId], [data]) |
 |---------------------------------------------------->|
 |                                                     |
 |  DataTransferResponse(status, [statusInfo], [data]) |
 |<----------------------------------------------------|
 |                                                     |

Constructing the Request

The CSMS builds a DataTransferRequest with the following fields:

Field Type Required Max Length Description
vendorId string Yes 255 Identifies the vendor-specific implementation. Should use reversed DNS notation (e.g., com.mycompany.feature).
messageId string No 50 Optional identifier for a specific message type or implementation within the vendor namespace.
data any No undefined Arbitrary data payload. No type or length constraint in the schema. Can be a string, number, object, array, etc.
customData CustomDataType No - Optional custom extension data (requires vendorId).
DataTransferRequest — Example
{
  "vendorId": "com.example.fleet",
  "messageId": "getVehicleStatus",
  "data": {
    "vehicleId": "VIN-12345",
    "requestTimestamp": "2025-06-15T10:30:00Z"
  }
}

Processing the Response

The Charging Station responds with a DataTransferResponse. The CSMS must handle all possible status values:

Status Value Meaning CSMS Action
Accepted Request was understood and processed successfully. Process the optional data field per your vendor-specific agreement.
Rejected Request was understood but explicitly rejected. Handle rejection per vendor-specific agreement. Log the reason.
UnknownVendorId The Charging Station has no implementation for the given vendorId. The station does not support this vendor extension. Do not retry with same vendorId to this station.
UnknownMessageId The vendorId is known, but the messageId is not recognized. The station supports this vendor but not this specific message. Check messageId spelling/version.
Response — Accepted
{
  "status": "Accepted",
  "data": {
    "vehicleStatus": "charging",
    "batteryLevel": 72
  }
}
Response — UnknownVendorId
{
  "status": "UnknownVendorId",
  "statusInfo": {
    "reasonCode": "UnknownVendor",
    "additionalInfo": "Vendor com.example.fleet is not supported by this station."
  }
}

OCPP WebSocket Wire Format

The DataTransferRequest is sent as an OCPP CALL message over WebSocket:

CALL message format
[2, "msg-uuid-001", "DataTransfer", {
  "vendorId": "com.example.fleet",
  "messageId": "getVehicleStatus",
  "data": { "vehicleId": "VIN-12345", "requestTimestamp": "2025-06-15T10:30:00Z" }
}]

Wire Format Fields

  • 2 = CALL message type
  • "msg-uuid-001" = unique message ID for correlation
  • "DataTransfer" = OCPP action name

Requirements Checklist for P01

Req ID Requirement Implementation Guidance
P01.FR.01 Only use DataTransferRequest for functions not supported by standard OCPP. Before implementing a DataTransfer, verify the functionality doesn't already exist in OCPP 2.1.
P01.FR.02 vendorId should use reversed DNS namespace (e.g., com.example.feature). Use your organization's reversed domain as prefix.
P01.FR.03 messageId may be used to indicate a specific message or implementation. Define a registry of messageIds per vendorId in your vendor documentation.
P01.FR.04 Length of data is undefined; recommended to agree on it between parties. Document the expected payload schema per messageId in your vendor-specific spec.
P01.FR.05 If recipient has no implementation for the vendorId, it returns UnknownVendorId. Handle this status in your response processing — the station doesn't support this vendor.
P01.FR.06 If messageId mismatches, recipient returns UnknownMessageId. Handle this status — the vendorId is known but the specific message is not.
P01.FR.07 Usage of Accepted/Rejected and data is part of vendor-specific agreement. Define and document your own contract for what these mean per messageId.

4. P02 — CSMS Receives DataTransfer from Charging Station

CS-Initiated
Use Case ID P02
Direction Charging Station → CSMS
CSMS Role Responder (receives request, must send response)
OCPP Messages DataTransferRequest / DataTransferResponse

Flow Diagram

Sequence Diagram (CSMS perspective)
Charging Station                                CSMS
      |                                            |
      |  DataTransferRequest(vendorId, [messageId], [data])
      |------------------------------------------->|
      |                                            |  ← CSMS must validate
      |                                            |    and respond
      |  DataTransferResponse(status, [statusInfo], [data])
      |<-------------------------------------------|
      |                                            |

Incoming Request: DataTransferRequest

The CSMS receives a DataTransferRequest from a Charging Station with the following fields:

Field Type Required Max Length Description
vendorId string Yes 255 Identifies the vendor-specific implementation.
messageId string No 50 Identifies the specific message type within the vendor namespace.
data any No undefined Arbitrary payload. Can be any JSON type.
customData CustomDataType No - Optional custom extension data.
Incoming Request — Example
{
  "vendorId": "com.chargervendor.diagnostics",
  "messageId": "temperatureAlert",
  "data": {
    "connectorId": 1,
    "temperature": 85.5,
    "unit": "celsius",
    "timestamp": "2025-06-15T14:22:00Z"
  }
}

CSMS Validation & Response Logic

The CSMS must follow this decision logic when processing incoming DataTransferRequest messages:

Validation Decision Tree
Receive DataTransferRequest
         |
         v
+-------------------------+
| Is vendorId recognized? |
+----------+--------------+
           |
      No   |   Yes
      |    |    |
      v    |    v
   Return  | +------------------------------+
   status: | | Is messageId provided and    |
   "Unknown| | recognized (if applicable)?  |
   VendorId| +-------------+----------------+
   "       |          |         |
           |     No   |    Yes  |
           |     |    |    |    |
           |     v    |    v    |
           |  Return  | +---------------------+
           |  status: | | Process vendor-     |
           |  "Unknown| | specific logic      |
           |  MessageId +----------+----------+
           |  "       |            |
           |          |       +----+-----+
           |          |  Success?    Failure?
           |          |       |          |
           |          |       v          v
           |          |    Return     Return
           |          |    status:    status:
           |          |    "Accepted" "Rejected"
           |          |    + [data]   + [statusInfo]

Building the Response: DataTransferResponse

The CSMS must always respond with a DataTransferResponse:

Field Type Required Description
status DataTransferStatusEnumType Yes One of: Accepted, Rejected, UnknownMessageId, UnknownVendorId
statusInfo StatusInfoType No Additional information about the status.
data any No Arbitrary response data per vendor-specific agreement.
customData CustomDataType No Optional custom extension data.

Response Examples by Status

Status: Accepted
{
  "status": "Accepted",
  "data": {
    "acknowledged": true,
    "action": "logged"
  }
}
Status: Rejected
{
  "status": "Rejected",
  "statusInfo": {
    "reasonCode": "InvalidData",
    "additionalInfo": "Temperature value out of expected range."
  }
}
Status: UnknownVendorId
{
  "status": "UnknownVendorId",
  "statusInfo": {
    "reasonCode": "UnknownVendor",
    "additionalInfo": "Vendor 'com.chargervendor.diagnostics' is not registered in this CSMS."
  }
}
Status: UnknownMessageId
{
  "status": "UnknownMessageId",
  "statusInfo": {
    "reasonCode": "UnknownMessage",
    "additionalInfo": "messageId 'temperatureAlert' is not a recognized message for vendor 'com.chargervendor.diagnostics'."
  }
}

Requirements Checklist for P02

Req ID Requirement Implementation Guidance
P02.FR.01 The vendorId should be known and uniquely identify the vendor implementation. Maintain a registry/map of supported vendorIds in your CSMS.
P02.FR.02 Only used for functions not supported by standard OCPP. This is the Charging Station's responsibility, but the CSMS should validate accordingly.
P02.FR.03 vendorId should use reversed DNS notation. Validate format if desired, but do not reject based on format alone.
P02.FR.04 messageId may indicate a specific message or implementation. Use as a sub-routing key within a vendorId namespace.
P02.FR.05 Length of data is undefined; recommended to agree between parties. Validate payload size against your vendor-specific contract.
P02.FR.06 If CSMS has no implementation for the vendorId, return UnknownVendorId. Mandatory. Implement vendorId lookup. If not found, respond with UnknownVendorId.
P02.FR.07 If messageId mismatches (when used), return UnknownMessageId. Mandatory. If vendorId is known but messageId is not recognized, respond with UnknownMessageId.
P02.FR.08 Usage of Accepted/Rejected and data is per vendor-specific agreement. Define and document your vendor-specific contracts.

5. Message Schemas

Reference

DataTransferRequest JSON Schema

DataTransferRequest Schema
{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "$id": "urn:OCPP:Cp:2:2025:1:DataTransferRequest",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "vendorId": {
      "description": "This identifies the Vendor specific implementation",
      "type": "string",
      "maxLength": 255
    },
    "messageId": {
      "description": "May be used to indicate a specific message or implementation.",
      "type": "string",
      "maxLength": 50
    },
    "data": {
      "description": "Data without specified length or format. This needs to be decided by both parties (Open to implementation)."
    },
    "customData": {
      "$ref": "#/definitions/CustomDataType"
    }
  },
  "required": ["vendorId"]
}

Key Observations

  • vendorId is the only required field.
  • data has no type constraint — it accepts any valid JSON value (string, number, boolean, object, array, null).
  • additionalProperties: false means no fields beyond the four defined above are allowed at the top level.

DataTransferResponse JSON Schema

DataTransferResponse Schema
{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "$id": "urn:OCPP:Cp:2:2025:1:DataTransferResponse",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "status": {
      "$ref": "#/definitions/DataTransferStatusEnumType"
    },
    "statusInfo": {
      "$ref": "#/definitions/StatusInfoType"
    },
    "data": {
      "description": "Data without specified length or format, in response to request."
    },
    "customData": {
      "$ref": "#/definitions/CustomDataType"
    }
  },
  "required": ["status"]
}

Key Observations

  • status is the only required field.
  • data has no type constraint, same as the request.
  • statusInfo provides optional structured error/info context.

Supporting Type Definitions

DataTransferStatusEnumType
{
  "type": "string",
  "enum": ["Accepted", "Rejected", "UnknownMessageId", "UnknownVendorId"]
}
StatusInfoType
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "reasonCode": {
      "type": "string",
      "maxLength": 20
    },
    "additionalInfo": {
      "type": "string",
      "maxLength": 1024
    },
    "customData": {
      "$ref": "#/definitions/CustomDataType"
    }
  },
  "required": ["reasonCode"]
}
CustomDataType
{
  "type": "object",
  "properties": {
    "vendorId": {
      "type": "string",
      "maxLength": 255
    }
  },
  "required": ["vendorId"]
}

// Note: CustomDataType intentionally
// does NOT have
// "additionalProperties: false",
// allowing arbitrary extra fields.

6. CSMS Decision Logic

Implementation

P02 — Handling Incoming DataTransferRequest

Pseudocode for handling incoming DataTransferRequest messages from Charging Stations:

P02 Handler (pseudocode)
function handleDataTransferRequest(request: DataTransferRequest): DataTransferResponse {

    // Step 1: Vendor lookup (MANDATORY - P02.FR.06)
    vendorHandler = vendorRegistry.get(request.vendorId)

    if (vendorHandler == null) {
        return {
            status: "UnknownVendorId",
            statusInfo: {
                reasonCode: "UnknownVendor",
                additionalInfo: "No handler for vendorId: " + request.vendorId
            }
        }
    }

    // Step 2: Message lookup (MANDATORY when messageId is used - P02.FR.07)
    if (request.messageId != null) {
        messageHandler = vendorHandler.getMessageHandler(request.messageId)

        if (messageHandler == null) {
            return {
                status: "UnknownMessageId",
                statusInfo: {
                    reasonCode: "UnknownMessage",
                    additionalInfo: "No handler for messageId: " + request.messageId
                }
            }
        }
    }

    // Step 3: Process the vendor-specific logic
    try {
        result = messageHandler.process(request.data)

        return {
            status: "Accepted",
            data: result    // optional response data per vendor agreement
        }
    } catch (error) {
        return {
            status: "Rejected",
            statusInfo: {
                reasonCode: truncate(error.code, 20),
                additionalInfo: truncate(error.message, 1024)
            }
        }
    }
}

P01 — Sending DataTransferRequest to Charging Station

Pseudocode for sending DataTransferRequest messages to Charging Stations:

P01 Sender (pseudocode)
function sendDataTransfer(
    chargingStationId: string,
    vendorId: string,
    messageId?: string,
    data?: any
): DataTransferResponse {

    request = {
        vendorId: vendorId          // required, max 255 chars
    }

    if (messageId != null) {
        request.messageId = messageId   // optional, max 50 chars
    }

    if (data != null) {
        request.data = data             // optional, any JSON type
    }

    // Send via OCPP WebSocket as CALL [2, uniqueId, "DataTransfer", request]
    response = ocppConnection.send(chargingStationId, "DataTransfer", request)

    // Handle response
    switch (response.status) {
        case "Accepted":
            // Success - process response.data per vendor agreement
            return response

        case "Rejected":
            // Station understood but rejected - check statusInfo
            log.warn("DataTransfer rejected", response.statusInfo)
            return response

        case "UnknownVendorId":
            // Station does not support this vendor extension
            log.warn("Station does not support vendorId: " + vendorId)
            return response

        case "UnknownMessageId":
            // Station supports vendor but not this message
            log.warn("Station does not support messageId: " + messageId)
            return response
    }
}

7. Implementation Notes & Best Practices

Guide

VendorId Convention

Use reversed DNS notation for vendorId values to ensure global uniqueness and avoid collisions between different vendors:

Format Example
com.<company>.<feature> com.example.fleet
org.<organization>.<module> org.openchargealliance.test

Structured Data Field

Although the data field has no schema constraint, it is strongly recommended to use structured JSON objects. This allows for:

  • Schema validation on your application layer
  • Versioning of the custom protocol
  • Easier debugging and logging
Recommended pattern
{
  "vendorId": "com.example.ice",
  "messageId": "iceParkedAtCs",
  "data": {
    "start_time": "2020-04-01T11:01:02"
  }
}

CSMS Vendor Handler Architecture

Implement a registry pattern to handle incoming DataTransfer messages:

Vendor Handler Registry
CSMS DataTransfer Handler
+-- Vendor Registry (Map<vendorId, VendorHandler>)
|   +-- "com.vendorA.feature" -> VendorAHandler
|   |   +-- "messageType1" -> handler function
|   |   +-- "messageType2" -> handler function
|   |   +-- (unknown messageId) -> return UnknownMessageId
|   +-- "com.vendorB.diagnostics" -> VendorBHandler
|   |   +-- ...
|   +-- (unknown vendorId) -> return UnknownVendorId

Error Handling Notes

  • The specification states error handling is n/a for both P01 and P02. This means OCPP-level errors (like CALLERROR for malformed JSON, etc.) are handled by the underlying OCPP transport layer, not by the DataTransfer logic.
  • Application-level errors within your vendor-specific logic should be communicated via the Rejected status with descriptive statusInfo.

Wire-Level Message Examples

Complete OCPP WebSocket wire-level examples for both flows:

CALL — CSMS to CS (P01)
[2, "19223201", "DataTransfer", {
  "vendorId": "com.example.fleet",
  "messageId": "getVehicleStatus",
  "data": {
    "vehicleId": "VIN-12345"
  }
}]
CALLRESULT — CS to CSMS (P01)
[3, "19223201", {
  "status": "Accepted",
  "data": {
    "vehicleStatus": "charging",
    "batteryLevel": 72
  }
}]
CALL — CS to CSMS (P02)
[2, "abc-123-def", "DataTransfer", {
  "vendorId": "com.chargervendor.diagnostics",
  "messageId": "temperatureAlert",
  "data": {
    "connectorId": 1,
    "temperature": 85.5,
    "unit": "celsius"
  }
}]
CALLRESULT — CSMS to CS (P02)
[3, "abc-123-def", {
  "status": "Accepted",
  "data": {
    "acknowledged": true
  }
}]

Wire Format Reference

  • [2, ...] = CALL message (request)
  • [3, ...] = CALLRESULT message (response)
  • The second element is the unique message ID used for request/response correlation

OCPP 2.1 DataTransfer Flows (P01–P02) - CSMS Developer Guide. Based on OCPP 2.1 Edition 2 Specification (Part 2), Section P.