# Charging Station Message Exchange (CSME)

> A service that exchanges real-time charging-station telemetry and commands between Road and an external party such as a smart-charging service provider.

CSME exchanges information between a charging station connected to the Road CPO and an external party, such as a smart-charging service provider. It supports smart charging, V1G and V2G controls, and other use cases that depend on a station's real-time data.

CSME forwards a station's messages from the Road CPO to the external party as they happen: the meter values recorded during a charging session, and events such as changes in charging state. A third party can use this data to work out a charging schedule and issue commands back to control the charge. A fleet operator can use the same telemetry to understand charging patterns and make better use of the capacity it has.

## Terminology

Some common and new terms used throughout the document:

- *CSO / CPO*, Charging Station Operator, e.g. Road
- *CSMS*, Charging Station Management System, cloud-based backend maintained by Road where charging stations connect to
- *CSMEC or CSME Client*, Charging Station Message Exchange Client, an external party that is interested in receiving information from a charging station
- *CSMES or CSME Service*, Charging Station Message Exchange Service, the CPO service which enables REST communication between CPO and CSMEC
- *Pairing*, a process that establishes a link between a charging station and CSMEC that enables message exchange
- *Unpairing*, a process that disables the link between charging station and CSMEC
- Charging station *telemetry*, OCPP messages that are generated by a charging station and forwarded to CSMEC. In principle, any OCPP message type could be included but in practice the set of message types is limited to the ones that give insight into charging process such as: `StartTransaction`, `StopTransaction`, `MeterValues` for OCPP 1.6, `TransactionEvent` for OCPP 2.0.1, etc.
- Charging station *commands*, messages that CSMEC sends to a charging station via CSMES in order to influence the charging process or to inspect it, e.g. `SetChargingProfile`, `ClearChargingProfile`, `GetConfiguration`, `GetBaseReport`, etc.

## API overview

The CSME API (CSMES) is the CPO-side service that connects a charging station to a CSMEC. It forwards station data to the CSMEC and takes commands back from the CSMEC to send on to the station.

To establish a pairing and receive a station's messages, the CSMEC must implement and expose a number of API endpoints on its side.

The API follows RESTful principles and exchanges messages in JSON format.

### API versioning

Every API endpoint path has a major version prefix, e.g. `/1` in `/1/pairings`. This way we can guarantee full backwards compatibility within the scope of any major version. Within a major version the API will evolve and new payload fields or new API endpoints can and will be introduced, so clients need to make sure to parse messages accordingly and not fail on unknown fields. As soon as it's no longer possible to evolve the API without dropping or renaming existing fields a new major version of the API will be introduced.

### Schema validation

The API implements strict schema validation for incoming messages based on the respective OCPP version specification, ensuring that the messages are correctly formatted.

### Error handling

The API provides error codes and messages for various scenarios, such as `400` for `Bad Request` and `401` for `Unauthorized`, helping clients understand and rectify issues. A retry with exponential backoff is recommended for any connection error or `5xx` error code.

Application level errors for charging station commands and telemetry are returned in the response envelope. It is up to the CSMES and CSMEC to decide whether erroneous events should be re-submitted or whether they should be treated as a fire and forget.

### General flow

Following is a sample flow of events when a charging station pairing is initiated from CSMEC side, followed by charging station message exchange and eventual unpairing:

```mermaid
sequenceDiagram
  actor User
  participant CSMES as CSME Service
  participant CSMEC as CSME Client
  User->>+CSMEC: Generate pairing code
  CSMEC-->>-User: Pairing code
  User->>+CSMES: Pair a charging station
  CSMES->>+CSMEC: Confirm pairing (pairing code<br/>and charging station details)
  CSMEC-->>-CSMES: Pairing confirmed
  CSMES-->>-User: Pairing confirmed
  loop During charging process
    CSMES->>+CSMEC: Forward telemetry for<br/>any paired charging station
    CSMEC-->>-CSMES: Confirm reception
    Note over CSMES,CSMEC: Parties independently exchange<br/>telemetry messages and commands
    CSMEC->>+CSMES: Send charging station commands
    CSMES-->>-CSMEC: Confirm delivery
  end
  User->>+CSMES: Unpair charging station
  CSMES->>+CSMEC: Propagate unpairing request
  CSMEC-->>-CSMES: Unpairing confirmed
  CSMES-->-User: Unpairing confirmed
```

### Pairing initiated by CSMEC

In this case the user enters a *pairing code* provided by the CSMEC into the CSMES system in order to establish a charging station pairing to CSMEC.

```mermaid
sequenceDiagram
    actor User
    participant CSMES as CSME Service
    participant CSMEC as CSME Client
    User->>+CSMEC: Generate pairing code
    CSMEC-->>-User: Pairing code
    User->>+CSMES: Pair a charging station
    CSMES->>+CSMEC: Confirm pairing<br/>POST /1/csmec/pairings<br/>(charging station details)
    CSMEC-->>-CSMES: Pairing confirmed
    CSMES-->>-User: Pairing confirmed
```

The CSMES will initiate a request to CSMEC as follows:

#### `POST /1/csmec/pairings` (CSMEC API)

Sample request body:

```json
{
  "id": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
  "pairingCode": "4A6BH28",
  "chargingStation": {
    "evses": [
      {
        "evseId": 1,
        "capabilities": ["CHARGING_PROFILE_CAPABLE"],
        "connectors": [
          {
            "id": 1,
            "powerType": "AC_3_PHASE",
            "maxVoltage": 230,
            "maxAmperage": 16
          }
        ]
      }
    ]
  }
}
```

`Pairing` object:

| Name              | Type              | Description                                                                 | Required? |
| ----------------- | ----------------- | --------------------------------------------------------------------------- | --------- |
| `id`              | `UUID`            | Pairing ID, generated by CSMES when charging station pairing is established | Yes       |
| `pairingCode`     | `string`          | Pairing code provided by the user                                           | Yes       |
| `chargingStation` | `ChargingStation` | Charging station details                                                    | Yes       |

`ChargingStation` object:

| Name    | Type     | Description                    | Required? |
| ------- | -------- | ------------------------------ | --------- |
| `evses` | `EVSE[]` | List of charging station EVSEs | Yes       |

`EVSE` object:

| Name           | Type          | Description                                                   | Required? |
| -------------- | ------------- | ------------------------------------------------------------- | --------- |
| `evseId`       | `int`         | EVSE identifier within a charging station: `1`, `2` and so on | Yes       |
| `capabilities` | `string[]`    | List of capabilities this EVSE supports                       | Yes       |
| `connectors`   | `Connector[]` | List of EVSE connectors                                       | Yes       |

`Connector` object:

| Name          | Type     | Description                                                                   | Required? |
| ------------- | -------- | ----------------------------------------------------------------------------- | --------- |
| `id`          | `int`    | Connector identifier within an EVSE: `1`, `2` and so on                       | Yes       |
| `powerType`   | `string` | Connector power type, one of: `UNSPECIFIED`, `AC_1_PHASE`, `AC_3_PHASE`, `DC` | Yes       |
| `maxVoltage`  | `int`    | Max voltage                                                                   | Yes       |
| `maxAmperage` | `int`    | Max amperage                                                                  | Yes       |

Expected HTTP response codes:

| Code  | Description                                                                        |
| ----- | ---------------------------------------------------------------------------------- |
| `201` | Pairing successfully created                                                       |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided |
| `404` | Invalid or expired pairing code                                                    |
| `409` | This pairing has already been created                                              |
| `5xx` | Internal server error occurred, client is advised to retry the request later       |

### Pairing initiated by CSMES

In this case the user enters a *pairing code* provided by the CSMES into the CSMEC system in order to establish a charging station pairing to CSMEC.

```mermaid
sequenceDiagram
    actor User
    participant CSMES as CSME Service
    participant CSMEC as CSME Client
    User->>+CSMES: Generate pairing code
    CSMES-->>-User: Pairing code
    User->>+CSMEC: Pair a charging station
    CSMEC->>+CSMES: Confirm pairing<br/>POST /1/pairings
    CSMES-->>-CSMEC: Pairing confirmed<br/>(charging station details)
    CSMEC-->>-User: Pairing confirmed
```

The CSMEC will initiate a request to the CSMES as follows:

#### `POST /1/pairings` (CSMES API)

Sample request body:

```json
{
  "pairingCode": "4A6BH28"
}
```

Sample response body:

```json
{
  "id": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
  "pairingCode": "4A6BH28",
  "chargingStation": {
    "evses": [
      {
        "evseId": 1,
        "capabilities": ["CHARGING_PROFILE_CAPABLE"],
        "connectors": [
          {
            "id": 1,
            "powerType": "AC_3_PHASE",
            "maxVoltage": 230,
            "maxAmperage": 16
          }
        ]
      }
    ]
  }
}
```

Refer to previous section for field descriptions.

Expected HTTP response codes:

| Code  | Description                                                                        |
| ----- | ---------------------------------------------------------------------------------- |
| `201` | Pairing successfully created                                                       |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided |
| `404` | Invalid or expired pairing code                                                    |
| `409` | This pairing has already been created                                              |
| `5xx` | Internal server error occurred, client is advised to retry the request later       |

### Unpairing initiated by CSMEC

#### `DELETE /1/pairings/:pairingId` (CSMES API)

Expected HTTP response codes:

| Code  | Description                                                                        |
| ----- | ---------------------------------------------------------------------------------- |
| `204` | Pairing successfully deleted                                                       |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided |
| `404` | Pairing not found                                                                  |
| `5xx` | Internal server error occurred, client is advised to retry the request later       |

### Unpairing initiated by CSMES

#### `DELETE /1/csmec/pairings/:pairingId` (CSMEC API)

Expected HTTP response codes:

| Code  | Description                                                                        |
| ----- | ---------------------------------------------------------------------------------- |
| `204` | Pairing successfully deleted                                                       |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided |
| `404` | Pairing not found                                                                  |
| `5xx` | Internal server error occurred, client is advised to retry the request later       |

### Sending telemetry to CSMEC

Once pairing is completed, the CSMES will forward relevant telemetry information to the CSMEC.

```mermaid
sequenceDiagram
  participant CS as Charging Station
  participant CSMES as CSME Service
  participant CSMEC as CSME Client
  CS->>CSMES: OCPP Commands
  CSMES->>+CSMEC: Send telemetry for relevant commands<br/>POST /1/csmec/messages
  CSMEC-->>-CSMES: A 200 response
```

The CSMES will send the telemetry as follows:

#### `POST /1/csmec/messages` (CSMEC API)

Due to potentially high volume of messages from charging stations they will be aggregated and sent to CSMEC in batches, thus each request body will contain one or more messages.

Please note that frequency and contents of some messages that are generated by a charging station are subject to the charging station configuration.

Sample request body:

```json
{
  "messages": [
    {
      "pairingId": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
      "protocol": "ocpp2.0.1",
      "type": "TransactionEvent",
      "payload": {
        // JSON-formatted object specific to protocol and type
        // In this example, `TransactionEvent`
        "eventType": "Ended",
        "evse": {
          "connectorId": 1,
          "id": 1
        },
        "meterValue": [
          {
            "sampledValue": [
              {
                "context": "Transaction.Begin",
                "unitOfMeasure": {
                  "unit": "kWh"
                },
                "value": 88.643
              }
            ],
            "timestamp": "2024-01-26T16:40:38Z"
          },
          {
            "sampledValue": [
              {
                "context": "Transaction.End",
                "unitOfMeasure": {
                  "unit": "kWh"
                },
                "value": 88.663
              }
            ],
            "timestamp": "2024-01-26T16:41:26Z"
          }
        ],
        "seqNo": 6,
        "timestamp": "2024-01-26T16:41:26Z",
        "transactionInfo": {
          "chargingState": "Charging",
          "stoppedReason": "Remote",
          "transactionId": "d4c36b70-31fa-44d2-87d3-a5105150d5cd"
        },
        "triggerReason": "EVCommunicationLost"
      },
      "errorPayload": null,
      // Contains the complete response payload from CSMS for successful calls
      "csmsResponsePayload": {
        "idTokenInfo": {
          "status": "Accepted"
        }
      },
      // Contains error payload for failed calls (null when call succeeded)
      "csmsErrorPayload": null
    },
    {
      // Another message - example of a failed call
      "pairingId": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
      "protocol": "ocpp2.0.1",
      "type": "TransactionEvent",
      "payload": {
        "eventType": "Ended",
        "seqNo": 7,
        "timestamp": "2024-01-26T16:45:00Z",
        "transactionInfo": {
          "transactionId": "d4c36b70-31fa-44d2-87d3-a5105150d5cd"
        },
        "triggerReason": "StoppedByEV"
      },
      "errorPayload": null,
      // Null when call failed
      "csmsResponsePayload": null,
      // Contains error details for failed calls
      "csmsErrorPayload": {
        "errorCode": "InternalError",
        "errorDescription": "Database connection failed"
      }
    },
    {
      // Another message - example of a charging station's answer to a GetConfiguration command
      "pairingId": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
      "protocol": "ocpp1.6",
      "type": "GetConfiguration:Response",
      "payload": {
        "configurationKey": [
          { "key": "BoPenabled", "readonly": false, "value": "true" }
        ],
        "unknownKey": []
      },
      // Set instead of `payload` if the charging station rejected the command
      "errorPayload": null,
      // Always null on a `:Response` message
      "csmsResponsePayload": null,
      "csmsErrorPayload": null
    }
  ]
}
```

Sample response body. The response should contain information about the successful acceptance or rejection of each message:

```json
{
  "messages": [
    { "success": true },
    {
      "success": false,
      "error": { "code": "BAD_REQUEST", "message": "invalid protocol version" }
    }
  ]
}
```

Message object:

| Name                  | Type     | Description                                                                                                               | Required? |
| --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- | --------- |
| `pairingId`           | `UUID`   | Pairing ID                                                                                                                | Yes       |
| `protocol`            | `string` | Protocol type, one of: `ocpp1.5`, `ocpp1.6`, `ocpp2.0.1`, `ocpp2.1`                                                       | Yes       |
| `type`                | `string` | Message type, specific to the `protocol` used, e.g.: `MeterValues`, `TransactionEvent`, `GetConfiguration:Response`, etc. | Yes       |
| `payload`             | `object` | The actual message body that's coming from a charging station (null when the charging station rejected a command)         | No        |
| `errorPayload`        | `object` | Error details when the charging station rejected a command (null otherwise)                                               | No        |
| `csmsResponsePayload` | `object` | The complete CSMS response payload for successful calls (null for failed calls)                                           | No        |
| `csmsErrorPayload`    | `object` | Error details for failed calls (null for successful calls)                                                                | No        |

Currently, CSME supports forwarding telemetry as raw OCPP messages. Learn more about the protocols on [OCA website](https://openchargealliance.org/protocols/open-charge-point-protocol/). Depending on value of `protocol` message `payload` will conform to either [OCPP 1.6 schemas](https://github.com/mobilityhouse/ocpp/tree/master/ocpp/v16/schemas) or [OCPP 2.0.1 schemas](https://github.com/mobilityhouse/ocpp/tree/master/ocpp/v201/schemas).

The `csmsResponsePayload` field contains the complete response that the CSMS (Charging Station Management System) sent back to the charging station for the corresponding request. For example, when a charging station sends a `TransactionEvent`, the `csmsResponsePayload` will contain the CSMS's response that may contain `idTokenInfo`, `transactionId` (for OCPP 1.6), or other relevant response data. This enables CSME clients to have full context of the charging station communication, including both the original request and the CSMS response.

A charging station's answer to a `GetConfiguration` command arrives as a message of its own, with a `type` of `GetConfiguration:Response`. Its `payload` is the answer. If the charging station rejected the command, `payload` is `null` and the error is in `errorPayload`. `csmsResponsePayload` and `csmsErrorPayload` are always `null` on this message. No other command's answer is forwarded.

The configuration a `GetBaseReport` command asks for arrives in `NotifyReport` messages, which carry the `requestId` the command was sent with.

Both are forwarded only when enabled for the CSMEC, as `GetConfiguration:Response` and `NotifyReport`. They go to every CSMEC paired to the charging station, not only the one that sent the command.

The `csmsErrorPayload` field contains error details including error code and description in case of an error during the handling of OCPP message by CSMS. In this case `csmsResponsePayload` will be `null`.

#### Error Payload Structure

The `csmsErrorPayload` and `errorPayload` object structure for failed calls typically includes:

```json
{
  "errorCode": "string", // OCPP-defined error code (e.g., "InternalError", "NotSupported")
  "errorDescription": "string" // Human-readable error description
}
```

Common error codes that may appear in `csmsErrorPayload` (for the complete list refer to the OCPP spec):

- `InternalError`: Server-side processing error
- `NotSupported`: The requested operation is not supported
- `SecurityError`: Security validation failed
- `FormationViolation`: Request format validation failed
- `PropertyConstraintViolation`: Property value constraint violated
- `OccurrenceConstraintViolation`: Required property missing or unexpected property present

Expected HTTP response codes:

| Code  | Description                                                                                                                                   |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Request successfully processed. Note that due to batch nature of this endpoint individual messages could still fail, refer to the table below |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided                                                            |
| `5xx` | Internal server error occurred, client is advised to retry the request later                                                                  |

Every message delivery status will contain an `error` object in case when there was a failure with its processing. The following table describes possible values of the error object's `code` attribute:

| Code               | Description                                               |
| ------------------ | --------------------------------------------------------- |
| `BAD_REQUEST`      | Message payload or protocol is invalid                    |
| `ENTITY_NOT_FOUND` | Pairing not found                                         |
| `ACCESS_DENIED`    | Client has no access to requested pairing                 |
| `INTERNAL_ERROR`   | Internal server error, client may retry the message later |

### Receiving commands from CSMEC

The CSMEC will send commands to CSMES in order to control charging process.

```mermaid
sequenceDiagram
    participant CS as Charging Station
    participant CSMES as CSME Service
    participant CSMEC as CSME Client
    CSMEC->>+CSMES: Send command to charging station<br/>POST /1/messages
    CSMES->>+CS: OCPP Command
    CS-->>-CSMES: Confirm reception
    CSMES-->>-CSMEC: A 200 response
```

#### `POST /1/messages` (CSMES API)

A charging station answers a command asynchronously, so the response to this call only confirms that the command was delivered.

Sample request body:

```json
{
  "messages": [
    {
      "pairingId": "045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe",
      "protocol": "ocpp2.0.1",
      "type": "SetChargingProfile",
      "payload": {
        // JSON-formatted object specific to protocol and type
        // In this example, `SetChargingProfile`
        "evseId": 1,
        "chargingProfile": {
          "id": 1,
          "stackLevel": 1,
          "chargingProfilePurpose": "TxProfile",
          "chargingProfileKind": "Absolute",
          "chargingSchedule": [
            {
              "id": 1,
              "chargingRateUnit": "W",
              "chargingSchedulePeriod": [{ "startPeriod": 0, "limit": 11000 }]
            }
          ],
          "validTo": "2024-01-16T15:09:15Z",
          "transactionId": "4609d4ce-1c7c-4e31-be54-63c6d1541e6c"
        }
      }
    }
  ]
}
```

Sample response body. The response will contain information about the successful acceptance or rejection of each message:

```json
{
  "messages": [
    { "success": true },
    {
      "success": false,
      "error": { "code": "BAD_REQUEST", "message": "invalid protocol version" }
    }
  ]
}
```

Expected HTTP response codes:

| Code  | Description                                                                                                                                   |
| ----- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `200` | Request successfully processed. Note that due to batch nature of this endpoint individual messages could still fail, refer to the table below |
| `401` | Request is unauthorized, ensure that valid authentication credentials are provided                                                            |
| `5xx` | Internal server error occurred, client is advised to retry the request later                                                                  |

Every message delivery status will contain an `error` object in case when there was a failure with its processing. The following table describes possible values of the error object's `code` attribute:

| Code                  | Description                                                                                    |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| `BAD_REQUEST`         | Message payload or protocol is invalid                                                         |
| `ENTITY_NOT_FOUND`    | Pairing not found                                                                              |
| `ACCESS_DENIED`       | Client has no access to requested pairing                                                      |
| `SERVICE_UNAVAILABLE` | Charging station is unavailable (temporarily disconnected), client may retry the message later |
| `INTERNAL_ERROR`      | Internal server error, client may retry the message later                                      |

## Environments

Road exposes 2 CSME environments: staging and production. These are the base URLs of them:

- Staging: https://csme.public.road.dev
- Production: https://csme.road.io

## Authentication and authorisation

CSME exchanges data between two parties, so every request is authenticated on both sides. This section covers the authentication methods CSME supports on each side.

### CSME service

CSMEC can use pre-shared secret token to authenticate against CSMES. The value of secret token `CSMES_TOKEN` should be passed in `Authorization` header, e.g.:

```shell
curl -XPOST \
  -H "Authorization: Bearer $CSMES_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"pairingCode": "4A6BH28"}' \
  https://csme.road.io/1/pairings
```

### CSME client

CSME Service supports 2 authentication methods when calling CSME Client API endpoints.

#### OAuth2 client credentials flow

For this method CSMEC needs to provide `client ID`, `client secret` and token URL. Access token `CSMEC_TOKEN` acquired by CSMES will be passed in `Authorization` header with every request to CSMEC, e.g.:

```shell
curl -XDELETE \
  -H "Authorization: Bearer $CSMEC_TOKEN" \
  https://api.provider.com/1/csmec/pairings/045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe
```

#### Pre-shared API key

Alternative authentication method is a pre-shared API key `CSMEC_API_KEY` that is passed in `x-api-key` header, e.g.:

```shell
curl -XDELETE \
  -H "X-Api-Key: $CSMEC_API_KEY" \
  https://api.provider.com/1/csmec/pairings/045a6ffd-b16e-48fa-ab67-86ca4ccf8ffe
```
