Sending commands

Ask a charger to start, stop, restart or release a cable with one call that queues while it is offline, retries safely and reports the outcome.

3 min readEdit this page
On this page

Everything you ask a charger to do goes through one endpoint: POST /v1/charging-stations/{chargingStationId}/commands. It works the same for every charger and every command type, even when the charger is offline. This page covers what all commands share; the guides cover what each one is for.

Send a command

POST/v1/charging-stations/$CHARGER/commandsbash
curl -X POST "$API/charging-stations/$CHARGER/commands" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"type": "unlockConnector", "evseId": 1, "connectorId": 1}'
200Response
{
  "id": "1f2e3d4c-5b6a-4978-8a9b-0c1d2e3f4a5b",
  "commandType": "unlockConnector",
  "chargingStationId": "9a7c0e12-3b4d-4e5f-8a6b-7c8d9e0f1a2b",
  "evseId": 1,
  "issuedBy": { "kind": "apiKey", "apiKeyId": "a1b2c3d4" },
  "status": "queued",
  "createdAt": "2026-10-01T10:00:00Z",
  "sentAt": null,
  "resolvedAt": null,
  "expiresAt": "2026-10-01T10:05:00Z",
  "request": {
    "type": "unlockConnector",
    "evseId": 1,
    "connectorId": 1
  },
  "result": null,
  "failureReason": null,
  "supersededBy": null
}

The call answers 201 at once with a command, recorded before the charger has said anything. status is sent if the charger was online and got it straight away, or queued if it is waiting for the charger to connect.

Follow it to the answer

GET/v1/charging-stations/$CHARGER/commands/$COMMAND_IDbash
curl "$API/charging-stations/$CHARGER/commands/$COMMAND_ID" \
  -H "Authorization: Bearer $TOKEN"

Or skip polling: live updates send a commandStatusChanged event with the command's id on every change.

statusMeaning
queuedWaiting for the charger to connect
sentDelivered, waiting for the answer
acceptedThe charger said yes. Its answer is in result
rejectedThe charger said no. Its answer is in result
failedNo usable answer. failureReason says why: timed_out means the charger did not answer in time
expiredThe charger did not connect before expiresAt, so it was never sent
supersededA newer command of the same type for the same outlet replaced it. supersededBy names the newer one

A timed_out command may still have happened: a charger can act and lose the connection before it answers. Check for the effect before you send it again.

Send to a charger that is offline

A charger does not need to be connected when you send. The command waits as queued and goes out the moment the charger reconnects. If it does not reconnect in time, the command expires instead of arriving long after it stopped making sense:

CommandWaits for
reset, unlockConnector, changeAvailability5 minutes
remoteStartTransaction, remoteStopTransaction, updateFirmware, triggerMessage15 minutes
reserveNow, cancelReservation30 minutes

expiresAt on the command always gives the exact time.

Retry safely

Idempotency-Key is required on this endpoint, and is what makes a retry safe:

  • Same key, same body: you get the original command back with 200, and nothing new is sent.

  • Same key, different body: 409 idempotencyKeyConflict.

  • No key: 400 missingIdempotencyKey.

Use a fresh key for each new command and reuse it for every retry of that command. A start retried after a network timeout then never becomes two sessions.

Know what a charger supports

Chargers differ in what they can do. Ask before you build a button:

GET/v1/charging-stations/$CHARGER/commands/capabilitiesbash
curl "$API/charging-stations/$CHARGER/commands/capabilities" \
  -H "Authorization: Bearer $TOKEN"
200Response
{
  "reset": "supported",
  "unlockConnector": "supported",
  "changeAvailability": "supported",
  "remoteStartTransaction": "supported",
  "remoteStopTransaction": "supported",
  "reserveNow": "notSupported",
  "cancelReservation": "notSupported",
  "updateFirmware": "supported"
}

Each command type answers one of three things:

AnswerMeaning
supportedThe charger can do it
notSupportedIt cannot. Sending it anyway is refused with 422
notYetKnownThe charger has not been connected long enough to tell

This answers from what the charger has already reported, so it works while the charger is offline. Add ?evseId=2 when support could differ between outlets. notYetKnown is never a guess: it turns into one of the other two once the charger has connected and reported what it has.

Command types

type picks the command. Each type has its own fields:

typeWhat it doesGuide
remoteStartTransactionStart a sessionStart and stop sessions remotely
remoteStopTransactionStop a sessionStart and stop sessions remotely
unlockConnectorRelease a stuck cableRelease a stuck cable
changeAvailabilityTake a charger or outlet in or out of serviceTake a charger in and out of service
resetRestart a chargerRestart a charger
reserveNowHold an outlet for one person until a time you chooseCreate command
cancelReservationCancel a reservationCreate command
updateFirmwareInstall the newest firmware Flowion has for the charger's make and modelCreate command
triggerMessageAsk the charger to send a status, meter reading or other report nowCreate command

A few things are worth knowing before you send:

  • remoteStartTransaction and remoteStopTransaction are refused with 422 chargingStationNotAccepted until the charger has finished registering with Flowion. The other types are not.

  • updateFirmware is not supported on every charger. See which. A charger connected through its maker's cloud gets its firmware from the maker, and the command answers 422 vendorManagedFirmware.

  • For triggerMessage, accepted means the charger will send the report. The report itself arrives like any other update from the charger.

  • To fetch a charger's diagnostic log, use Diagnostics instead.

Calls that wait for the charger

Reading and writing a charger's own settings, and manufacturer-specific messages, use their own endpoints under .../commands/. Those wait for the charger's answer, up to about 35 seconds, so the charger has to be online:

CodeMeaning
200The charger answered, including when it said no
409The charger is not connected
422The request cannot be sent to this charger, or was refused before sending
502The charger disconnected mid-call or sent an answer Flowion could not read
503The command could not be sent
504The charger did not answer in time

They are recorded in the same history as every other command.

Review a charger's history

POST/v1/charging-stations/$CHARGER/commandsbash
curl -G "$API/charging-stations/$CHARGER/commands" \
  -d status=failed \
  -H "Authorization: Bearer $TOKEN"

Every command ever sent to the charger, newest first, whoever sent it. issuedBy says whether a person, an API key or Flowion itself asked. Filter by status or evseId, and page with cursor and limit.

Next: Rate limits.

In the API reference