Roll a pilot charger back to your previous platform
While your network moves to Ivora, a charger proxy sits between your chargers and the two platforms. Each charger keeps dialling the same address; the proxy routes it either to your previous platform or to Ivora, one charger at a time. During a pilot you can send any pilot charger back to your previous platform yourself, at any time, from your dashboard's Migration page or through the API below. To roll back a whole site or every charger at once, use the migration's workspace (Migration portal): it acts on the same rollbacks.
A rollback moves only the charger's connection:
- The proxy routes the charger to your previous platform and closes its connection to Ivora. The charger reconnects by itself, usually within seconds, to your previous platform, which controls it again.
- Nothing is sent to the charger, and nothing changes in Ivora's record of the station: it stays registered, with its history, and can be moved to Ivora again later by the migration team.
- QR sticker scans of that charger go to your previous platform again (QR sticker redirects).
Diagram source
sequenceDiagram
participant You as Your team admin
participant API as Ivora API
participant Proxy as Charger proxy
participant Old as Previous platform
You->>API: POST .../migration/chargers/{station_id}/rollback
API-->>You: 202, status pending
Proxy->>API: routing report (every few seconds)
API-->>Proxy: rollbacks to apply
Proxy->>Proxy: route to previous platform, close Ivora connection
Proxy->>API: result: applied (no connection to Ivora open)
API-->>You: migration.charger_rolled_back (webhook)
Note over Proxy,Old: the charger reconnects through the proxy
Proxy->>API: routing report: connected to previous platform
You->>API: GET .../migration/rollbacks/{rollback_id}
API-->>You: applied, reconnect: reconnectedWho can roll back
| Action | Needs |
|---|---|
| List migrating chargers, read a rollback | stations:read |
| Roll a charger back | stations:admin: your team's owners and admins, Ivora staff, and API keys created with it |
A rollback is never available to an AI agent: a person types the charger's name to confirm it.
List your migrating chargers
GET /v1/tenants/{tenant_id}/migration/chargers lists the chargers the proxy
reports for your project, and those a site move recorded, ordered by station
ID (after and limit, up to 500, page through them).
GET /v1/tenants/{tenant_id}/migration/chargers/{station_id} reads one.
| Field | Meaning |
|---|---|
state | on_ivora, on_previous_platform (never moved, or rolled back) or rollback_pending |
route | Where the proxy routes the charger: ivora or previous_platform |
proxied | true when the proxy reports the charger. Only these can be rolled back here |
connected | The side its connection through the proxy is open on: ivora, previous_platform, both (for a moment while it moves) or none (offline) |
proxy_reported_at, proxy_stale | When Ivora last heard from the proxy about it; stale after five minutes. A rollback asked while the proxy is stale waits until it reports again |
shadow | The station is in shadow mode: your previous platform controls it and Ivora only observes |
recorded_route | The route Ivora recorded from site moves and rollbacks, which QR sticker scans follow |
rollback_available | Whether a rollback would be accepted now |
last_rollback | The latest rollback of this charger, as below |
Roll a charger back
curl -X POST "$API/v1/tenants/7/migration/chargers/42/rollback" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "CP-0042", "reason": "Driver reported a failed start at bay 3"}'
nameis the station's connection name (namein station reads), typed to confirm. Another name is refused with409 station_name_mismatch(details.name), so a wrong station ID never moves another charger.reason(3 to 300 characters) is kept with the rollback, in your audit log and in the event.- The answer is
202with the rollback,status: pending. A retry with the sameIdempotency-Keyreturns the same rollback as it is now.
A rollback works while Ivora's charging core is unavailable, which is when you
are most likely to need it: the station is then read from the proxy's own
report of it. A station the proxy does not report cannot be looked up then and
the request answers 503; retry later or ask Ivora support.
A rollback is always accepted while the charger is routed to Ivora, also while it is charging. A session that is running on Ivora then stays open on Ivora: the charger ends it on your previous platform, and Ivora's copy is reconciled by hand with the migration team. Roll back between sessions when you can.
| Refusal | When |
|---|---|
404 station_not_found | No such station in your project |
409 station_name_mismatch | name is not the station's connection name |
409 migration_charger_not_proxied | The proxy does not report this charger. Ask Ivora support, who roll it back for you |
409 migration_rollback_pending | A rollback of this charger is already pending or waiting for its connection to Ivora to close (details.rollback_id) |
409 migration_already_rolled_back | The charger is already routed to your previous platform and has no connection to Ivora |
Follow the result and the reconnect
GET /v1/tenants/{tenant_id}/migration/rollbacks/{rollback_id}:
status | Meaning |
|---|---|
pending | Recorded; the proxy has not switched the route yet |
applied_socket_open | The proxy routes the charger to your previous platform (rolled_back_at), but its connection to Ivora did not close yet. The proxy keeps closing it; the rollback becomes applied once it is closed |
applied | The proxy routes the charger to your previous platform and no connection to Ivora is open |
failed | The proxy could not switch it (detail). Ask again, or ask Ivora support, who roll it back by hand |
superseded | Not applied: Ivora staff moved the charger to Ivora again after you asked (for example after rolling it back by hand while the proxy was unreachable). Ask again if you still want it back |
expired | The proxy did not apply it within 24 hours (it could not reach Ivora). Ivora on-call rolls the charger back by hand; ask again once the proxy reports |
A rollback that is still pending or applied_socket_open ten minutes after
you asked alerts Ivora on-call, who then roll the charger back by hand within
four business hours.
reconnect says whether the charger reconnected to your previous platform,
from the proxy's reports after the switch:
reconnect | Meaning |
|---|---|
waiting_for_switch | Still pending |
reconnecting | Within two minutes of the switch, no connection to your previous platform reported yet |
reconnected | A connection to your previous platform is up |
not_connected | Two minutes after the switch, no connection at all: check the charger on site, or with your previous platform |
still_on_ivora | A connection to Ivora is still open: the proxy could not close it yet (also while applied_socket_open) |
superseded | The charger was moved to Ivora again since |
unknown | The proxy has not reported since the switch |
null | The rollback failed, was superseded or expired |
Poll every few seconds, or subscribe to the event below and then read the rollback.
Event
migration.charger_rolled_back (stations:read) is sent once, when the proxy
has switched the charger and no connection to Ivora is open (so never while
the rollback is applied_socket_open):
{
"rollback_id": "mrb_1f0e9c...",
"station_id": 42,
"name": "CP-0042",
"previous": "ivora",
"current": "previous_platform",
"rolled_back_at": "2026-10-28T15:04:11+00:00",
"reason": "Driver reported a failed start at bay 3",
"requested_by": "user:3b1f...",
"requested_at": "2026-10-28T15:04:05+00:00",
"team_id": "team_9a2c...",
"source": "tenant"
}
source is tenant for your own team and ivora when Ivora staff asked for
it. A failed, superseded or expired rollback sends no event; read the rollback
for it.
Audit log
Each request is recorded as migration.rollback_requested with who asked, the
team, the station and the reason. Each later status is recorded as
migration.rollback_<status>: applied_socket_open, applied, failed,
superseded and expired (by system:migration). Every change in the proxy's
routing is recorded as migration.proxy_routes_changed.
For Ivora staff
Ivora staff roll a charger back through the same route, with their own
sign-in. The charger proxy reaches Ivora through two staff routes, called with
the project's proxy key (migration:proxy,
made by Ivora staff); they are not part of a project's integration:
| Route | What the proxy does |
|---|---|
PUT /v1/admin/tenants/{tenant_id}/migration/proxy-state | Every few seconds: reports every charger it routes for the project (routing key, station, route, open connections, and route_changed_at, when it last moved the charger to that route by its own clock). The answer lists the rollbacks to apply |
POST /v1/admin/tenants/{tenant_id}/migration/rollbacks/{rollback_id}/result | Reports applied, applied_socket_open, failed or superseded once it acted. A result that cannot follow the current status is refused with 409 migration_rollback_settled |
The report answers pending rollbacks, and applied_socket_open ones while the
proxy still reports a connection to Ivora, so the proxy closes it again. Before
applying one, Ivora and the proxy both check for a forward flip after the
request: Ivora from the proxy's route report and the recorded site flips and
route reports, the proxy from its own flip log. Either one marks it
superseded instead. Ivora compares the request with the time the proxy sends
in route_changed_at (a time ahead of Ivora's clock counts as the time Ivora
received it), not with when the report arrived. A charger the proxy reports
for the first time, or again after it stopped reporting it, without that time
does not count as moved forward. A known charger is matched from Ivora's own record of the
proxy's last report, so the report keeps working while the charging core is
unavailable; a charger the core cannot confirm then is kept, not dropped.
Both routes keep working until the pilot's sync unit moves to the migration's
runner channel: a runner credential with the single
scope migration:routes (Migration portal).
A rollback asked through a migration reaches the proxy on either channel.
Production notes
- The proxy's key is the project's proxy key: it reaches these two routes and the QR route report of the pilot project only, and every other route refuses it. Keep it on the pilot proxy host only, never in a laptop or a shared vault entry, and revoke it when the pilot ends. Rotation and the replacement of an older staff key: Proxy keys.
- On-call is paged by the API's
ALERT migration_rollback_stuckline (one per rollback, afterIVORA_MIGRATION_ROLLBACK_ALERT_MINUTES, ten by default) and by the production watch's count of rollbacks open for more than ten minutes (WATCH_ROLLBACK_MINUTES). Roll the charger back by hand on the proxy host withpilot rollback. - The deploy checklist (the database migration, the key, the proxy files, its
pilot.portalblock and service, the lab rehearsal) is in the pilot proxy runbook in Ivora'sivora-migraterepository:pilot/README.md, "Production deploy checklist".



