Session limits
A session you start from a bill can carry one limit: a maximum energy, or a maximum amount at the bill's price. Ivora watches the session's meter values and stops it when it reaches the limit. Prepaid and pay-in-store flows use it: take the payment, then start a session that ends at what was paid for.
The limit is a stop control, not a price. The bill still prices the energy the charger actually delivered, at its tariff, exactly as it would without a limit. Some energy usually flows after the stop, so a session can end slightly above its limit. Ivora reports that overrun with the final totals.
Set a limit at start
Send limit in the body of either start call:
| Start | Route | Scopes |
|---|---|---|
| Managed bill | POST /v1/tenants/{tenant_id}/bills/{bill_id}/start | billing:write, stations:control |
| Application-managed session | POST /v1/tenants/{tenant_id}/charging-sessions/{session_id}/start | billing:write, stations:control, settlement:write |
Set exactly one of these:
{"limit": {"energy_kwh": "12.5"}}
{"funding_confirmed": true, "limit": {"amount_minor": 1500, "currency": "USD"}}
| Field | Rules |
|---|---|
energy_kwh | Above 0 and at most 500 kWh, with at most 3 decimal places (1 Wh). A decimal string or a JSON number |
amount_minor | 1 to 100000 minor units of the bill's currency (up to $1,000 in USD or CA$1,000 in CAD) |
currency | Required with amount_minor. It must be the bill's tariff currency, otherwise 422 limit_currency_mismatch |
The limit is checked before the start is journaled: a refused limit answers
422 and creates no operation, so you can retry with the same
Idempotency-Key. A retry with the same key and the same limit replays the
original start. A different limit under that key answers
409 idempotency_key_reused. A start without limit has no limit, as before.
A limit is set once, at start. It cannot be changed or removed later. You can
still stop the session yourself at any time.
Limits are available on bill starts only. The plain station start command
has no bill, tariff or receipt, so it takes no limit. A managed bill that
starts itself when the driver returns from hosted checkout has no limit either:
to use a limit, start that bill yourself.
Amount limits
An amount converts to energy once, when the start is reserved, at the bill's
per-kWh rate (rate_minor, frozen with the bill when it was created):
energy_kwh = amount_minor / rate_minor, rounded down to the Wh. The limit
record shows the rate it used and the energy cap it enforces.
Only the energy rate counts. Session fees, time-based fees and idle or parking
fees are not counted against the amount. On a tariff with those fees, the final
amount can exceed the limit by them. overrun_minor reports the difference. On
a tariff with time-of-day energy prices, the conversion uses the bill's base
rate. A bill with no energy price (a free tariff) refuses an amount limit with
422 limit_unpriced; use energy_kwh. An amount that buys less than 1 Wh
answers 422 limit_too_small. An amount that buys more than 500 kWh at the
rate (the most an energy limit can be) answers 422 limit_too_large; Ivora
never caps it silently, so set a smaller amount or an energy_kwh limit.
How Ivora stops the session
Every OCPP version is stopped the same way, from the server. Ivora reads the session's energy, its current power and the charger's meter interval. It sends the stop when:
delivered + expected until the next meter value + margin >= limit
- delivered: the transaction's energy so far, in kWh.
- expected: the current power multiplied by the interval between the charger's two latest meter values (60 seconds when unknown). Ivora's check runs about every 15 seconds, so the interval is never taken as shorter than that. Without a reported power, Ivora uses the energy rate between the two latest registers.
- margin: an extra energy allowance set for the environment, 0 kWh by default.
The stop is the ordinary stop command: RemoteStopTransaction on OCPP 1.6,
RequestStopTransaction on OCPP 2.0.1 and 2.1. Ivora does not send charger-side
transaction limits. If the transaction is still active a minute after a stop,
Ivora sends the stop again, up to three attempts in all. On an application-managed
session the stop is the session's single stop operation: the session reads
stopping, and your own stop call answers 409 stop_already_reserved. If
you stopped the session first, Ivora does not send a second stop.
If the charger still has not ended the transaction a minute after the third
stop, Ivora stops retrying and raises a session_limit_unanswered alert for
its operators: the session is still charging past its limit. From then on your
own stop call is accepted again on an application-managed session. The alert
resolves when the transaction ends or the bill is final.
Ivora sends a stop only when meter values arrive. A charger that reports no meter values during the session is never stopped by a limit. Set a meter interval on the charger (MeterValueSampleInterval on OCPP 1.6, the SampledDataCtrlr interval on 2.x) for a closer stop.
Read the outcome
Every bill read, and the bill inside a charging-session read, carries limit:
{
"type": "amount",
"energy_kwh": "42.857",
"energy_wh": 42857,
"amount_minor": 1500,
"currency": "USD",
"rate_minor": "35.00",
"stop_reason": "limit_reached",
"stop_requested_at": "2026-10-21T09:41:12+00:00",
"stop_energy_kwh": "42.61",
"stop_operation": {"id": "op_…", "status": "succeeded", "result": {"reason": "limit_reached"}},
"final_energy_kwh": "42.874",
"final_amount_minor": 1501,
"overrun_kwh": "0.017",
"overrun_wh": 17,
"overrun_minor": 1
}
| Field | Meaning |
|---|---|
stop_reason | limit_reached once Ivora decided to stop the session at its limit; null while the session runs below it, or when it ended another way |
stop_energy_kwh | The energy delivered when Ivora decided to stop |
stop_operation | The latest stop Ivora sent. succeeded means the charging core accepted the command. The transaction ending confirms the stop |
final_energy_kwh, final_amount_minor | Set once the bill is final: the billed energy and total, fees included |
overrun_kwh, overrun_wh | Final energy minus the energy cap, never below 0 |
overrun_minor | Amount limits: final amount minus the limit, never below 0 |
Energy is a kWh decimal string with an integer Wh beside it, as everywhere in the API. Raw OCPP 1.6 meter registers are Wh. Ivora converts them, and the transaction's own energy is already kWh.
The same limit travels in the receipt data: the final bill, which
bill.finalized and the charging-session read carry.
Events
| Event | When | Payload |
|---|---|---|
bill.limit_reached | Once per bill, when Ivora's first stop is accepted | bill (with limit), session_id (application-managed sessions, else null), transaction_id |
bill.finalized | The bill is final | bill with limit, including the final totals and overrun |
charging_session.status_changed | An application-managed session's status changes, for example to stopping | session with bill.limit |
The stop itself is also an operation.completed event under the bill scope.
See Webhooks and events.



