OCPP 2.0.1 Edition 4 Section H

Reservation Flows - CSMS Developer Guide

Based on OCPP 2.0.1 Edition 4 Specification (Part 2, Section H, pages 204-215). This guide covers all reservation flows from the CSMS (Charging Station Management System) perspective.

6 Sections
4 Use Cases
H01 - H04

1. Overview

Introduction

The Reservation functional block enables an EV Driver to reserve an EVSE at a Charging Station until a certain expiry time, ensuring it cannot be occupied by another user. The CSMS is responsible for initiating reservations, cancelling them, and tracking their lifecycle.

Key Concepts

Specific EVSE Reservation

Reserve a particular EVSE by its ID. The CS reports connector status as Reserved for all connectors of that EVSE.

Unspecified EVSE Reservation

Reserve any available EVSE at the station. The CS internally ensures one EVSE remains available for the reserved IdToken. Requires ReservationNonEvseSpecific = true.

Connector Type Reservation

Reserve any EVSE that has a specific connector type (e.g., cCCS2). Sent without evseId but with connectorType.

Reservation ID

A unique integer assigned by the CSMS. Used to identify, cancel, replace, or consume a reservation. The same ID can be reused to replace an existing reservation.

Only available EVSEs can be reserved. The CS cannot know when an occupied EVSE will become free. Reservations are associated with an IdToken and optionally a groupIdToken, allowing any member of the group to use the reservation.

Message Direction Summary

Message Direction Initiated By
ReserveNowRequest CSMS -> CS CSMS
ReserveNowResponse CS -> CSMS CS (response)
CancelReservationRequest CSMS -> CS CSMS
CancelReservationResponse CS -> CSMS CS (response)
ReservationStatusUpdateRequest CS -> CSMS CS
ReservationStatusUpdateResponse CSMS -> CS CSMS (response)

2. OCPP Messages Summary

Protocol

2.1 ReserveNowRequest (CSMS -> CS)

OCPP Action: "ReserveNow". Sent by the CSMS to reserve an EVSE at a Charging Station.

CSMS --> Charging Station
Field Type Required Description
id integer Yes Unique reservation ID assigned by CSMS
expiryDateTime dateTime (RFC 3339) Yes When the reservation expires
idToken IdTokenType Yes The IdToken for which this reservation is made
evseId integer No Specific EVSE to reserve. If omitted, CS picks any available EVSE
connectorType ConnectorEnumType No Reserve for a specific connector type (only when evseId is omitted)
groupIdToken IdTokenType No Group token — any member of the group can use the reservation
ReserveNowRequest Example
{
  "id": 42,
  "expiryDateTime": "2025-12-15T14:30:00Z",
  "idToken": {
    "idToken": "AABBCCDD",
    "type": "ISO14443"
  },
  "evseId": 1,
  "groupIdToken": {
    "idToken": "GROUP001",
    "type": "Central"
  }
}

2.2 ReserveNowResponse (CS -> CSMS)

Charging Station --> CSMS
Field Type Required Description
status ReserveNowStatusEnumType Yes Result of the reservation attempt
statusInfo StatusInfoType No Additional status details
Status Value Meaning
Accepted Reservation was accepted
Faulted All targeted EVSEs are faulted
Occupied All targeted EVSEs are Reserved or Occupied
Rejected CS is configured not to accept reservations, or other rejection
Unavailable All targeted EVSEs are Unavailable

2.3 CancelReservationRequest (CSMS -> CS)

OCPP Action: "CancelReservation". Sent by the CSMS to cancel an existing reservation.

CSMS --> Charging Station
Field Type Required Description
reservationId integer Yes ID of the reservation to cancel
CancelReservationRequest Example
{
  "reservationId": 42
}

2.4 CancelReservationResponse (CS -> CSMS)

Charging Station --> CSMS
Status Value Meaning
Accepted Reservation was successfully canceled
Rejected No matching reservationId found

2.5 ReservationStatusUpdateRequest (CS -> CSMS)

OCPP Action: "ReservationStatusUpdate". Sent by the CS when a reservation expires or is removed due to EVSE issues.

Charging Station --> CSMS
Field Type Required Description
reservationId integer Yes The reservation ID
reservationUpdateStatus ReservationUpdateStatusEnumType Yes Updated status
Status Value Meaning
Expired Reservation expired (expiryDateTime reached)
Removed Reservation was removed (EVSE became Faulted or Unavailable)
ReservationStatusUpdateRequest Example
{
  "reservationId": 42,
  "reservationUpdateStatus": "Expired"
}

2.6 ReservationStatusUpdateResponse (CSMS -> CS)

Empty response body — no fields. Just acknowledges receipt.

CSMS --> Charging Station
ReservationStatusUpdateResponse Example
{}

3. Key Data Types & Schemas

Reference

3.1 Shared Types

IdTokenType

Field Type Required Description
idToken string (max 36) Yes The identifier (e.g. RFID UID, UUID)
type IdTokenEnumType Yes Type of identifier
additionalInfo AdditionalInfoType[] No Additional authorization info

IdTokenEnumType values: Central, eMAID, ISO14443, ISO15693, KeyCode, Local, MacAddress, NoAuthorization

StatusInfoType

Field Type Required Description
reasonCode string (max 20) Yes Predefined reason code (case-insensitive)
additionalInfo string (max 512) No Human-readable details

ConnectorEnumType

Values: cCCS1, cCCS2, cG105, cTesla, cType1, cType2, s309-1P-16A, s309-1P-32A, s309-3P-16A, s309-3P-32A, sBS1361, sCEE-7-7, sType2, sType3, Other1PhMax16A, Other1PhOver16A, Other3Ph, Pan, wInductive, wResonant, Undetermined, Unknown

3.2 Rust Types (rust-ocpp v2.0.0)

Reservation Rust Types
// --- ReserveNow ---
pub struct ReserveNowRequest {
    pub id: i32,
    pub expiry_date_time: DateTime<Utc>,       // chrono
    pub connector_type: Option<ConnectorEnumType>,
    pub evse_id: Option<i32>,
    pub id_token: IdTokenType,
    pub group_id_token: Option<IdTokenType>,
}

pub struct ReserveNowResponse {
    pub status: ReserveNowStatusEnumType,
    pub status_info: Option<StatusInfoType>,
}

pub enum ReserveNowStatusEnumType {
    Accepted, Faulted, Occupied, Rejected, Unavailable,
}

// --- CancelReservation ---
pub struct CancelReservationRequest {
    pub reservation_id: i32,
}

pub struct CancelReservationResponse {
    pub status: CancelReservationStatusEnumType,
    pub status_info: Option<StatusInfoType>,
}

pub enum CancelReservationStatusEnumType {
    Accepted, Rejected,
}

// --- ReservationStatusUpdate ---
pub struct ReservationStatusUpdateRequest {
    pub reservation_id: i32,
    pub reservation_update_status: ReservationUpdateStatusEnumType,
}

pub struct ReservationStatusUpdateResponse {}  // empty

pub enum ReservationUpdateStatusEnumType {
    Expired, Removed,
}

4. Reservation Flows

Use Cases H01-H04
H01

Reservation

Ensure the EV Driver can charge at a Charging Station by making a reservation until a certain expiry time.

Actors: Charging Station, CSMS, EV Driver

Prerequisite: The Charging Station has at least one available EVSE. There are three reservation scenarios depending on whether a specific EVSE, connector type, or any EVSE is requested.

S1 - Reserve an unspecified EVSE at a Charging Station

The EV Driver wants to reserve any EVSE at the station (no specific EVSE preference).

Flow:

  1. EV Driver asks the CSMS to reserve an unspecified EVSE
  2. CSMS sends ReserveNowRequest WITHOUT evseId to the Charging Station
  3. CS responds with ReserveNowResponse with status Accepted
  4. (Optional) CSMS notifies the EV Driver
Sequence Diagram - S1
EV Driver        CSMS                    Charging Station
    |               |                           |
    |--reserve----->|                           |
    |               |--ReserveNowRequest------->|
    |               |  (no evseId)              |
    |               |<--ReserveNowResponse------|
    |               |  (status=Accepted)        |
    |<--notify------|                           |

CSMS Action:

  • Generate a unique integer reservation ID
  • Set reservation expiry timestamp
  • Include the EV Driver's IdToken
  • Do NOT set evseId
  • Optionally set connectorType if the EV Driver has a preference
  • Optionally set groupIdToken if group reservation
S2 - Reserve a specific EVSE at a Charging Station

The EV Driver wants a specific EVSE.

Flow:

  1. EV Driver asks the CSMS to reserve a specific EVSE
  2. CSMS sends ReserveNowRequest WITH evseId to the Charging Station
  3. CS responds with ReserveNowResponse with status Accepted
  4. CS sends StatusNotificationRequest with status Reserved for all Connectors of that EVSE
  5. CSMS responds with StatusNotificationResponse to the CS
Sequence Diagram - S2
EV Driver        CSMS                    Charging Station
    |               |                           |
    |--reserve----->|                           |
    |               |--ReserveNowRequest------->|
    |               |  (evseId=1)               |
    |               |<--ReserveNowResponse------|
    |               |  (status=Accepted)        |
    |<--notify------|                           |
    |               |<--StatusNotificationReq---|
    |               |  (status=Reserved)        |
    |               |--StatusNotificationResp-->|

Alternative: Instead of StatusNotificationRequest, the CS can send a NotifyEventRequest with trigger=Delta, component.name="Connector", variable="AvailabilityState", actualValue="Reserved".

S3 - Reserve a connector type at a Charging Station

The EV Driver wants to reserve any EVSE that has a specific connector type.

Flow:

  1. EV Driver asks the CSMS to reserve a connector type
  2. CSMS sends ReserveNowRequest with connectorType specified AND no evseId
  3. CS responds with ReserveNowResponse with status Accepted
Sequence Diagram - S3
EV Driver        CSMS                    Charging Station
    |               |                           |
    |--reserve----->|                           |
    |               |--ReserveNowRequest------->|
    |               |  (connectorType=cCCS2,    |
    |               |   no evseId)              |
    |               |<--ReserveNowResponse------|
    |               |  (status=Accepted)        |
    |<--notify------|                           |

Requirements (CSMS-relevant):

ID Requirement
H01.FR.01 If CS returns Rejected — the CS is configured not to accept reservations. CSMS should record this and inform the user.
H01.FR.02 If sending a ReserveNowRequest with the same id as an existing reservation, the CS will replace the old reservation. Use this for updating reservations.
H01.FR.03 If the id does not match an existing reservation, CS returns Accepted if it succeeds in reserving an EVSE.
H01.FR.04 When sending WITHOUT evseId and at least one EVSE is Available (and config ReservationNonEvseSpecific is true), the CS accepts the reservation.
H01.FR.06 When sending with a connectorType and at least one EVSE with that connector type is Available (and config ReservationNonEvseSpecific is true), the CS accepts.
H01.FR.07 After CS accepts a reservation without evseId, it ensures one EVSE remains available for the reserved IdToken during the validity period.
H01.FR.09 After CS accepts a reservation with connectorType, it ensures one Connector with that type remains available for the reserved IdToken.
H01.FR.11 If all targeted EVSEs are Reserved or Occupied, CS returns Occupied.
H01.FR.12 If all targeted EVSEs are Faulted, CS returns Faulted.
H01.FR.14 If all targeted EVSEs are Unavailable, CS returns Unavailable.
H01.FR.15 When a transaction starts for the reserved IdToken, the CS includes the reservationId in TransactionEventRequest. The CSMS must check this field to know the reservation is consumed.
H01.FR.16 When a targeted EVSE becomes Faulted, the CS cancels the reservation and sends ReservationStatusUpdate with status Removed. CSMS must handle this.
H01.FR.17 When a targeted EVSE becomes Unavailable, the CS cancels the reservation and sends ReservationStatusUpdate with status Removed. CSMS must handle this.
H01.FR.18 CS only accepts unspecified-EVSE reservations if configuration variable ReservationNonEvseSpecific = true.
H01.FR.19 CS rejects unspecified-EVSE reservations if ReservationNonEvseSpecific is false or not set.
H01.FR.20 When an unspecified reservation is accepted AND #available EVSEs == #reservations, the CS sends StatusNotificationRequest with connectorStatus = Reserved for all connectors of the last EVSE. CSMS must update its connector status tracking.
H01.FR.23 When a specific-EVSE reservation is accepted, the CS sends connector status Reserved for all connectors of that EVSE. CSMS must update connector status tracking.
H01.FR.24 When a connectorType reservation is accepted AND #reservations for that type == #available EVSEs with that type, connector status Reserved is reported. CSMS must update its tracking.

CSMS Implementation Checklist:

  1. Generate unique reservation IDs (integers) — store in database
  2. Build and send ReserveNowRequest via CSMS->CS CALL message
  3. Handle ReserveNowResponse: Accepted -> Store reservation in DB; Faulted/Occupied/Unavailable -> Inform user; Rejected -> CS doesn't support reservations
  4. Handle incoming StatusNotificationRequest with connectorStatus = Reserved — update connector status in DB
  5. Handle incoming NotifyEventRequest with AvailabilityState = Reserved — update connector status in DB
  6. Handle TransactionEventRequest with reservationId — mark reservation as consumed/completed
  7. Handle ReservationStatusUpdate with Removed — mark reservation as removed (EVSE fault/unavailable)
  8. It is RECOMMENDED to validate the IdToken with an AuthorizeRequest after receiving ReserveNowRequest and before the start of the transaction
H02

Cancel Reservation

Cancel a reservation on a Charging Station.

Actors: Charging Station, CSMS, EV Driver

Prerequisite: The Functional Block Reservation is installed; EV Driver has a reservation at the Charging Station.

Flow:

  1. EV Driver asks the CSMS to cancel a reservation
  2. CSMS sends CancelReservationRequest with the reservationId to the Charging Station
  3. If the CS has a matching reservation, it returns Accepted
  4. If needed, the CS sends StatusNotificationRequest with status Available or NotifyEventRequest with AvailabilityState = Available for all Connectors of the EVSE(s) that became available
  5. CSMS responds with StatusNotificationResponse or NotifyEventResponse
  6. The reservation is canceled
Sequence Diagram - Cancel Reservation
User             CSMS                    Charging Station
  |                |                           |
  |--cancel req--->|                           |
  |                |--CancelReservationReq---->|
  |                |  (reservationId=42)       |
  |                |<--CancelReservationResp---|
  |                |  (status=Accepted)        |
  |                |                           |
  |                |<--StatusNotificationReq---|
  |                |  (status=Available)       |
  |                |--StatusNotificationResp-->|

Remark: The CS does NOT send a ReservationStatusUpdate because the cancellation was explicitly initiated by the CSMS — it is already aware.

Requirements (CSMS-relevant):

ID Requirement
H02.FR.01 If the CS has no matching reservationId, it returns Rejected. CSMS should handle this gracefully (stale reservation).
H02.FR.02 If the CS finds a valid reservationId, the reservation is canceled. CSMS should mark reservation as canceled in DB.
H02.FR.03 If a specific EVSE was reserved, the CS allows charging on that EVSE again.
H02.FR.04 The CS sends StatusNotificationRequest(Available) or NotifyEventRequest(AvailabilityState=Available) for each connector of the freed EVSE. CSMS must update connector statuses.
H02.FR.05 If no specific EVSE was reserved (unspecified reservation), the CS allows charging on all EVSEs that were not explicitly reserved.
H02.FR.06 If before cancelling, the number of reserved EVSEs equaled the number of available EVSEs (all were reported as Reserved), after cancellation the CS sends connector status Available for all connectors of EVSEs that were not explicitly reserved. CSMS must update connector statuses.

CSMS Implementation Checklist:

  1. Look up the reservation in the database to find the charge_point_id and reservation_id
  2. Send CancelReservationRequest via CSMS->CS CALL message with reservationId
  3. Handle CancelReservationResponse: Accepted -> Mark reservation as canceled in DB, inform user; Rejected -> Reservation not found on CS (may already be expired/used), reconcile DB state.
  4. Handle incoming StatusNotificationRequest / NotifyEventRequest with connector status changing from Reserved -> Available — update connector status in DB
H03

Use a Reserved EVSE

Use a reserved EVSE — covers how a reserved EVSE is used based on IdToken and GroupIdToken information.

Actors: Charging Station, CSMS, EV Driver

The CSMS role is mostly passive here — the CS handles matching internally. The CSMS participates when the CS sends an AuthorizeRequest or TransactionEventRequest.

S1 - Use EVSE reserved for this IdToken (connector status = Reserved)
Variant A: IdToken presented first (TxStartPoint = "Authorized")

Flow:

  1. EV Driver presents an IdToken at the CS that matches the reservation's IdToken
  2. CS matches IdToken with the reservation
  3. Connector status becomes Available (reservation consumed)
  4. CS optionally sends AuthorizeRequest to CSMS — CSMS must respond with AuthorizeResponse
  5. If authorized (or authorization skipped), CS starts a transaction as in E03
  6. Connector status becomes Occupied when cable is connected
Sequence - IdToken First
EV Driver        CSMS                    Charging Station
    |               |                    [Connector Reserved
    |               |                     for TOKEN_A]
    |--present      |                           |
    |  TOKEN_A----->|                           |
    |               |                           |
    |           [opt: AuthorizeRequest]         |
    |               |<--AuthorizeRequest--------|
    |               |  (idToken=TOKEN_A)        |
    |               |--AuthorizeResponse------->|
    |               |  (status=Accepted)        |
    |               |                           |
    |               |    [Continue regular      |
    |               |     charging session]     |
Variant B: Cable plugged in first (TxStartPoint = "EVConnected")

Flow:

  1. EV Driver connects the cable
  2. CS starts a transaction, but EVSE connector status remains Reserved
  3. EV Driver presents IdToken matching the reservation
  4. CS matches IdToken — reservation consumed
  5. Connector status becomes Occupied
  6. CS optionally sends AuthorizeRequest — CSMS responds
  7. Transaction starts as in E02 (Cable Plugin First)
S2 - Use EVSE reserved for a GroupIdToken (connector status = Reserved)

When the reservation has a groupIdToken, any member of the group can use the reservation.

Variant A: IdToken presented first

Flow:

  1. EV Driver presents IdToken that is NOT the same as the reservation's idToken, but the reservation has a groupIdToken
  2. CS authorizes via AuthorizeRequest (Local Authorization List, Auth Cache, or sends to CSMS)
  3. CS checks if the groupIdToken of the presented IdToken matches the reservation's groupIdToken
  4. If groupIdTokens match — reservation consumed, connector becomes Available, then Occupied
  5. Transaction starts as in E03
Sequence - GroupIdToken Match
EV Driver        CSMS                    Charging Station
    |               |                    [Connector Reserved
    |               |                     for groupIdToken B]
    |--present      |                           |
    |  TOKEN_A----->|                           |
    |               |                           |
    |           [Lookup groupIdToken of TOKEN_A]|
    |               |<--AuthorizeRequest--------|
    |               |  (idToken=TOKEN_A)        |
    |               |--AuthorizeResponse------->|
    |               | (groupIdToken=TOKEN_B)    |
    |               |                           |
    |               |    [Continue regular       |
    |               |     transaction]           |

CSMS action for AuthorizeRequest: When the CSMS receives an AuthorizeRequest, it MUST return the groupIdToken in the AuthorizeResponse.idTokenInfo.groupIdToken field. This is how the CS determines if the presented IdToken belongs to the same group as the reservation.

Variant B: Cable plugged in first

Same logic but cable is connected before IdToken is presented.

S3 - Use reservation when connector is Available (unspecified EVSE, multiple available)

When the reservation was for an unspecified EVSE and multiple EVSEs are available, the connector status may still be Available (not Reserved). The flow is identical to S1 — the CS still matches the IdToken internally.

Requirements (CSMS-relevant):

ID Requirement
H03.FR.01 For reservation with specific evseId: CS allows charging on that EVSE when the presented IdToken matches. CSMS must respond to AuthorizeRequest if sent.
H03.FR.02 For reservation with specific connectorType: CS allows charging on an EVSE with that connector type when IdToken matches.
H03.FR.03 For reservation without evseId or connectorType: CS allows charging on any EVSE when IdToken matches.
H03.FR.04 If reservation has groupIdToken AND specific evseId: CS allows charging if presented IdToken matches either idToken OR associated groupIdToken. CSMS must return groupIdToken in AuthorizeResponse.
H03.FR.05 Same as FR.04 but for connectorType reservations.
H03.FR.06 Same as FR.04 but for unspecified reservations.
H03.FR.07 To determine the groupIdToken of a presented IdToken, the CS MAY look it up in its Local Authorization List or Authorization Cache.
H03.FR.08 If the IdToken is NOT in Local Auth List or Auth Cache, the CS sends AuthorizeRequest to the CSMS. The CSMS must return the associated groupIdToken in AuthorizeResponse.
H03.FR.09 When an idToken or groupIdToken is presented that matches a reservation, the CS considers the reservation consumed. CSMS knows the reservation is consumed when it receives a TransactionEventRequest with the reservationId.
H03.FR.10 After reservation is consumed: if connector had status Reserved and no cable is plugged in -> status becomes Available; if cable is plugged in -> status becomes Occupied. CSMS must handle these status updates.

CSMS Implementation Checklist:

  1. Handle AuthorizeRequest: When a CS sends an AuthorizeRequest, look up the IdToken and return the groupIdToken in the response. This is critical for group-based reservations.
  2. Handle TransactionEventRequest with reservationId: This indicates the reservation has been used. Mark the reservation as consumed/completed in the DB.
  3. Handle StatusNotificationRequest / NotifyEventRequest for connector status changes from Reserved -> Available -> Occupied — update tracking.
  4. If an IdToken does NOT match the reservation (and groupIdTokens don't match either), the CS will NOT change the connector status — it stays Reserved. A non-matching EV Driver is rejected.

Important Notes: If an IdToken does NOT match the reservation (and groupIdTokens don't match either), the IdToken is not authorized to charge on that reserved EVSE. If TxStartPoint = Authorized or PowerPathClosed: a transaction would NOT be started. If TxStartPoint = EVConnected or ParkingBayOccupancy: a transaction IS started (by cable plug-in), but charging would NOT start. The transaction would be ended at cable plug-out.

H04

Reservation Ended, Not Used

Notify the CSMS about a reservation that has expired without being used.

Actors: Charging Station, CSMS

This covers what happens when the expiryDateTime is reached and the EV Driver never used the reservation.

Flow:

  1. The Charging Station has a reservation
  2. The expiryDateTime is reached
  3. The CS removes the reservation
  4. If a specific EVSE was reserved: CS makes the EVSE available again and sends StatusNotificationRequest(Available) or NotifyEventRequest(AvailabilityState=Available) for all Connectors of that EVSE
  5. CSMS responds with StatusNotificationResponse or NotifyEventResponse
  6. CS sends ReservationStatusUpdateRequest with reservationUpdateStatus = Expired
  7. CSMS responds with ReservationStatusUpdateResponse (empty body)
Sequence Diagram - Reservation Expired
Charging Station                        CSMS
    |                                     |
    | [expiryDateTime reached]            |
    | [Reservation removed]               |
    |                                     |
    |  [if specific EVSE was reserved]    |
    |--StatusNotificationRequest--------->|
    |  (status=Available)                 |
    |<--StatusNotificationResponse--------|
    |                                     |
    |--ReservationStatusUpdateRequest---->|
    |  (reservationId=42,                 |
    |   reservationUpdateStatus=Expired)  |
    |<--ReservationStatusUpdateResponse---|
    |  (empty body)                       |

Remark: Use of StatusNotificationRequest instead of NotifyEventRequest is deprecated but still allowed.

Requirements (CSMS-relevant):

ID Requirement
H04.FR.01 When receiving ReservationStatusUpdateRequest with status Expired -> mark reservation as expired in DB. Respond with empty ReservationStatusUpdateResponse.
H04.FR.02 If a specific EVSE was reserved, the CS allows charging on that EVSE again. CSMS should reflect this.
H04.FR.03 The CS sends StatusNotificationRequest(Available) or NotifyEventRequest(AvailabilityState=Available) for each connector. CSMS must update connector statuses.

CSMS Implementation Checklist:

  1. Handle incoming ReservationStatusUpdateRequest: parse reservationId and reservationUpdateStatus. If Expired: mark reservation as expired in DB, optionally notify the EV Driver. If Removed: mark reservation as removed in DB (EVSE became Faulted/Unavailable), optionally notify the EV Driver. Always respond with empty ReservationStatusUpdateResponse {}.
  2. Handle incoming StatusNotificationRequest with connector status changing from Reserved -> Available — update connector status in DB.
  3. Optional: Implement CSMS-side expiry tracking — if the CSMS tracks expiry times, it can proactively mark reservations as expired even before the CS notifies, and clean up stale reservations.

5. Database Schema Considerations

Storage

5.1 Suggested Reservations Table

SQL Schema
CREATE TABLE reservations (
    id              SERIAL PRIMARY KEY,        -- internal PK
    reservation_id  INTEGER NOT NULL,          -- OCPP reservation id (sent in ReserveNowRequest.id)
    charge_point_id UUID NOT NULL REFERENCES charge_points(id),
    evse_id         INTEGER,                   -- NULL for unspecified EVSE reservations
    connector_type  TEXT,                       -- NULL if not specified (ConnectorEnumType value)
    id_token        TEXT NOT NULL,              -- the reserved IdToken value
    id_token_type   TEXT NOT NULL,              -- IdTokenEnumType value
    group_id_token  TEXT,                       -- optional group IdToken value
    group_id_token_type TEXT,                   -- optional group IdTokenEnumType value
    expiry_date_time TIMESTAMPTZ NOT NULL,      -- when the reservation expires
    status          TEXT NOT NULL DEFAULT 'Active',  -- Active, Used, Expired, Canceled, Removed
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    UNIQUE(reservation_id, charge_point_id)    -- reservation_id is unique per charge point
);

CREATE INDEX idx_reservations_charge_point ON reservations(charge_point_id);
CREATE INDEX idx_reservations_status ON reservations(status);
CREATE INDEX idx_reservations_id_token ON reservations(id_token);
CREATE INDEX idx_reservations_expiry ON reservations(expiry_date_time)
    WHERE status = 'Active';

5.2 Reservation Status Lifecycle

Status State Machine
                     ┌──────────┐
        Create ────> │  Active   │
                     └─────┬────┘
                           │
              ┌────────────┼────────────┬──────────────┐
              ▼            ▼            ▼              ▼
         ┌────────┐  ┌──────────┐ ┌──────────┐  ┌──────────┐
         │  Used  │  │ Expired  │ │ Canceled │  │ Removed  │
         └────────┘  └──────────┘ └──────────┘  └──────────┘
         (H03/E*)    (H04-Expired) (H02)       (H01.FR.16/17)
Status Trigger Source
Active ReserveNowResponse(Accepted) CSMS creates reservation
Used TransactionEventRequest with reservationId CS consumed the reservation (H03)
Expired ReservationStatusUpdateRequest(Expired) CS reports expiry (H04)
Canceled CancelReservationResponse(Accepted) CSMS explicitly canceled (H02)
Removed ReservationStatusUpdateRequest(Removed) CS reports EVSE Faulted/Unavailable (H01.FR.16/17)

6. Configuration & Message Routing

Config

Configuration variables on the Charging Station that affect reservation behavior. The CSMS can read/write these via GetVariables/SetVariables messages.

6.1 ReservationCtrlr

Variable Type Description
NonEvseSpecific boolean If true, the CS accepts reservations where evseId is not specified. CSMS should query this variable (via GetVariables) to know if unspecified-EVSE reservations are supported.

The CSMS should check this configuration variable (via B06 - Get Variables flow) before attempting to send a ReserveNowRequest without evseId. If the variable is false or not present, the CS will reject unspecified-EVSE reservations.

6.2 Message Routing in ingest.rs

The following OCPP actions need to be routed in the message ingest handler:

CSMS-Initiated (Outgoing CALLs via ConnectionManager)

Action String Request Type Response Type Flow
"ReserveNow" ReserveNowRequest ReserveNowResponse H01
"CancelReservation" CancelReservationRequest CancelReservationResponse H02

CS-Initiated (Incoming CALLs handled by CSMS)

Action String Request Type Response Type Flow
"ReservationStatusUpdate" ReservationStatusUpdateRequest ReservationStatusUpdateResponse H04, H01

Note: StatusNotificationRequest and TransactionEventRequest (which carry reservation-related data) are already handled in other flows (G01, E01-E15) but need reservation-aware logic added.

TransactionEventRequest Integration

When handling TransactionEventRequest, check for the reservationId field. If present:

  • Look up the reservation in the database
  • Mark it as Used
  • This is how the CSMS knows a reservation was consumed (flow H03)

The reservationId field in TransactionEventRequest is an Option<i32> in rust-ocpp.

OCPP 2.0.1 Reservation Flows - CSMS Developer Guide. Based on OCPP 2.0.1 Edition 4 Specification (Part 2, Section H).