Write your first policy
Give a bookkeeper billing and nothing else, limit a shift to the morning, take one thing away with a deny, then ask why and preview a change first.
On this page
The policies Flowion provides cover the common cases. When they don't fit, write your own: a permission policy that allows exactly the permissions you name, for the hours you choose, and nothing else. Then give it to a member the way you would give them any policy, and ask the API why they can or can't do something.
This page makes three: a bookkeeper who sees and manages billing and nothing else, a morning shift that can send commands to chargers only on weekdays, and a deny that takes one thing away. For how policies, bindings and the provided policies fit together, read Permissions and policies first.
Before you start
You need
identity.policy.manage. The Owner holds it. Nobody else does until an Owner gives it to them, and a deny can never take it away from an Owner.A policy can only allow what you hold. To write a policy that allows
billing.account.manage, you must hold it yourself, in this organization. A policy that goes further is refused withpermission_policy.not_grantable. Every example below is for an Owner.Permission names are exact. There are no wildcards, so a policy never gains a permission by accident when a new one is added. Read the names from List permissions.
$MEMBERis the membership id fromGET /organizations/$ORG/members, not the user id.
Give a bookkeeper billing and nothing else
A policy is a name and a document. The document is version 1 and a list of statements. Each statement says allow or deny and names the permissions it covers.
200Response
Keep the id: it is the policyId you give to a member.
Leaving out projectId gives the policy across the whole organization, which is where billing lives. The bookkeeper still holds what every member holds, and can now see and manage the organization's billing. They can't invite people, make API keys or touch a charger, because nothing allows it. Nothing is allowed unless a policy says so.
Change your mind later with Replace a permission policy. The version goes up and everyone who holds the policy holds the new document on their very next request.
Ask why: explain
Don't guess what a member can do. Ask. Explain runs the same code that answers every request, so it can't disagree with what really happens. It needs identity.policy.explain.
200Response
(considered is shortened here: it also lists every other policy the member holds.) Read it in this order:
verdictisalloworrefuse.reasonsays why.allowedanddeniedname a statement. For a refusal,no-allow-foundmeans no policy allows it. The others are listed in Errors, pagination and versioning.decidedByis the statement that decided: which policy, at which organization or project, and the statement's position (statementIndexcounts from 0). It is absent when nothing decided, because nothing allowed it.consideredis every policy that applied, with each statement and, for one that didn't match, the condition that stopped it:permission,mode,timeWindoworattributes.
Ask about something the bookkeeper may not do and you get the other half:
The answer is "verdict": "refuse" and "reason": "no-allow-found", with no decidedBy. Add "projectId" to ask about one project, "mode": "sandbox" to ask about sandbox, and "actingAs": "assistant" to ask about the member's assistant, which can do no more than the member and no more than read.
Limit a policy to a time of day
Add conditions to a statement to say when it applies. This morning shift can read and send commands to chargers (which includes starting and stopping a charge) from 06:00 to 11:00 on weekdays, on Stockholm time:
Give it at one project, so the shift reaches that site and no other:
What a condition means:
| Condition | Rule |
|---|---|
time_window | days are mon to sun, from and to are HH:MM on a 24-hour clock, and zone is an IANA zone name and always required, so a window never depends on where Flowion's servers are. It is read on Flowion's clock at the moment of the request. A from later than to runs past midnight, and belongs to the day it starts on |
mode | live, sandbox or both. Without it a statement applies in live only: add "mode": ["live", "sandbox"] to cover your sandbox too |
attributes | Limits a statement to named project_id, location_id or charger_id values. Flowion fills these in from the resource the request is about; a request cannot supply them |
Every condition in a statement must hold. There is no "or" inside a statement; write two statements instead.
Ask at 14:00 on a Tuesday and explain shows exactly what stopped it:
(Shortened to the relevant fields.)
Take something away with a deny
A deny beats any allow, from any policy, at the organization or in a project. Use one to keep a single permission away from someone who otherwise holds a lot:
Writing a policy that denies, and giving one, each needs identity.policy.deny as well, a separate right that only the Owner holds until they give it out. A deny can't name the few permissions only a transfer of ownership can move, such as identity.binding.manage: that is refused with permission_policy.deny_on_owner_floor, so an organization can't lock itself out of its own access.
Preview a change: simulate
Before you give or take a policy, see what it would change. Simulate decides every permission twice, under what the member holds now and under what they would hold, and returns the difference. It changes nothing.
200Response
To preview taking something away, send the removeBindingIds you read from GET /organizations/$ORG/members/$MEMBER/bindings. Add projectId to evaluate at a project, and mode for sandbox. Conditions are weighed at the moment you ask, so a window that is closed now gains nothing yet.
When a policy is refused
A write that breaks a rule is refused with a code you can switch on, and the message says which statement:
| Code | Status | Meaning |
|---|---|---|
permission_policy.invalid_document | 422 | The document is not valid: no statements, more than 50, a bad time window or an empty condition |
permission_policy.unknown_permission | 422 | A statement names something that is not in the catalogue |
permission_policy.name_taken | 409 | Another of your policies, or one Flowion provides, already has this name |
permission_policy.not_grantable | 403 | The policy allows something you don't hold |
The full list, with statuses, is in Errors, pagination and versioning.