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
| Operation | Endpoint | Body |
|---|---|---|
| Set profile | POST /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:
chargingStationIdaccepts the internal UUID or the OCPP station ID.connectorIdis a connector UUID and must belong to that station (422otherwise); omit it to address the entire charge point (OCPP connector0). - Clear:
chargingStationIdis the internal UUID only. The optionalconnectorIdinsidechargingProfileis 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:
chargingProfilePurpose | Scope |
|---|---|
ChargePointMaxProfile | Hard cap for the whole charge point, regardless of transactions |
TxDefaultProfile | Default limit applied to every transaction |
TxProfile | Limit 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:
| Kind | Schedule starts |
|---|---|
Absolute | at chargingSchedule.startSchedule (fixed point in time) |
Recurring | repeats from startSchedule per recurrencyKind (Daily / Weekly) |
Relative | when the transaction starts |
Profile fields
| Field | Meaning |
|---|---|
chargingProfileId | integer, your identifier — reuse it to replace, reference it to clear |
stackLevel | integer ≥ 0, precedence within a purpose |
transactionId | required for TxProfile: the session's numeric OCPP transaction ID (ocppTransactionId on SessionDto) |
validFrom / validTo | optional overall validity window (ISO 8601) |
chargingSchedule | the limit curve, see below |
Charging schedule
| Field | Meaning |
|---|---|
chargingRateUnit | W (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 |
duration | optional schedule length in seconds; without it the last period runs until the schedule is superseded or the transaction ends |
startSchedule | ISO 8601 start (required for Absolute/Recurring anchoring) |
minChargingRate | optional 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
| Status | Cause |
|---|---|
404 | Unknown station / connector ID (mind the UUID-vs-OCPP-ID rules above) |
422 | Station has never connected (no details), or connectorId not part of the station |
403 | Key lacks smart-charging:manage |
500 | Station 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
- Remote Commands & Troubleshooting — verifying effect, OCPP log correlation
- Session Flows — where
ocppTransactionIdcomes from - General guide — errors, permissions catalog
Updated 7 days ago