Sending commands to a charger
Every action you ask a charging station to take — start a session,
unlock a connector, reset itself — goes through one resource:
POST /v1/charging-stations/{id}/commands. This page is the shape that
endpoint shares across every command; the how-to guides cover what each
individual command means and when to reach for it.
The request creates a record, not an answer#
curl -X POST "$API/charging-stations/$CHARGER/commands" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"type": "unlock_connector", "connector_id": 1}'
The response is a 201 (or a 200 — see below) with a Command resource,
created immediately, before the station has said anything:
{
"id": "1f2e...",
"command_type": "unlock_connector",
"charging_station_id": "9a7c...",
"evse_id": null,
"status": "queued",
"created_at": "2026-08-31T10:00:00Z",
"sent_at": null,
"resolved_at": null,
"expires_at": "2026-08-31T10:05:00Z",
"request": { "connector_id": 1 },
"result": null,
"failure_reason": null,
"superseded_by": null
}
status starts at queued or sent and is never the station's answer —
poll or watch for it to reach a terminal value:
curl "$API/charging-stations/$CHARGER/commands/$COMMAND_ID" \
-H "Authorization: Bearer $TOKEN"
status is one of queued, sent, accepted, rejected, failed,
superseded, or expired. accepted/rejected is the station's own
answer, in result; failed and expired mean the platform gave up
without one. Rather than polling, subscribe to
live updates and watch for a
command_status_changed event carrying this command's id — it fires on
every status transition.
accepted means the station agreed to try, not that the action
happened. A remote_start_transaction the station accepts still has to
find a vehicle plugged in and close its contactor; if nothing is plugged
in, most stations accept and then simply wait. Watch for the resulting
resource to actually change — a new transaction, a connector's status —
rather than trusting accepted alone.
It queues even when the station is offline#
A station does not need a live connection at the moment you call this
endpoint. The command is written down and delivered the next time the
station is reachable; queued is exactly that state. expires_at bounds
how long it waits — a command still queued past its expiry moves to
expired on its own rather than being delivered indefinitely late.
Idempotency-Key is required, not optional#
Every POST to this endpoint needs one:
Idempotency-Key: 7d6a2f10-...
Retry the same request with the same key and the same body, and you get
back the original command — 200, not 201 — instead of a second
one being queued. Retry with the same key but a different body and
you get 409: the key is already bound to a different request. A missing
key is 400. This is what makes it safe to retry a start-transaction
call after a timeout without risking two sessions.
Command types#
The type field selects which command, and each type has its own body
fields — see each guide for the full shape:
type | Guide |
|---|---|
remote_start_transaction | Start and stop sessions remotely |
remote_stop_transaction | Start and stop sessions remotely |
unlock_connector | Unlock a stuck connector |
change_availability | Take a charger in and out of service |
reset | Reset a charger |
reserve_now | Holds a connector for one id-token (RFID card) until it either starts a session or the reservation expires. |
cancel_reservation | Cancels a reservation created with reserve_now. |
update_firmware | 1.6 stations only — points the station at a firmware image to fetch. |
get_diagnostics | 1.6 stations only — asks the station to upload a diagnostics bundle. |
Listing and filtering#
GET /v1/charging-stations/{id}/commands returns the same paginated
envelope as every other list endpoint — data, page, per_page,
total — and accepts status and evse_id to narrow it, newest first.
Useful for a station you suspect has a stuck or repeatedly failing
command queued against it.
Next: Rate limits.