Session flows

Public API — Session Flows

How charging sessions are started, tracked, and stopped through the Public API, with sequence diagrams for the typical integrations

Session model

A session represents one charging transaction on a connector.

StatusMeaning
RESERVATIONCreated by a reservation, charging not started yet
ACTIVEVehicle is (or is about to start) charging
COMPLETEDTransaction finished; a CDR is produced afterwards
stateDiagram-v2
    [*] --> RESERVATION: reservation placed
    [*] --> ACTIVE: transaction started
    RESERVATION --> ACTIVE: driver plugs in / starts
    ACTIVE --> COMPLETED: stop command / cable unplugged / station stop
    COMPLETED --> [*]: CDR created

Key SessionDto fields: id, status, startDate/stopDate, meterStart/meterStop, totalEnergy, price (with tariffId), chargingPeriods[], the infrastructure path (chargingPoolIdchargingStationIdchargingPointIdconnectorId), tokenUid, and — for calibration-law markets — signed metering data (signedStartData, signedStopData, transparencyCode, ...).

Remote start

Starting a session is asynchronous: the API accepts the command, forwards it to the charging station over OCPP, and the session materializes when the station confirms. You get a command ID to poll.

Prefer the v2 commands API — identical behavior, plus ocppMessageId in every response for tracing through OCPP logs.

sequenceDiagram
    autonumber
    participant C as Your backend
    participant API as Public API
    participant CPO as CPO Backbone
    participant GW as Charger Gateway (OCPP)
    participant CS as Charging Station

    C->>API: POST /v2/commands/start-session<br/>{ connectorId, tokenUid }
    API->>CPO: create start-session command + PENDING session
    CPO->>GW: RemoteStart
    GW->>CS: RemoteStartTransaction (OCPP 1.6)<br/>RequestStartTransaction (OCPP 2.0.1)
    API-->>C: 200 { id, status: PENDING, sessionId: null, ocppMessageId }

    CS-->>GW: Accepted
    Note over CS: Driver plugs in,<br/>transaction starts
    CS-->>GW: StartTransaction / TransactionEvent(Started)
    GW-->>CPO: transaction started
    CPO->>CPO: session → ACTIVE

    loop until status is terminal
        C->>API: GET /v2/commands/start-session/{id}
        API-->>C: { id, status, sessionId, ocppMessageId }
    end
    C->>API: GET /v1/sessions/{sessionId}
    API-->>C: SessionDto (status: ACTIVE, meterStart, ...)

Request:

curl -s -X POST \
  -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{ "connectorId": "1e0b57e3-...", "tokenUid": "a81bc81b-..." }' \
  "https://<host>/api/v2/commands/start-session"

Response:

{
  "id": "cmd-550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "sessionId": null,
  "ocppMessageId": "550e8400-e29b-41d4-a716-446655440000"
}

Poll GET /api/v2/commands/start-session/{id} until sessionId is set (station accepted, session created) or the status reports failure. Failure causes include: connector occupied or offline, station rejecting the remote start, or token not authorized.

Remote stop

POST /commands/stop-session identifies the session by one of three selectors:

{ "chargingSessionId": "..." }

or { "chargingStationId": "..." } (stops the station's active session) or { "ocppTransactionId": "..." }.

sequenceDiagram
    autonumber
    participant C as Your backend
    participant API as Public API
    participant GW as Charger Gateway (OCPP)
    participant CS as Charging Station

    C->>API: POST /v2/commands/stop-session<br/>{ chargingSessionId }
    API->>GW: RemoteStop
    GW->>CS: RemoteStopTransaction / RequestStopTransaction
    API-->>C: 200 { ocppMessageId }
    CS-->>GW: StopTransaction / TransactionEvent(Ended)
    GW-->>API: transaction ended
    Note over API: session → COMPLETED,<br/>CDR created afterwards
    C->>API: GET /v1/sessions/{sessionId}
    API-->>C: SessionDto (status: COMPLETED, meterStop, price)

The v1 endpoint returns 204 No Content; v2 returns 200 with ocppMessageId. In both versions the actual stop is confirmed by the station asynchronously — verify via the session status or a webhook.

Tracking sessions: poll vs push

Two options, use one:

  1. PollGET /v1/sessions/{id} for a single session, or POST /v1/sessions/rsql-list for bulk reconciliation:

    { "filter": "status == 'ACTIVE'", "sort": "-startDate" }
  2. Push — register a SESSION webhook. It fires on session started, updated (e.g. new meter values / charging periods), and completed, delivering the full SessionDto each time.

sequenceDiagram
    participant CS as Charging Station
    participant CPO as CPO Backbone
    participant You as Your endpoint

    CS-->>CPO: MeterValues / TransactionEvent
    CPO->>CPO: update session
    CPO->>You: PUT {webhookUrl}/{sessionId} (SessionDto)
    Note over CPO,You: fire-and-forget, no retry —<br/>reconcile periodically via rsql-list

From session to CDR

When a session completes, the backbone produces a CDR (charge detail record) with the final billed cost — the CDR, not the live session price, is the billing source of truth.

  • Pull: GET /v1/cdrs, POST /v1/cdrs/rsql-list (permission cdr:read).
  • Push: register a CDR webhook — fires once per created CDR.

Related

  • Webhooks — registration and delivery contract


Did this page help you?