Reference and AI

API versions and changelog

The Ivora API keeps the path major /v1. Within it, the contract is versioned by date (YYYY-MM-DD). The current version is 2026-10-15; 2026-10-14 remains supported.

Policy

  • Additive by default. New routes, new optional request fields, new response fields, new event types and new error codes ship without a new version. Write clients that ignore fields and event types they do not know.
  • Breaking changes are rare. Removing or renaming a field, changing its type or meaning, or changing a route's behaviour needs Ivora's sign-off and ships only as a new dated version. Your keys keep their pinned version until you move them.
  • Six months' notice. A deprecated field, route or version is announced here at least six months before its sunset date.
  • At most two supported versions at any time: the latest and the one before it.

Headers

HeaderDirectionMeaning
Ivora-VersionResponseThe version applied to this request. Every response carries it
Ivora-VersionRequest, optionalA version to apply, such as 2026-10-15. A version not listed below answers 400 invalid_api_version with details.supported_versions. Without it, your API key's pinned version applies
DeprecationResponseOnly on a deprecated route or a route with a deprecated field: when the deprecation was announced (RFC 9745, @<unix seconds>)
SunsetResponseWith Deprecation: the date after which the route or field may be removed (RFC 8594)
LinkResponseWith Deprecation: this changelog, rel="deprecation"

Webhook deliveries also carry Ivora-Version: the version the endpoint is pinned to.

Pinning

Every API key and webhook endpoint stores the version it is pinned to, shown as api_version in GET /v1/api-keys, the key creation response and webhook endpoint reads. A new key or endpoint is pinned to the latest version. Keys and endpoints created before versioning are pinned to 2026-10-14.

flowchart diagram; its source follows
Diagram source
flowchart LR
  Request[Request] --> Header{Ivora-Version sent?}
  Header -->|No| Pin[Key's pinned version]
  Header -->|Supported version| Applied[That version]
  Header -->|Anything else| Refused[400 invalid_api_version]
  Pin --> Response[Response with Ivora-Version]
  Applied --> Response

Versions

VersionStatusSunset
2026-10-15CurrentNone scheduled
2026-10-14SupportedNone scheduled

2026-10-15

Tariff price blocks (tariffs guide).

  • Changed in this version: tariff responses (GET, POST, PATCH and DELETE on /v1/tenants/{tenant_id}/tariffs) carry elements (OCPI 2.2.1 price blocks), tariff_alt_text, rules and time_zone. A simple tariff shows its one ENERGY element.
  • For every version (additive):
    • Tariffs carry is_complex and pricing_summary. A 2026-10-14 client sees a complex tariff as its base rate_minor_per_kwh plus these two fields.
    • Tariff creation and edits accept elements, tariff_alt_text, rules and time_zone. With elements, rate_minor_per_kwh is optional.
    • New routes: POST /v1/tenants/{tenant_id}/tariffs/{tariff_id}/quote and POST /v1/tenants/{tenant_id}/tariffs/simulate.
    • Bills accept pricing_group and merchant_validation. Bills on complex tariffs have billing_version: "elements-v1". Final bills carry cost_breakdown. The bill.finalized event carries the final bill, so it gains cost_breakdown and can carry billing_version: "elements-v1".
    • Group discounts and merchant validations compound: the merchant validation applies to the amount left after the group discount, then the session cost cap applies.
    • Bills on complex tariffs charge TIME over the transaction's charging time and PARKING_TIME over its idle time, as the charger reports them (charging and idle time); with too little data the whole transaction is charging time. Final bills on complex tariffs carry cost_breakdown.activity. Power and kWh restrictions use the session's averages over its charging time. A transaction with no start time cannot be priced by a time-dependent tariff.
    • New error codes: 422 tariff_invalid, 409 tariff_complex and 409 transaction_start_unknown.
  • Unchanged: simple tariffs read, write and bill exactly as in 2026-10-14. OCPI partners receive complex tariffs' elements and tariff_alt_text, never billing rules.

New API keys and webhook endpoints are pinned to 2026-10-15. Existing ones keep 2026-10-14 until you move them; send Ivora-Version: 2026-10-15 to try the new shape first.

Deprecations

Deprecated fields and routes are listed here with their announcement and sunset dates, and their responses carry the Deprecation headers.

WhatAnnouncedSunsetReplacement
allow_protected on configuration changes (ignored)2026-10-17—stations:admin

Changelog

Unreleased additions (2026-10-14 contract)

Additive; no new version.

  • stations:admin: a new elevated scope for protected configuration keys and for pushing a password to a connected charger (PUT .../credentials). stations:write still sets the password of an offline or new station (applied = stored); on a connected station it now answers 403 stations_admin_required and sends nothing. Team owners and admins and Ivora platform administrators hold it; API keys only when created with it. Connection keys (WebSocketURL, OCPPID, SecurityProfile, the OCPP 2.x network variables) also need confirm_connection_change: true. Password changes are limited to 5 per station per hour (429 credentials_rate_limited). New events station.credentials_changed and station.configuration_changed.

  • Idle time on bills: bills on complex tariffs now tell charging from idle time (OCPP 2.0.1 chargingState; OCPP 1.6 connector status and meter power). TIME is charged over charging time only and PARKING_TIME over idle time after charging, on final bills, live estimates and running-cost displays. Idle shorter than 2 minutes is charging time; time before the first charging (at least 2 minutes of it, else the first Charging (status or chargingState) or metered draw of any length) is waiting, charged as neither. A session that draws less than 1 Wh is all waiting (reason no_energy) and bills no time and no energy (the metered kWh is kept; simple per-kWh tariffs are unchanged). Final bills carry cost_breakdown.activity with the method, the seconds of each and the intervals. Generated tariff descriptions now read "per hour while charging" for TIME and "idle fee after charging" for PARKING_TIME. OCPI CDRs carry total_parking_time, and sessions and CDRs with idle time have one charging period per interval. Simple tariffs and existing final bills are unchanged.

  • Per-project charger URLs (preproduction only, not available in production; on for every project by default, Ivora staff may turn a project's off; in preproduction the project URL is the address chargers connect to): POST /v1/tenants/{tenant_id}/stations accepts charger_id instead of name, unique within the project; stations carry charger_id, username and connection_base_url; ?charger_id= filters the station list; PATCH accepts charger_id; projects carry charger_urls and connection_base_url. Error code project_urls_disabled. Station names are now limited to 36 characters, the charging core's own limit (longer ones could never be stored), and names starting with p<digits>- are reserved for these stations. Staff routes GET|PUT /v1/admin/tenants/{tenant_id}/charger-urls read the effective state (source: default or override) and set an override.

  • Enterprise invoicing: Ivora can switch a team to billing_mode: "enterprise". Its monthly usage-fee invoice is then sent with net terms (due_date, collection_method: "send_invoice") and paid by ACH debit, bank transfer or, if allowed, card; Canadian teams pay by bank transfer, pre-authorized debit in US dollars, or card. Every amount stays in US dollars. GET /v1/teams/{team_id}/billing gains billing_mode, invoice_terms, bank_account and bank_transfer. A project's billing profile gains billing_mode, bank_account and invoice_terms without the billing contacts, legal name or PO number, and no bank_transfer. Invoices gain collection_method, due_date, overdue, days_overdue, payment_processing, minimum_topup_minor and po_number. The usage estimate gains billing_mode, minimum_commitment_minor and minimum_topup_minor. Staff settings gain bank_country (us or ca); currency stays usd. New statuses: enterprise and suspended. New error code: 402 billing_suspended. New routes: POST /v1/teams/{team_id}/billing/bank-account/setup|sync and DELETE /v1/teams/{team_id}/billing/bank-account, plus the staff route PATCH /v1/admin/billing-teams/{team_id}/invoicing. Self-serve billing is unchanged.

  • Team billing: GET /v1/teams/{team_id}/billing reads the team's billing account (status, card, grace, discount, and each project's rate and current_month), GET /v1/teams/{team_id}/billing/invoices and GET /v1/teams/{team_id}/billing/usage its invoices and this month's estimate across every project, and POST /v1/teams/{team_id}/billing/payment-method/setup|sync and DELETE /v1/teams/{team_id}/billing/payment-method manage its card. Signed-in owners, admins and billing members only; API keys are refused. Admins now hold the billing:manage team permission (they already managed the card through any project's billing:write).

  • Project keys, operations and projects: API keys with project scopes belong to the project, not their creator, and are managed by the team's owners, admins and developers at GET|POST /v1/tenants/{tenant_id}/api-keys and DELETE /v1/tenants/{tenant_id}/api-keys/{key_id}. POST /v1/api-keys with project scopes now makes such a key, so operators and viewers no longer make project keys, and a key mixing project and workspace scopes is refused (422). GET /v1/api-keys lists your workspace keys and the project keys you made, with owner. GET /v1/tenants/{tenant_id}/operations lists the project's operations by every actor, and GET .../operations/{operation_id} reads another actor's operation within your read scopes. New scope webhooks:read lists webhook endpoints and deliveries (webhooks:write includes it; viewers and operators hold it). POST /v1/teams/{team_id}/projects, GET|PATCH /v1/tenants/{tenant_id} create, read and rename projects. Error code project_limit.

  • Teams: GET /v1/me/teams, GET /v1/me/invites, POST /v1/me/invites/{invite_id}/accept|decline, GET /v1/teams/{team_id}/members, PATCH|DELETE /v1/teams/{team_id}/members/{user_id} and GET|POST|DELETE /v1/teams/{team_id}/invites[/{invite_id}]. Signed-in tenant access now comes from team roles; existing grants are imported unchanged in effect. Error codes last_owner, already_member, invite_pending, invite_closed and invite_expired. The billing role holds no project scope: it reads only .../billing-profile, /invoices and /usage and manages the team's card.

2026-10-14

The first dated version. It is the /v1 contract as it was on 2026-10-14, with no behaviour change for existing callers.

  • Responses carry Ivora-Version. Requests may send it; an unsupported value answers 400 invalid_api_version.
  • API keys and webhook endpoints show their pinned api_version.
  • The OpenAPI document's info.version is the latest version date.