OCPI COMMAND · UPDATED 11 AUG 2026

START_SESSION: remote start over OCPI

A driver taps “Start charging” in an eMSP app and electrons flow from a CPO's charger seconds later. Between the tap and the current sits the OCPI Commands module — and three network hops that can each fail independently.

PART OF THE OCPI 2.2.1 COMPLETE GUIDE · 15 MODULE & COMMAND GUIDES

IN ONE PARAGRAPH

START_SESSION is the most-used command in OCPI and the one with the most failure modes, because success depends on a chain reaching physical hardware. The immediate response only confirms the request was queued. Treating it as confirmation that charging began is the defect behind most remote-start support tickets.

The three-step async flow

Three parties, three hops, and every one can fail on its own:

  1. eMSP to CPO — the OCPI command, answered immediately with a CommandResponse.
  2. CPO to charger — an OCPP RemoteStartTransaction (1.6J) or RequestStartTransaction (2.0.1) over the charger's live WebSocket.
  3. Back up the chain — the charger answers, the CPO POSTs a CommandResult to your response_url, and a Session object appears separately.

The Session is what actually confirms charging. The CommandResult confirms the charger accepted the instruction; the Session confirms a charge exists. They are different facts and they arrive separately.

The fields

FieldRequiredNotes
response_urlYesWhere the CommandResult is POSTed
tokenYesThe full Token object — not just a uid
location_idYesThe site
evse_uidNoOmit and the CPO chooses an available EVSE
connector_idNoOnly meaningful with evse_uid
authorization_referenceNoEchoed onto the Session and CDR — always send it

Sending the whole Token object rather than a reference is deliberate: the CPO may need to authorise without being able to reach you, so it needs the token's valid and whitelist state in hand.

On EVSE targeting: omitting evse_uid lets the CPO pick, which raises success rates. But drivers are standing at a specific charger, so in practice you almost always want to name it — a driver at bay A2 does not want the CPO starting bay A7.

Example: the full exchange

POST /ocpi/2.2.1/commands/START_SESSION
Authorization: Token <base64 token>
OCPI-from-country-code: IN
OCPI-from-party-id: EMS
OCPI-to-country-code: IN
OCPI-to-party-id: EFI
X-Request-ID: 7c1a4e90
X-Correlation-ID: booking-88231

{
  "response_url": "https://emsp.example.com/ocpi/2.2.1/commands/START_SESSION/7c1a4e90",
  "token": {
    "country_code": "IN", "party_id": "EMS",
    "uid": "DR-88231", "type": "APP_USER",
    "contract_id": "IN-EMS-C0142",
    "issuer": "Example Mobility Services",
    "valid": true, "whitelist": "ALLOWED",
    "last_updated": "2026-08-11T08:55:00Z"
  },
  "location_id": "LOC-ND-014",
  "evse_uid": "EVSE-014-02",
  "authorization_reference": "auth-4471"
}

The immediate answer — queued, not started:

HTTP/1.1 200 OK

{
  "data": { "result": "ACCEPTED", "timeout": 30 },
  "status_code": 1000,
  "timestamp": "2026-08-11T09:11:59Z"
}

Then, once the charger has responded over OCPP:

POST /ocpi/2.2.1/commands/START_SESSION/7c1a4e90
X-Correlation-ID: booking-88231

{ "result": "ACCEPTED" }

And separately, through the Sessions module, the object that actually proves a charge exists:

PUT /ocpi/2.2.1/sessions/IN/EMS/sess-77c1

{
  "id": "sess-77c1",
  "start_date_time": "2026-08-11T09:12:00Z",
  "kwh": 0.000,
  "auth_method": "COMMAND",
  "authorization_reference": "auth-4471",
  "location_id": "LOC-ND-014",
  "evse_uid": "EVSE-014-02",
  "connector_id": "1",
  "status": "ACTIVE",
  "last_updated": "2026-08-11T09:12:00Z"
}

Reading the failure modes

ResultWhat went wrongWhat to tell the driver
REJECTED (sync)CPO refused before trying — unknown location, bad tokenCannot start here — try another charger
NOT_SUPPORTEDThis CPO does not support remote startUse your card or the CPO's own app
EVSE_OCCUPIEDAnother vehicle is already using itThis charger is in use
EVSE_INOPERATIVEFaulted or disabledThis charger is out of order
TIMEOUTCharger never answered — usually offlineCannot reach this charger right now
FAILEDCharger tried and could notCheck the cable is fully inserted, then retry

FAILED is worth special handling. The most common physical cause is a cable that is not seated properly, and a message telling the driver to reseat it resolves a meaningful share of these without any support contact.

Where implementations go wrong

  • Showing success on the CommandResponse. ACCEPTED at phase one means queued. Showing “charging started” there produces a support ticket every time the charger then reports EVSE_OCCUPIED. Wait for the Session.
  • Retrying on timeout. The charger may be mid-start. A retry can produce two sessions and a double charge. Reconcile against Sessions instead.
  • Non-idempotent callback handlers. Duplicate results are normal. Key on the request ID and ignore repeats.
  • Omitting authorization_reference. Without it, matching this command to its Session and CDR becomes timestamp guesswork.
  • Not checking capabilities first. The Locations module tells you whether an EVSE is REMOTE_START_STOP_CAPABLE. Checking saves a guaranteed failure.
  • Ignoring stale EVSE status. Starting against an EVSE your map last saw an hour ago is how EVSE_OCCUPIED rates climb.

A flow that holds up in production

  1. Check the EVSE is AVAILABLE and REMOTE_START_STOP_CAPABLE.
  2. Send the command with authorization_reference set.
  3. On ACCEPTED, show starting — never started.
  4. Wait for whichever arrives first: the CommandResult or the Session.
  5. Show charging only once a Session exists.
  6. On timeout, check Sessions before declaring failure.
  7. Never auto-retry; let the driver decide, after you have confirmed no session started.

The whole design principle: the Session is the truth, the command is only a request.

Frequently asked questions

What happens when you send OCPI START_SESSION?

The CPO answers immediately with a CommandResponse confirming the request was queued, relays it to the charger as an OCPP RemoteStartTransaction or RequestStartTransaction, then POSTs a CommandResult to your response_url. A Session object arrives separately and is what actually confirms charging began.

Does an ACCEPTED response mean charging has started?

No. ACCEPTED in the CommandResponse means the request was queued for processing. The charger may still report EVSE_OCCUPIED or fail. Only the appearance of a Session object confirms a charge exists.

Should you retry START_SESSION after a timeout?

No. The charger may be mid-start, and a retry can create two sessions and a double charge. Check the Sessions module to see whether a session actually started before doing anything else.

Should you specify evse_uid in START_SESSION?

Usually yes. Omitting it lets the CPO pick any available EVSE, which raises success rates, but a driver is standing at a specific charger and will not appreciate the CPO starting a different bay.

Why does START_SESSION carry the whole Token object?

Because the CPO may need to authorise the driver without being able to reach the eMSP. Sending the full token gives it the valid and whitelist state it needs to make that decision locally.