Bulk operations

Public API — Bulk Operations

Run one maintenance command — firmware update, OCPP config change, reset, availability change — across a fleet of charging stations, immediately or scheduled, with per-station progress tracking

Base path: /api/v1/bulk-operations. Permissions: bulk-operation:read for reads, bulk-operation:create for create, update, and cancel.

Model

A bulk operation fans out into one item per charging station. The operation aggregates its items' outcomes:

stateDiagram-v2
    [*] --> PENDING: created (scheduled)
    PENDING --> RUNNING: scheduledAt reached / immediate
    PENDING --> CANCELLED: cancel
    RUNNING --> CANCELLED: cancel
    RUNNING --> COMPLETED: all items succeeded
    RUNNING --> PARTIALLY_FAILED: some items failed
    RUNNING --> FAILED: all items failed

The operation DTO carries live counters: totalCount, successCount, failureCount, partialFailureCount, pendingCount, plus timestamps (scheduledAt, startedAt, completedAt, cancelledAt) and blame fields (createdByApiKeyId / createdByUser, same for cancellation).

Item statuses: PENDINGIN_PROGRESS → (WAITING_FOR_COMPLETION) → SUCCEEDED / PARTIALLY_FAILED / FAILED, plus SKIPPED and CANCELLED. WAITING_FOR_COMPLETION means the command was delivered and the backbone now waits for the station's asynchronous confirmation (firmware installed, station rebooted).

Operation types

type selects the command; payload must match it:

typepayload
FIRMWARE_UPDATE{ "firmwareId": "<uuid>", "retries"?: n, "retryInterval"?: seconds }
OCPP_CONFIG_UPDATE{ "ocppVersion": "OCPP_1_6" | "OCPP_2_0_1", "configUpdates": [{ "key", "value", "component"? }] }
CHARGING_STATION_RESET{ "resetType": "SOFT" | "HARD" }
CHANGE_AVAILABILITY{ "availabilityType": "OPERATIVE" | "INOPERATIVE" }

Notes:

  • FIRMWARE_UPDATE — the firmware must be registered (see /api/v1/firmware) and in status PRODUCTION_READY, LIVE, or STABLE; other statuses are rejected with 403 unless the key holds firmware:test-unapproved.
  • OCPP_CONFIG_UPDATEcomponent applies to OCPP 2.0.1 variables only.
  • postExecutionResetType (optional, top-level) reboots each station after the command: for FIRMWARE_UPDATE it must be HARD; for CHARGING_STATION_RESET it is not allowed (the operation is the reset).

Endpoints

OperationEndpointPermission
CreatePOST /bulk-operations201bulk-operation:create
ListGET /bulk-operationsbulk-operation:read
Get one (incl. chargingStationIds, firmware details)GET /bulk-operations/{id}bulk-operation:read
Update (full replace)PUT /bulk-operations/{id}bulk-operation:create
CancelPOST /bulk-operations/{id}/cancelbulk-operation:create
List itemsGET /bulk-operations/{id}/itemsbulk-operation:read
Get one itemGET /bulk-operations/{id}/items/{itemId}bulk-operation:read

List filters (query): chargingStationId, name, type, statuses (array), scheduledAtAfter / scheduledAtBefore; standard pagination envelope.

Creating an operation

curl -s -X POST -H "x-api-key: $API_KEY" -H "Content-Type: application/json" \
  -d '{
        "type": "OCPP_CONFIG_UPDATE",
        "name": "Fleet heartbeat interval 300s",
        "scheduledAt": "2026-07-15T02:00:00Z",
        "chargingStationIds": ["<station-uuid>", "<station-uuid>"],
        "payload": {
          "ocppVersion": "OCPP_1_6",
          "configUpdates": [{ "key": "HeartbeatInterval", "value": "300" }]
        }
      }' \
  "https://<host>/api/v1/bulk-operations"

Scheduling rules:

  • scheduledAt: null → runs immediately (the response has immediate: true).
  • Otherwise scheduledAt must be in the future.
  • Conflict window: a station cannot appear in two bulk operations scheduled within 10 minutes of each other — the create/update is rejected (400, reason names the conflicting operation).

Tracking progress

Poll the operation for aggregate counts, drill into items for per-station outcomes:

curl -s -H "x-api-key: $API_KEY" \
  "https://<host>/api/v1/bulk-operations/<id>/items?statuses[]=FAILED&page=1&limit=50"

Each item carries:

  • status and timestamps (publishedAt, startedAt, completedAt),
  • chargingStation (identity of the target),
  • executionDetails — type-specific progress (e.g. firmware status notifications, per-key config results),
  • on failure: errorCode, errorMessage, errorDetails (e.g. station offline, OCPP rejection).

Terminal operation statuses: COMPLETED (all succeeded), PARTIALLY_FAILED (mixed), FAILED (all failed), CANCELLED.

Rescheduling and cancelling

Update (PUT) is a full replace of name, scheduledAt, chargingStationIds, and payload, and is only accepted while the operation:

  • is still PENDING, and
  • its scheduledAt has not passed yet.

type is immutable — create a new operation instead. The new scheduledAt must be in the future and passes the same 10-minute conflict check.

Cancel works on PENDING or RUNNING operations (400 otherwise):

  • items still PENDING or WAITING_FOR_COMPLETION become CANCELLED and count as failures;
  • items already IN_PROGRESS on a station are not interrupted.

Errors

StatusCause
400scheduledAt in the past; station in another operation within the 10-minute window; update on a non-PENDING or already-started operation; type change on update; cancel on a finished operation; empty chargingStationIds
403Missing permission, or firmware not in an allowed status
404Unknown operation / item / station ID

Domain errors use the { "reason": "...", "issues": [...] } shape (see General guide).

Related



Did this page help you?