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.
| Status | Meaning |
|---|---|
RESERVATION | Created by a reservation, charging not started yet |
ACTIVE | Vehicle is (or is about to start) charging |
COMPLETED | Transaction 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 (chargingPoolId → chargingStationId → chargingPointId → connectorId), 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:
-
Poll —
GET /v1/sessions/{id}for a single session, orPOST /v1/sessions/rsql-listfor bulk reconciliation:{ "filter": "status == 'ACTIVE'", "sort": "-startDate" } -
Push — register a
SESSIONwebhook. It fires on session started, updated (e.g. new meter values / charging periods), and completed, delivering the fullSessionDtoeach 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(permissioncdr:read). - Push: register a
CDRwebhook — fires once per created CDR.
Related
- Webhooks — registration and delivery contract
Updated 7 days ago