What it does
STOP_SESSION asks the CPO to terminate an active charging session. The CPO translates it into an OCPP RemoteStopTransaction (1.6J) or RequestStopTransaction (2.0.1), the charger stops delivering power, and a CDR follows once the CPO has finalised the numbers.
It is sent by the eMSP, which is the party holding the driver relationship. The driver taps “stop” in an app they got from their mobility provider, and that provider has to reach across into a charging network it does not own.
The one identifier that matters
Unlike START_SESSION, which needs a token, a location and an EVSE, STOP_SESSION takes exactly one meaningful field: session_id.
| Field | Required | Notes |
|---|---|---|
response_url | Yes | Where the CommandResult is POSTed |
session_id | Yes | The session to terminate, as issued by the CPO |
That simplicity hides a dependency: you can only stop a session whose ID you already know, and that ID came from the CPO through the Sessions module. If your session ingestion is lagging or lossy, you cannot stop sessions you cannot see. Reliability of STOP_SESSION is therefore a function of how well you consume Sessions, not of how well you send commands.
The full exchange
POST /ocpi/2.2.1/commands/STOP_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: b41f9a02
X-Correlation-ID: driver-stop-88231
{
"response_url": "https://emsp.example.com/ocpi/2.2.1/commands/STOP_SESSION/b41f9a02",
"session_id": "sess-77c1"
}The synchronous acknowledgement — again, not the outcome:
HTTP/1.1 200 OK
{
"data": { "result": "ACCEPTED", "timeout": 30 },
"status_code": 1000,
"timestamp": "2026-08-11T10:03:12Z"
}And the asynchronous result once the charger has actually stopped:
POST /ocpi/2.2.1/commands/STOP_SESSION/b41f9a02
{
"result": "ACCEPTED"
}If the session ID is not recognised, the failure comes back in the synchronous response rather than the callback, because the CPO can answer that without asking the charger:
HTTP/1.1 200 OK
{
"data": { "result": "UNKNOWN_SESSION" },
"status_code": 1000,
"timestamp": "2026-08-11T10:03:12Z"
}
The race you must design for
Charging sessions end on their own. The driver unplugs, the battery fills, the vehicle stops drawing, a fault trips. Any of these can happen in the seconds between your command leaving and the charger receiving it.
The result is a set of outcomes that look like failures but are not:
| What you observe | What actually happened | Correct response |
|---|---|---|
UNKNOWN_SESSION on a session you just saw active | The session ended and the CPO already closed it | Success from the driver's point of view — show “charging stopped” |
REJECTED immediately after | The charger is already idle | Treat as already-stopped, not as an error |
TIMEOUT, then a CDR arrives | The stop worked; the result was lost | Reconcile against the CDR, not the command |
ACCEPTED but the session lingers | Charger acknowledged and is ramping down | Wait — power-down is not instantaneous on DC |
UNKNOWN_SESSION. The user-facing question is “has charging stopped?”, and the authoritative answer is the Session status or the CDR — not the command result.Stop is not the same as billing complete
A common product mistake is treating the STOP_SESSION result as the moment to show a final cost. It is not. The sequence is:
- Command accepted — the CPO will try.
- Charger stops delivering power.
- Session object transitions to
COMPLETED. - The CPO finalises meter values, applies the tariff, and issues a CDR.
Step 4 can lag by minutes and legitimately by hours if the charger was offline. Presenting a total before the CDR arrives means either showing an estimate you later have to correct, or showing nothing. Both are valid product choices — silently showing an estimate as if final is not.
Production lessons
- Make your session store the source of truth for stoppability. Only offer a stop button for sessions you can see as active. Offering it for a stale session generates avoidable
UNKNOWN_SESSIONresponses. - Debounce driver taps. Frustrated drivers tap repeatedly. Send one command, keep the button in a pending state until you have a result or a session transition.
- Treat
UNKNOWN_SESSIONas probable success. Verify against session status before showing an error. The overwhelming majority are races, not faults. - Set a hard fallback. If no result and no session transition arrives within a couple of minutes, tell the driver to unplug manually rather than leaving a spinner running.
- Log the delta between command sent and session
COMPLETED. That distribution per CPO is one of the clearest partner quality metrics you can collect.
Frequently asked questions
It asks the CPO to terminate an active charging session by session ID. The CPO relays it to the charger as an OCPP RemoteStopTransaction or RequestStopTransaction, and a CDR follows once the CPO has finalised the numbers.
Usually because the session already ended on its own — the driver unplugged, or the vehicle finished — and the CPO has closed it. From the driver's perspective this is success, not failure, so verify against session status before showing an error.
No. Stopping power delivery and issuing the CDR are separate steps. The CPO must finalise meter values and apply the tariff, which can take minutes or longer if the charger was offline. The CDR is the authoritative total.
Only session_id, plus the response_url for the async result. That means you can only stop sessions whose IDs you have already received through the Sessions module.