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
PreviousInteractive API referenceNextErrors, pagination and versioning
Was this page helpful?
Edit this page

Roles and permissions

Every organization-scoped and environment-scoped endpoint runs the same check: does the caller have at least this role, on this organization or environment? Set up your workspace introduces the three roles in passing; this page is the full picture, including how to read a 403 that does not tell you why.

The three roles#

Lowercase on the wire — owner, admin, user — and each one implies the ones below it:

RoleReaches
ownerEvery environment in the organization, implicitly. Billing, member management, organization deletion. Exactly one per organization, assigned at creation, no transfer operation.
adminEvery environment in the organization, implicitly. Everything but the owner-only organization actions.
userOnly the environments explicitly granted to them.

"Reaches every environment implicitly" is not a convenience default — owner and admin do not hold per-environment grants at all, because they do not need them. Only a user-role membership has anything to grant.

Environment-level access grants#

A user-role member starts with access to nothing. Give them an environment:

Shell
curl -X POST "$API/organizations/$ORG/members/$MEMBER/environment-access" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"environment_id": "..."}'

Granting access to an owner or admin membership is rejected outright — they already have it, implicitly, and a grant that does nothing would just be confusing to read later. This is how you hand a contractor staging and nothing else: add them to the organization as user, then grant only that one environment.

A member can always read their own membership and their own environment grants (GET /organizations/{organization_id}/members/me, and the same member id's /environment-access), regardless of role — that exemption covers reading only. Granting and revoking access stays an Owner/Admin-only action, and the exemption is keyed to the caller's own membership id, resolved server-side from the verified token — never to a member_id the client asserts, so it cannot be used to read anyone else's grants.

API keys do not inherit a role's implicit reach#

Everything above describes a human membership. An API key carries one of the same three role names, but it participates in this model differently in one specific way that is easy to assume works like a membership and does not: a key never gets an owner or admin's implicit "every environment in the organization" reach, regardless of its role. An owner-scoped key is checked against the one environment it was minted for, exactly like a user-scoped one — role only ever affects what it may do inside that environment, never how many environments it can reach. See Authentication for how a key is minted and why this is enforced as a hard equality check independent of role.

A key also answers no organization-wide question at all — nothing that is not scoped to an environment. Listing an organization's members, listing its environments, anything behind an organization-level role check: a key gets 404 there, the same response a non-member gets, regardless of the role it carries.

Reading a 403 — and a 404 that means the same thing#

Two different failures collapse to two different status codes, and the choice is deliberate:

  • You are not a member of the organization at all → 404. Not 403. A non-member cannot even confirm the organization exists, so the response looks identical to "no such organization."
  • You have a user-role membership with no grant for this specific environment → 404 as well, for the identical reason: a user-role member should not be able to tell "this environment does not exist" from "it exists but I can't see it."
  • You are a member, with a role, but it falls short of what the endpoint requires → 403. You already know the resource exists — you got this far — so there is nothing left to hide.

So a 403 always means: you can see this, you're just not allowed to do it. A 404 on a resource you swear you just created usually means your token's role does not extend to it — check whether it needs an environment grant before assuming it was deleted.

Every error carries the shared body ({ "error": "forbidden", "message": "..." } for a 403, { "error": "not_found", "message": "..." } for the 404 case) — see Errors, pagination and versioning.

The check fails closed#

If the identity service that resolves membership cannot be reached, the request is refused — never treated as authorized. An outage in the authorization path shows up as errors, not as everyone suddenly having access.

Next: Sending commands to a charger, which uses this same role model at the user floor for every command.