GuidesAPI reference
Introduction
  • Overview
Orientation
  • What a CSMS does
  • OCPP in ten minutes
  • The vocabulary
  • Lifecycle of a charging session
  • Which OCPP version am I dealing with?
  • How Flowion maps onto OCPP
Getting started
  • Quickstart
  • Core concepts
  • Set up your workspace
  • Connect a simulated charger
  • Connect a real charger
  • Environments
API guide
  • Authentication
  • Your first API call
  • Interactive API reference
  • Roles and permissions
  • Errors, pagination and versioning
  • Live updates
  • Sending commands to a charger
  • Rate limits
How-to guides
  • Register and provision a charger
  • Read and change charger settings
  • Authorize drivers with RFID cards
  • Start and stop sessions remotely
  • Track sessions and energy
  • Take a charger in and out of service
  • Unlock a stuck connector
  • Reset a charger
  • Send vendor-specific data
  • Keep a site within its electrical limits
  • Use day-ahead electricity prices
  • Receive events with webhooks
  • OCPP feature coverage
Troubleshooting
  • 8 more coming soon
    • My charger will not connectSoon
    • It connects, then dropsSoon
    • A command timed out or returned 409Soon
    • A card is not being authorizedSoon
    • My transaction looks wrongSoon
    • Meter values are missing or sparseSoon
    • Reading the OCPP message logSoon
    • Known limitationsSoon
Reference
  • 5 more coming soon
    • GlossarySoon
    • Endpoint indexSoon
    • OCPP message supportSoon
    • Status and error codesSoon
    • ChangelogSoon
Flowion Docsflowion.io
PreviousRoles and permissionsNextLive updates
Was this page helpful?
Edit this page

Errors, pagination and versioning

Three cross-cutting conventions apply to every endpoint in this API, without exception. Once you know them you can guess a lot about an endpoint you have never called.

Errors#

Every error response — 400, 401, 403, 404, 409, 422, 500, whatever the status — carries the same body:

JSON
{
  "error": "forbidden",
  "message": "you do not have the required role for this action"
}

error is a stable, machine-readable snake_case code, safe to switch on. message is prose for a human and can change wording between releases — never parse it. A 401 raised by token verification itself (missing header, expired token, JWKS unreachable) carries the identical shape, even though it comes from a different layer than the rest of the API.

A 500 never leaks what actually went wrong — you always get a fixed { "error": "internal_error", "message": "an unexpected error occurred" }, with the real error logged on our side, not yours. There is no machine-readable request_id in the body today; if you need to report a failure, include the request's timestamp, the endpoint, and the body you sent.

Not every failure mode carries this body

The shared shape covers everything a handler in this API produces. A malformed request — for instance, a body that is not valid JSON at all — can fail before reaching a handler and return a plain-text response instead. If you see a non-JSON error body, the fix is almost always to check the request itself is syntactically valid JSON before diagnosing further.

Pagination#

Every list endpoint returns the same envelope:

JSON
{
  "data": [ ... ],
  "page": 1,
  "per_page": 20,
  "total": 137
}
  • page is 1-indexed, not 0-indexed.
  • per_page defaults to 20 and is capped at 100 — a per_page above the cap is not rejected, it is silently clamped.
  • total is the count of the filtered set, so it moves with whatever query parameters you passed (a status filter, a search term), not the whole collection.

There is no cursor and no next_page_token. Page through a large collection by incrementing page until data comes back shorter than per_page.

Versioning#

Every route lives under a /v1 prefix — /livez and /readyz are the only exceptions, deliberately: they are infrastructure liveness checks, not part of the data contract, so they stay reachable the same way across any future version.

The rule going forward is additive-within-a-version, breaking-only-with-a- new-version:

  • Adding a field to a response, a new optional request field, a new endpoint, or a new error code is safe and ships inside /v1 — you should not need to change anything to keep working.
  • Removing or renaming a field, changing a field's type, or tightening what a request accepts is a breaking change. It gets /v2, added alongside /v1 rather than replacing it — /v1 keeps working for whoever is still on it.

There is no /v2 yet. Everything in this API today is /v1.

Next: Live updates, or Roles and permissions if you have not read that yet.