Log inTalk to us

Plan a batch of chargers to move

post/v1/migrations/{migration_id}/batches
Requires an API key

A draft batch of the migration's chargers (station_ids, or every charger at location_ids; 500 at most), each in one open batch at a time. Pacing: wave_size chargers move at a time; the next wave starts interval_seconds after the last one settled; the first wave is a canary of canary chargers, after which the batch pauses itself; a share of failures above max_failure_ratio pauses it; a charger refused as busy (charging) is retried in a later wave until idle_timeout_minutes, then blocked, never forced; a move the proxy has not answered after move_timeout_minutes expires and counts as failed. Needs migration:plan (client, support, partner; a key needs stations:write); a narrowed team plans the chargers at its sites only. A retry with the same Idempotency-Key returns the same batch; nothing is created unless its audit entry is stored. Needs an active migration (409 migration_closed when it is paused, completed or archived). Errors: 404 migration_charger_not_in_migration, 404 migration_not_found, 409 migration_charger_in_batch, 409 migration_closed, 503 migration_audit_unavailable. See Errors and statuses.

Path parameters

  • migration_idstringRequired
    Pattern ^mig_[0-9a-f]{32}$

Headers

  • idempotency-keystringRequired

Request body

  • namestringRequired
    A label, such as "Depot, night 1".
    1–120 characters
  • station_idsarray of integer | null
    The chargers to move.
  • location_idsarray of integer | null
    Or every migration charger at these sites (500 at most).
  • pacingPacingInput
    Child attributes
    • wave_sizeinteger
      Chargers moved at a time: one wave's move requests go out together.
      Default 5 · Minimum 1 · Maximum 50
    • interval_secondsinteger
      Seconds after a wave settled (every charger moved, refused or failed) before the next one starts.
      Default 60 · Minimum 0 · Maximum 86400
    • canaryinteger
      The first wave's size; the batch then pauses itself until someone resumes it. 0: no canary wave.
      Default 2 · Minimum 0 · Maximum 50
    • max_failure_rationumber
      After a wave, the batch pauses itself when more than this share of the chargers it tried failed.
      Default 0.2 · Minimum 0 · Maximum 1
    • idle_timeout_minutesinteger
      A charger the proxy refused because it is charging is retried in a later wave (5 minutes later at the earliest), until this long after its first attempt; then it is blocked (not_idle), never forced.
      Default 60 · Minimum 1 · Maximum 1440
    • move_timeout_minutesinteger
      A move request the proxy has not answered this long after it was sent expires and counts as a failed charger of its wave.
      Default 30 · Minimum 1 · Maximum 1440

Returns · 201

  • idstringRequired
    bat_...
  • migration_idstringRequired
  • namestringRequired
  • statusenumRequired
    draft, submitted (waiting for the client's approval), approved, running, paused, completed, cancelled (moved chargers stay on Ivora) or rolled_back (a rollback of the batch was asked).
    One of draft, submitted, approved, running, paused, completed, cancelled, rolled_back
  • pacingPacingRequired
    Child attributes
    • wave_sizeintegerRequired
    • interval_secondsintegerRequired
    • canaryintegerRequired
    • max_failure_rationumberRequired
    • idle_timeout_minutesintegerRequired
    • move_timeout_minutesintegerRequired
  • countsobjectRequired
    Chargers per step, and total (of the caller's sites when narrowed).
  • waveintegerRequired
    Waves started.
  • wave_openbooleanRequired
    A wave's moves are out and not all answered.
  • next_wave_atstring | nullRequired
    When the next wave may start (running, between waves).
  • paused_reasonenum | nullRequired
    Why a paused batch paused: manual, canary (after its first wave), failure_ratio, rollback_requested (a rollback anywhere in the migration), runner_unhealthy or migration_paused (the migration was paused).
    One of manual, canary, failure_ratio, rollback_requested, runner_unhealthy, migration_paused
  • paused_atstring | nullRequired
  • job_idstringRequired
    The job of its moves (GET .../jobs/{job_id}).
  • created_bystringRequired
  • created_atstringRequired
  • updated_atstringRequired
  • submitted_bystring | nullRequired
  • submitted_atstring | nullRequired
  • approvalobject | nullRequired
    The client's approval; null until approved, and again after an edit.
    Child attributes
    • bystringRequired
    • roleenumRequired
      One of client, support, partner, observer
    • team_idstring | nullRequired
    • atstringRequired
    • chargersintegerRequired
      Chargers in the batch when it was approved.
    • digeststringRequired
      SHA-256 of the approved charger set and pacing; any edit revokes the approval.
  • started_bystring | nullRequired
    Who started it, or resumed it last: its moves are asked in their name.
  • started_atstring | nullRequired
  • finished_atstring | nullRequired
  • duration_snumber | nullRequired
    Seconds from its start to its end (or now while it runs).
  • rollbacksarray of BatchRollbackRequired
    Rollback jobs of its chargers, with their timings.
    Child attributes
    • job_idstringRequired
      The rollback job (GET .../jobs/{job_id}).
    • requested_atstringRequired
    • requested_bystringRequired
    • reasonstringRequired
    • chargersintegerRequired
      Chargers it rolls back (skipped ones left out).
    • appliedintegerRequired
      Chargers switched back by the proxy.
    • statusenumRequired
      One of running, succeeded, failed
    • last_applied_atstring | nullRequired
      When the proxy switched the last of them back.
    • switch_snumber | nullRequired
      Seconds from the request to the last switch back.
  • chargersarray of BatchCharger | nullRequired
    Each charger's step; null in a list.
    Child attributes
    • station_idintegerRequired
    • charger_keystringRequired
    • location_idinteger | nullRequired
    • stepenumRequired
      pending: waiting for its wave (detail busy: refused while charging, retried at retry_at). moving: its move request is out. moved: on Ivora. blocked: not moved (detail not_idle, not_proxied, shadow, rollback_pending). failed: the proxy failed or refused it, or did not answer in time (detail refused_parity, failed, expired). superseded: a rollback took over. skipped: the batch was cancelled or rolled back first, or the charger left the migration (detail).
      One of pending, moving, moved, blocked, failed, superseded, skipped
    • detailstring | nullRequired
    • waveinteger | nullRequired
      The wave of its latest move request.
    • attemptsintegerRequired
      Move requests sent for it (a busy charger is retried).
    • request_idstring | nullRequired
      Its latest move request (rr_...).
    • requested_atstring | nullRequired
    • retry_atstring | nullRequired
      A busy charger's next attempt at the earliest.
    • settled_atstring | nullRequired
    • move_snumber | nullRequired
      Seconds from its move request to the proxy's report that it moved.
POST /v1/migrations/{migration_id}/batches
curl --fail-with-body -X POST "https://api.ivoracharge.com/v1/migrations/mig_0123456789abcdef0123456789abcdef/batches" \
  -H "Authorization: Bearer $IVORA_API_KEY" \
  -H "Idempotency-Key: order-2026-09-30-0001" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "bay-01"
}'
Response · 201JSON
{
  "id": "id_0123456789abcdef",
  "migration_id": "migration_0123456789abcdef",
  "name": "bay-01",
  "status": "draft",
  "pacing": {
    "wave_size": 1,
    "interval_seconds": 1,
    "canary": 1,
    "max_failure_ratio": 1.5,
    "idle_timeout_minutes": 1,
    "move_timeout_minutes": 1
  },
  "counts": {},
  "wave": 1,
  "wave_open": false,
  "next_wave_at": "2026-09-30T17:00:00Z",
  "paused_reason": "manual",
  "paused_at": "2026-09-30T17:00:00Z",
  "job_id": "job_0123456789abcdef",
  "created_by": "string",
  "created_at": "2026-09-30T17:00:00Z",
  "updated_at": "2026-09-30T17:00:00Z",
  "submitted_by": "string",
  "submitted_at": "2026-09-30T17:00:00Z",
  "approval": {
    "by": "string",
    "role": "client",
    "team_id": "team_0123456789abcdef",
    "at": "string",
    "chargers": 1,
    "digest": "string"
  },
  "started_by": "string",
  "started_at": "2026-09-30T17:00:00Z",
  "finished_at": "2026-09-30T17:00:00Z",
  "duration_s": 1.5,
  "rollbacks": [
    {
      "job_id": "job_0123456789abcdef",
      "requested_at": "2026-09-30T17:00:00Z",
      "requested_by": "string",
      "reason": "string",
      "chargers": 1,
      "applied": 1,
      "status": "running",
      "last_applied_at": "2026-09-30T17:00:00Z",
      "switch_s": 1.5
    }
  ],
  "chargers": [
    {
      "station_id": 1,
      "charger_key": "string",
      "location_id": 7,
      "step": "pending",
      "detail": "string",
      "wave": 1,
      "attempts": 1,
      "request_id": "request_0123456789abcdef",
      "requested_at": "2026-09-30T17:00:00Z",
      "retry_at": "2026-09-30T17:00:00Z",
      "settled_at": "2026-09-30T17:00:00Z",
      "move_s": 1.5
    }
  ]
}
Requires an approved production API keyProduction access starts with a request, a short meeting with Ivora and a card on file, after which Ivora staff grant your account access. How to request access
Try itproduction · api.ivoracharge.com

Paste a production api key (iv_live_…). Create keys in Developer tools. The key stays in this page's memory, is sent only to https://api.ivoracharge.com and clears when you reload. Test keys are refused.

POST is a write: you confirm the method, path and project before it is sent, with a generated Idempotency-Key.