Charger management

Charger displays

Show your payment QR on a charger's own screen. You declare what each EVSE should show; Ivora stores that desired state, pushes it to the charger and pushes it again whenever the charger could have lost it. Your application still chooses the URL and serves the page it opens.

Important

Status: not enabled in production yet. Reads work, but PUT and DELETE return 409 displays_disabled, and Ivora sends nothing to charger screens until it enables displays. The routes are in the Charger displays group of the API reference. Physical acceptance on Renova, Sinexcel and generic OCPP 2.x chargers remains open.

How delivery works

sequence diagram; its source follows
Diagram source
sequenceDiagram
  participant App as Application backend
  participant API as Ivora API
  participant Sweep as Display sweep
  participant C as CSMS and charger
  App->>API: PUT display {qr, show_price, tariff_id, visibility}
  API-->>App: 200 record, delivery.state pending
  API->>Sweep: Wake
  Sweep->>C: Read online state, tariff, boot marker, active session
  Sweep->>C: Adapter messages for this charger
  C-->>Sweep: Core accepted or rejected
  Sweep->>API: Record delivery.state applied or failed
  API-->>App: display.delivery_changed webhook

The write is synchronous and pure data: it stores the desired state and returns. It never waits for the charger. A sweep inside the API compares desired, applied and observed state and pushes only what differs. A write wakes the sweep at once; otherwise it runs about every 15 seconds.

The sweep pushes again when:

  • the charger reboots (its boot marker changed), because the screen may be blank;
  • the displayed price changes and the display shows price;
  • you change qr, show_price, tariff_id or visibility;
  • Ivora changes the adapter that drives the charger. The push first clears what the previous adapter showed, when that adapter can clear;
  • a previous push failed, after a backoff of 1, 5, 15 and then 30 minutes. A reboot skips the wait, and so does new desired state: a PUT that changes a display on the station, a DELETE that needs a clear, or Ivora changing its adapter.

An offline charger receives nothing. Displays it has not yet received stay pending and are pushed on the first sweep after it reconnects.

If the charging core cannot be reached, the sweep stops and the next sweep continues. A push that found the core unreachable counts as a failure for its station's backoff, and its display reports failed with csms_unavailable; the station's other displays keep their state. One sweep spends about 20 seconds at most, and stations it did not reach follow on the next one.

applied means the charging core accepted the push. Like other charger-bound writes, it is dispatch acknowledgement, not proof of what the panel shows. The generic OCPP 2.x adapters cannot know whether a charger has a screen at all, so a 2.x charger without one still reports applied.

Check the station first

Every station record (list, read and create) carries a read-only display field. Read it to decide whether to rely on the screen or print a QR card.

"display": {
  "adapter": "builtin-rcd",
  "reason": "matched",
  "qr": true,
  "price": true,
  "clear": false,
  "evses": [{"evse_number": 1, "state": "applied", "shown": true}]
}
reasonMeaning
matchedAn adapter matches the charger's reported vendor, model and protocol, or a generic adapter covers an OCPP 2.x charger with one EVSE
assignedIvora assigned an adapter to this station
not_bootedThe charger has not connected yet, so its vendor is unknown. Displays wait as pending
no_adapterNo adapter drives this charger's screen, including OCPP 2.x chargers with more than one EVSE and no vendor adapter. Use a printed QR
protocol_unsupportedThe assigned adapter does not support the charger's OCPP version
assigned_unpublishedThe assigned adapter is not available. Ivora must correct the assignment

adapter is null for every reason except matched and assigned. clear says whether the adapter can take a QR off the screen. price says whether it also shows a price. evses lists the displays you configured.

API contract

Prefix: /v1/tenants/{tenant_id}/stations/{station_id}. {evse_number} is the OCPP EVSE number (1–99) that station records expose as connectors[].evse_number. See the Charger displays group in the API reference for exact schemas.

Method and suffixPurposeScope
GET /displaysList the station's displays and their deliverystations:read
PUT /evses/{evse_number}/displaySet the QR an EVSE screen showsstations:write
GET /evses/{evse_number}/displayRead one display and its deliverystations:read
DELETE /evses/{evse_number}/displayStop maintaining a displaystations:write
{"qr": "https://pay.example.com/checkout/HOST01-1", "show_price": true, "tariff_id": 42, "visibility": "idle"}
FieldRules
qrRequired. An https URL of at most 500 characters, without credentials, spaces or control characters. Any HTTPS origin is allowed. The screen encodes it verbatim, so never include customer data or secrets
show_priceDefault true. Show the price where the adapter can. You cannot supply price text
tariff_idOptional. A tariff in your tenant whose price the screen shows, usually the one your application bills with. Omit it to use the EVSE's connector tariff
visibilityidle (default) or always. See visibility

PUT and DELETE require an Idempotency-Key and follow the retry rules: the same key and body replay the first result. PUT returns 200 with the display record, which echoes tariff_id. Sending the same content again keeps its revision and delivery; any change, including a different tariff_id, is a new revision and returns to pending. On a connected charger with no adapter the display is stored with state unsupported.

Errors: 404 station_not_found, 404 evse_not_found (no EVSE with that number), 404 tariff_not_found (tariff_id is not a tariff in your tenant), 404 display_not_configured on reads and deletes, 422 invalid_request for field validation, 409 idempotency_key_reused, and 409 displays_disabled on PUT and DELETE while Ivora has switched charger displays off in the environment.

Delivery state

delivery.stateMeaning
pendingThe current revision is not yet acknowledged: the charger is offline, has not connected yet, or the push is queued
appliedThe core accepted the push of delivery.revision
failedThe last push was rejected or unconfirmed. Ivora retries it; last_error holds the stable code
unsupportedNo adapter drives this charger's screen. Print the QR instead

Typical last_error values are dispatch_unconfirmed (the core did not confirm the push), csms_unavailable (the charging core could not be reached), evse_not_found (the EVSE was removed), tariff_not_found (the tariff_id tariff no longer exists in your tenant; nothing is sent until you PUT another) and template_render_failed (the adapter could not render this display, for example a message that would exceed a protocol limit).

delivery.shown says whether the last applied push showed the QR (true) or cleared it (false). It is null before the first push.

Subscribe to display.delivery_changed webhooks instead of polling. The event fires when a display is created or its state, shown or deleted value changes. resource_id is <station_id>-<evse_number> and data.display carries the full record.

Visibility and clearing

With visibility: "idle" the QR is shown while the connector is payable and cleared otherwise. Payable means no active transaction on the EVSE and a connector status of Available, Preparing, Finishing or Occupied. always never clears.

Clearing needs an adapter with clear: true. On adapters with clear: false, idle behaves like always: Ivora pushes the QR when it could have been lost and never sends a clear. You can send idle everywhere and read clear to explain the behavior to your users.

DELETE returns the final record with deleted: true:

  • With clear: true, the record reports delivery.state pending until Ivora has sent one clear for a QR it showed, including one a push was sending at that moment. Then Ivora forgets the display. A display that never reached the screen is forgotten without a clear.
  • Otherwise Ivora forgets the display at once. The response keeps the last delivery.shown: true once a QR was sent, null if none was. A clear: false charger keeps showing the last QR: builtin-rcd chargers until they reboot, builtin-sinexcel chargers until something overwrites it, because the QR is stored in a configuration key that survives reboots. To point a screen elsewhere, PUT a new qr instead of deleting.

Deleting a station also removes its displays, without clearing the screen on any adapter. To blank a clear: true screen first, DELETE each display and delete the station only once reading the display returns 404 display_not_configured.

Price

The displayed price comes from the display's tariff_id when you set one. Otherwise it comes from the tariff of the EVSE's first connector that has one. If your application bills with its own tariff rather than the connector tariff, set tariff_id to that tariff so the screen shows what the driver pays. With neither, there is no price: adapters that always carry a price (builtin-rcd) show 0, and the generic OCPP 2.x adapters show no price text.

A price change, such as a new connector tariff or a PUT with a new tariff_id, reaches the screen on the next sweep. Some adapters also show one station-wide price; it uses the lowest-numbered EVSE that shows a price and has one.

Ivora-managed adapters

Adapters translate your display into vendor messages. Ivora manages them; your application never supplies OCPP messages. Four adapters are built in:

AdapterChargersProtocolsPriceClear
builtin-rcdVendors RCD and RENOVAOCPP 1.6, 2.0.1 and 2.1Yes: kWh price with the QR; tariff text by DefaultPrice on 1.6No
builtin-sinexcelVendor SINEXCELOCPP 1.6NoNo
builtin-ocpp201Any vendor without its own adapter, chargers with one EVSEOCPP 2.0.1Yes: tariff text as a second display message, cycled with the QRYes
builtin-ocpp21Any vendor without its own adapter, chargers with one EVSEOCPP 2.1Yes: tariff text as a second display message, cycled with the QRYes

builtin-rcd and builtin-sinexcel cannot clear. builtin-sinexcel writes the QR into the configuration key ChargePointQRCode_<evse_number>, which the charger keeps across reboots.

OCPP 2.0.1 and 2.1 chargers with one EVSE are covered automatically: when no adapter matches their vendor, builtin-ocpp201 or builtin-ocpp21 sends standard SetDisplayMessage messages. The QR and the price text have the same priority, so a screen that follows OCPP cycles between them. Without a price, the price text is cleared. With visibility: "idle" both leave the screen while the EVSE is in use; with always they stay. OCPP 2.1 chargers draw the QR from your URL. OCPP 2.0.1 has no QR format, so the screen loads a PNG of your qr from https://api.ivoracharge.com/v1/display-images/<sha256 of qr>.png: a public, cacheable address that Ivora serves only for QRs a display has pushed. These adapters cannot tell whether the charger has a screen, so keep the printed QR as well.

These messages address the whole charger, not one EVSE. On a charger with several EVSEs sharing a screen, one EVSE's QR would replace or alternate with another's, and a driver could pay for the wrong plug. So OCPP 2.x chargers with more than one EVSE, like OCPP 1.6 chargers, report no_adapter unless a vendor adapter matches them.

Ivora administrators can add adapters for more models, including vendor-specific ones that take precedence over the generic OCPP 2.x adapters, without an API release. An adapter is a declarative template, not code, and is limited to four OCPP actions: DataTransfer, ChangeConfiguration (OCPP 1.6), SetDisplayMessage and ClearDisplayMessage. Validation refuses the standard OCPP 1.6, security and ISO 15118 configuration keys, and keys naming URLs, hosts, credentials, certificates, firmware, networking or profiles. It does not judge vendor-specific keys or DataTransfer content, so Ivora reviews each adapter's rendered messages before publishing it. Your qr is the only value you supply, and adapters place it verbatim; every other value they send (EVSE number, station name, price) comes from Ivora's records. Ask Ivora if your charger model needs an adapter.

Retries

Charger commands are dispatched at most once, and unknown outcomes are reconciled by hand. Display pushes are the one deliberate exception: they set idempotent screen content, so Ivora resends them after failures and reboots. This never applies to commands or anything that moves money.

Limits

  • Physical acceptance on real chargers is still open; see the status above.
  • applied is dispatch acknowledgement, not proof of what the screen shows. A 2.x charger without a screen still reports applied under the generic adapters.
  • A reboot is noticed on the next sweep, so a rebooted charger shows its QR again within about 15 seconds of reconnecting, not instantly. One sweep handles up to 100 stations; more stations take extra sweeps.
  • builtin-rcd sends connector_id equal to the EVSE number. Acceptance covers single-connector RCD units only.
  • builtin-rcd always sends a price with its QR message. Without tariff_id or a connector tariff, or with show_price: false, it sends 0.
  • Turning show_price off everywhere does not remove a station-wide price already on the charger.
  • qr is limited to 500 characters because builtin-sinexcel writes it into an OCPP 1.6 configuration value, which holds at most 500.
  • A builtin-sinexcel screen keeps its last QR after DELETE, even across reboots, until another value is written.
  • OCPP 1.6 chargers, and OCPP 2.x chargers with more than one EVSE, need a printed QR unless a vendor adapter drives them.
  • The generic OCPP 2.x adapters are not yet accepted on physical chargers, and screens may differ in how they cycle the QR and the price text.
  • When Ivora changes a station's adapter, the previous adapter's messages are cleared only if it can clear. A station left without any adapter keeps what was last shown.
  • The legacy payment service still writes the screens of chargers provisioned through it until Ivora turns off its display writes per environment. On OCPP 2.x screens its QR stays in front of the generic adapters' messages until Ivora has turned those writes off and removed it.