Members & grants
Account roles (owner/admin/member), per-company grants with an access level (view/operate), and handing over ownership.
Members are the people with access to an account. Each member has one account
role; a MEMBER additionally has per-company grants that decide which NIFs they can
see and operate. Where members sit in the model, and when you want a managed account
instead, is in the Overview.
Every path on this page hangs off the account — /v1/accounts/{account_id}/members/… —
because a membership belongs to the account, not to a NIF. All of them require OWNER or
ADMIN of that account (403 MEMBER_MANAGEMENT_FORBIDDEN otherwise),
plus the members:read or members:write scope.
Changing people needs your live key. Members and grants are shared between Test and
Live, so every write on this page made with a beel_sk_test_ key answers
403 LIVE_CREDENTIAL_REQUIRED. Reading works with either key. See
Test and live keys.
Two independent axes
Role and access are not the same question, and confusing them is the usual mistake:
- Account role — who administers the account. Billing, API keys, inviting people.
- Access level — how much someone can do with one NIF. Read, or read and write.
| account_role | Administers | Reaches |
|---|---|---|
OWNER | Full control: billing, API keys, members, and handing ownership over. Exactly one per account. | Every NIF, implicitly. |
ADMIN | Everything an OWNER can do except handing ownership over. | Every NIF, implicitly. |
MEMBER | Nothing. No billing, no keys, no member management. | Only the NIFs granted to them, at the level of each grant. |
OWNER and ADMIN reach every NIF of the account implicitly. Only a MEMBER is
scoped NIF by NIF, through grants, at one of these levels:
| access_level | What the holder can do |
|---|---|
VIEW | Read their invoices, customers, products, series and fiscal data. |
OPERATE | Everything in VIEW, plus creating and editing them. Issuing on their behalf additionally requires a signed fiscal representation from the account holder. |
A grant takes VIEW or OPERATE. NONE only exists for managed
accounts: a grant that grants nothing is not a
grant, so you remove access by deleting the grant, not by downgrading it.
Grants govern people who use the dashboard. API keys are created by an OWNER or ADMIN
and act with that reach; a MEMBER cannot create keys.
OWNER/ADMIN cannot receive grants. They already reach everything, so their grant
list is always empty. Grants apply to MEMBERs only — trying otherwise is
422 GRANTS_ONLY_FOR_MEMBER.
Who is in the account
GET /v1/accounts/{account_id}/members lists them; GET …/members/{member_id} returns
one, in the same shape. Fields and types: List account members.
curl https://app.beel.es/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6/members \
-H "Authorization: Bearer beel_sk_live_xxx"Two ids come back and they are not interchangeable:
member_idis the handle for every call on this page. It identifies this person in this account.person_idis the stable identity of the human, the same across every account they belong to. Use it to recognise the same person across the accounts you manage — you never pass it to an endpoint.
Each member also carries a permissions block (can_change_role, assignable_roles,
can_remove, can_transfer_ownership, can_edit_grants). Drive your UI from it instead
of re-implementing the role policy; the API enforces the same rules regardless of what you
render.
Grant a member access to one NIF
PUT /v1/accounts/{account_id}/members/{member_id}/grants/{company_id} —
members:write, live key. The company is in the path, so the body carries only the
level. Returns 200 with the grant:
curl -X PUT \
https://app.beel.es/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6/members/6f8c1e10-3b2a-4c9d-8e7f-1a2b3c4d5e6f/grants/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer beel_sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{ "access_level": "OPERATE" }'One call touches one NIF. This PUT creates the grant or changes its level, and
leaves the member's other grants exactly as they were. To set up access to three NIFs you
make three calls — there is no endpoint that replaces the whole set at once, so a
concurrent edit by a colleague can never be silently wiped by your request.
Revoking is the mirror image — DELETE on the same path, 204:
curl -X DELETE \
https://app.beel.es/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6/members/6f8c1e10-3b2a-4c9d-8e7f-1a2b3c4d5e6f/grants/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer beel_sk_live_xxx"GET …/members/{member_id}/grants lists what a member currently holds — empty for
OWNER/ADMIN. See Grant a member access to a company and
Revoke a member's access.
| When | Error | Status |
|---|---|---|
The target is an OWNER or ADMIN. | GRANTS_ONLY_FOR_MEMBER | 422 |
| The NIF hangs off another account — including one you manage. | GRANT_COMPANY_NOT_IN_ACCOUNT | 422 |
access_level is NONE or anything other than VIEW / OPERATE. | VALIDATION_ERROR | 422 |
Change a member's role
PATCH /v1/accounts/{account_id}/members/{member_id} — members:write, live key,
body { "account_role": "ADMIN" }. See Change a member's role.
The only assignable roles are ADMIN and MEMBER. OWNER is never accepted here — it
returns 422 OWNER_ROLE_NOT_ASSIGNABLE, because ownership moves only
through the handover below (from the dashboard), never by setting a role.
Promoting a MEMBER to ADMIN makes their grants irrelevant — they now reach every NIF
implicitly. Demoting back to MEMBER restores whatever grants they had, so check them
after the change.
The last OWNER cannot be demoted, and DELETE …/members/{member_id} cannot remove them
either: the account always keeps exactly one (422 LAST_OWNER_PROTECTED).
To step down, hand ownership over first.
Hand over ownership
PUT /v1/accounts/{account_id}/owner is dashboard-only. No API key can hand an account
over, a live one included: the call answers
403 OPERATION_REQUIRES_SESSION, so a leaked key can never take the
account. The current OWNER does it from Settings → Members; the member becomes
OWNER and the caller drops to ADMIN. An ADMIN who tries gets
403 OWNER_ROLE_RESERVED.
Repeating it once that member already owns the account returns the same 204, so a retry
after a timeout is safe. See Set the account owner.
Provisioning accounts for others? You do not hand them over with this call: the holder takes ownership by redeeming a claim token.
Related
Connect Stripe
Connect Stripe per company (NIF) so its payments auto-generate invoices under the right NIF — for the companies you own and, white-label, for the managed accounts you provision.
Invitations
Invite a person to the account by copy-link or email, assign a role and initial grants, and revoke pending invitations.