Smart Charging

Public API — Smart Charging

How to limit charging power dynamically — site-wide caps, per-session throttling, scheduled load curves — using OCPP charging profiles.

Both endpoints require permission smart-charging:manage.

Endpoints

OperationEndpointBody
Set profilePOST /api/v1/charging-profiles/set-charging-profile{ chargingStationId, connectorId?, chargingProfile }
Clear profile(s)POST /api/v1/charging-profiles/clear-charging-profile{ chargingStationId, chargingProfile: <match criteria> }

Both return 200 with an empty body once the command is sent to the station. The station's acceptance/rejection is visible in the OCPP logs.

Addressing quirks (they differ between the two endpoints):

  • Set: chargingStationId accepts the internal UUID or the OCPP station ID. connectorId is a connector UUID and must belong to that station (422 otherwise); omit it to address the entire charge point (OCPP connector 0).
  • Clear: chargingStationId is the internal UUID only. The optional connectorId inside chargingProfile is the numeric OCPP connector ID (raw OCPP payload), not a UUID.

How profiles work

A profile is a power/current limit over time, attached to a purpose:

chargingProfilePurposeScope
ChargePointMaxProfileHard cap for the whole charge point, regardless of transactions
TxDefaultProfileDefault limit applied to every transaction
TxProfileLimit for one running transaction — requires transactionId

The station enforces the minimum of the applicable profiles (a TxProfile can go below the site cap, never above it). Within one purpose, the profile with the highest stackLevel wins; installing a profile with the same purpose + stackLevel replaces the previous one.

chargingProfileKind anchors the schedule in time:

KindSchedule starts
Absoluteat chargingSchedule.startSchedule (fixed point in time)
Recurringrepeats from startSchedule per recurrencyKind (Daily / Weekly)
Relativewhen the transaction starts

Profile fields

FieldMeaning
chargingProfileIdinteger, your identifier — reuse it to replace, reference it to clear
stackLevelinteger ≥ 0, precedence within a purpose
transactionIdrequired for TxProfile: the session's numeric OCPP transaction ID (ocppTransactionId on SessionDto)
validFrom / validTooptional overall validity window (ISO 8601)
chargingSchedulethe limit curve, see below

Charging schedule

FieldMeaning
chargingRateUnitW (watts, total) or A (amperes, per phase)
chargingSchedulePeriod[]list of { startPeriod, limit, numberPhases? }; startPeriod = offset in seconds from schedule start; each period's limit holds until the next period
durationoptional schedule length in seconds; without it the last period runs until the schedule is superseded or the transaction ends
startScheduleISO 8601 start (required for Absolute/Recurring anchoring)
minChargingRateoptional floor the station should not plan below

Examples

Site cap: 22 kW during working hours, 44 kW at night, every day:

curl -s -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
        "chargingStationId": "EWEL00629",
        "chargingProfile": {
          "chargingProfileId": 1,
          "stackLevel": 0,
          "chargingProfilePurpose": "ChargePointMaxProfile",
          "chargingProfileKind": "Recurring",
          "recurrencyKind": "Daily",
          "chargingSchedule": {
            "startSchedule": "2026-07-08T08:00:00Z",
            "duration": 86400,
            "chargingRateUnit": "W",
            "chargingSchedulePeriod": [
              { "startPeriod": 0,     "limit": 22000 },
              { "startPeriod": 36000, "limit": 44000 }
            ]
          }
        }
      }' \
  "https://<host>/api/v1/charging-profiles/set-charging-profile"

Throttle one running session to 16 A per phase (find ocppTransactionId on the session):

{
  "chargingStationId": "<station-uuid>",
  "connectorId": "<connector-uuid>",
  "chargingProfile": {
    "chargingProfileId": 42,
    "transactionId": 1234567,
    "stackLevel": 0,
    "chargingProfilePurpose": "TxProfile",
    "chargingProfileKind": "Relative",
    "chargingSchedule": {
      "chargingRateUnit": "A",
      "chargingSchedulePeriod": [{ "startPeriod": 0, "limit": 16, "numberPhases": 3 }]
    }
  }
}

Clear — criteria are AND-ed; an empty chargingProfile object clears everything on the station:

{
  "chargingStationId": "<station-uuid>",
  "chargingProfile": { "chargingProfilePurpose": "ChargePointMaxProfile", "stackLevel": 0 }
}

or by ID: { "chargingProfile": { "id": 1 } }.

Errors & troubleshooting

StatusCause
404Unknown station / connector ID (mind the UUID-vs-OCPP-ID rules above)
422Station has never connected (no details), or connectorId not part of the station
403Key lacks smart-charging:manage
500Station runs OCPP 2.0.1 — profiles not supported for it

A 200 means the command went out — the station can still reject the profile (unsupported purpose, too many periods, stack level conflicts — vendor-dependent). Check the station's reply in the OCPP logs (POST /api/v1/charging-stations/{id}/ocpp-logs/rsql-list); a rejected profile simply doesn't change the charging rate. TxProfile for a transaction that already ended is rejected by the station.

Profiles live on the station: a TxProfile dies with its transaction; ChargePointMaxProfile / TxDefaultProfile persist across reboots on most vendors — clear them explicitly when removing a cap.

Related



Did this page help you?