Skip to content

Tenant Roles & Organisation Best Practices

This guide shows a recommended default model you can seed into each tenant to support multi‑level structures such as Platform Owner → Distributor → Reseller → Operator → Customer → End User.

Use this as a starting point. Tenants may rename roles and adjust permissions to match their governance.


1) Organisation Hierarchy Model

Within a tenant, create an organisation tree that mirrors the commercial chain. Example:

Root (Tenant)
├─ Distributor: Acme Distributors
│  ├─ Reseller: Voltify UK
│  │  ├─ Customer: GreenFleet Ltd
│  │  │  ├─ Site: Depot A
│  │  │  └─ Site: HQ Car Park
│  └─ Reseller: ZapCo Ireland
└─ Direct Customers
   ├─ Customer: City Council
   │  └─ Sites: Depot East, Depot West
   └─ Customer: Retail Park North

Rules of thumb - Keep billing/contract entities at Distributor/Reseller/Customer nodes. - Keep physical assets (charge points) at Site (Location) nodes. - Prefer many Sites over nested sub‑sites for clearer SLA ownership.


2) Seeded Roles & Scope Bundles

Map roles to scopes (examples assume chargepoint/tenant scopes). Adjust to your needs.

Platform (outside tenant)

  • PLATFORM_ADMIN (via api‑auth) → cross‑tenant administration with ?as_tenant=

Tenant‑level defaults

  • TENANT_ADMIN
  • Full read/write across tenant entities.
  • Scopes: TENANT_READ TENANT_WRITE DOMAIN_* ORG_* LOC_* USER_* ROLE_WRITE PERM_WRITE MEMBERSHIP_* APPROVAL_* AUDIT_READ

    • Chargepoint read/write: CP_DEVICE_* CP_CONNECTOR_* CP_LOGS_* CP_ADMIN_SETTINGS
  • TENANT_MANAGER

  • Operates org structure, locations, and users; not roles/permissions.
  • Scopes: ORG_READ ORG_WRITE LOC_READ LOC_WRITE USER_READ USER_WRITE MEMBERSHIP_READ MEMBERSHIP_WRITE

  • TENANT_VIEWER

  • Read‑only across tenant domain.
  • Scopes: TENANT_READ ORG_READ LOC_READ USER_READ AUDIT_READ

Channel roles (for hierarchy nodes)

  • DISTRIBUTOR_ADMIN
  • Manage child Resellers and Customers under a Distributor org.
  • Scopes: ORG_READ ORG_WRITE LOC_READ USER_READ USER_WRITE MEMBERSHIP_* (scoped to subtree via portal policy)

  • RESELLER_ADMIN

  • Manage Customers under a Reseller org.
  • Scopes: ORG_READ ORG_WRITE LOC_READ LOC_WRITE USER_READ USER_WRITE MEMBERSHIP_* (subtree)

Customer roles

  • CUSTOMER_ADMIN
  • Manage Sites, users, and memberships for the customer org.
  • Scopes: ORG_READ LOC_* USER_* MEMBERSHIP_* CP_DEVICE_READ CP_CONNECTOR_READ CP_TXN_READ CP_LOGS_READ + CP_DEVICE_EDIT

  • SITE_MANAGER

  • Operate a specific Site (Location) and devices under it.
  • Scopes: LOC_READ USER_READ CP_DEVICE_READ CP_CONNECTOR_READ CP_TXN_READ CP_LOGS_READ CP_DEVICE_EDIT CP_CONNECTOR_EDIT

  • OPERATOR

  • Day‑to‑day charging ops (remote start/stop/reset if enabled).
  • Scopes: CP_DEVICE_READ CP_TXN_READ CP_OPS_REMOTE_START CP_OPS_REMOTE_STOP CP_OPS_RESET

  • END_USER

  • Self‑service for personal RFID/app tokens (if exposed), limited read on their sessions.
  • Scopes: USER_READ and per‑user session reads (narrow).

Tip: If you manage charging profiles/schedules centrally, add CP_ADMIN_SETTINGS to ADMIN/MANAGER roles only.


3.1 Create tenant (platform)

POST /api/tenant/v1/tenants
{ "slug": "acme", "name": "Acme CPO" }

3.2 Seed roles & permissions

POST /api/tenant/v1/tenants/acme/roles
{ "name": "TENANT_ADMIN" }
# repeat for TENANT_MANAGER, TENANT_VIEWER, DISTRIBUTOR_ADMIN, RESELLER_ADMIN, CUSTOMER_ADMIN, SITE_MANAGER, OPERATOR, END_USER

POST /api/tenant/v1/tenants/acme/roles/{roleId}/permissions
{ "permission_ids": ["TENANT_READ","ORG_WRITE","LOC_WRITE","CP_DEVICE_READ","CP_DEVICE_EDIT", "..."] }

3.3 Create org hierarchy

POST /api/tenant/v1/tenants/acme/orgs
{ "name": "Acme Distributors" }                     # distributor
POST /api/tenant/v1/tenants/acme/orgs
{ "name": "Voltify UK", "parent_id": "<distributor>" }  # reseller
POST /api/tenant/v1/tenants/acme/orgs
{ "name": "GreenFleet Ltd", "parent_id": "<reseller>" } # customer

3.4 Create locations (sites)

POST /api/tenant/v1/tenants/acme/locations
{ "organisation_id": "<greenfleet>", "name": "Depot A", "post_code": "DE1 1AA", "country": "GB", "is_default": true }

3.5 Add users & memberships

POST /api/tenant/v1/tenants/acme/users
{ "name": "Jane Smith", "email": "jane@greenfleet.com" }

POST /api/tenant/v1/tenants/acme/memberships
{ "user_id": "<jane>", "role_id": "<SITE_MANAGER>" }

Apply subtree scoping in the portal/UI: a role at an org node grants access to that node and all descendants.


4) Policy Notes

  • Single default Site per Customer: enforce is_default=true uniqueness per tenant + customer org.
  • Least privilege: start with VIEWER or OPERATOR and elevate per need.
  • Audit everything: write an AuditLog for structure changes, role/membership changes, and domain verification.
  • Cross‑tenant (platform): only with PLATFORM_ADMIN via ?as_tenant=.
  • PII minimization: avoid exposing personal data across org boundaries.

5) Example: Access Matrix (cheat‑sheet)

Role Org scope Structure admin Site admin Device ops Logs/Tx read Profile/settings
TENANT_ADMIN Tenant (all)
TENANT_MANAGER Tenant (all) (opt) (opt)
TENANT_VIEWER Tenant (all)
DISTRIBUTOR_ADMIN Distributor subtree ✓ (subtree) ✓ (subtree) (opt) ✓ (subtree) (opt)
RESELLER_ADMIN Reseller subtree ✓ (subtree) ✓ (subtree) (opt) ✓ (subtree) (opt)
CUSTOMER_ADMIN Customer subtree limited (opt) (opt)
SITE_MANAGER Site only (opt) (opt)
OPERATOR Site only
END_USER Self self only

6) Implementation Tips

  • Persist org scope on memberships: { user_id, role_id, tenant_id, org_id? } to limit access to a subtree.
  • Add portal policy layer that resolves allowed orgs for a request and injects to service queries.
  • For chargepoint views, join by location.organisation_id to enforce org scope.
  • Keep role names consistent across tenants; allow custom roles via additional entries.