Multi-Tenant Municipality Portal¶
RONL Business API is built for multi-tenancy from the ground up. Each Dutch municipality that integrates with the platform has its own branded portal, its own set of users and roles, its own isolated data, and its own audit logs — all running on shared infrastructure.

Supported municipalities¶
Four municipalities are currently configured:
| Municipality | Theme primary | Theme secondary | Portal URL |
|---|---|---|---|
| Utrecht | #C41E3A (red) |
#2C5F2D (green) |
https://mijn.open-regels.nl |
| Amsterdam | #EC0000 (bright red) |
#003B5C (dark blue) |
— |
| Rotterdam | #00811F (green) |
#0C2340 (navy) |
— |
| Den Haag | #007BC7 (blue) |
#005A99 / #E17000 (orange) |
— |
Adding a new municipality requires a tenants.json entry and a Keycloak user group — see Adding a Municipality.
Dynamic theming¶
The frontend reads the municipality claim from the JWT on login and applies the corresponding theme by setting CSS custom properties on document.documentElement:
This means a single deployed frontend binary serves all municipalities — the branding switches per user session based on their JWT claim, with no page reload required.
The tenant configuration is loaded from public/tenants.json at runtime. Each entry specifies the theme, the municipality code (for API filtering), enabled features, and contact details.
Per-tenant feature flags¶
The TenantFeatures configuration allows enabling or disabling specific government services per municipality:
zorgtoeslag — healthcare allowance calculation
kinderbijslag — child benefit calculation
huurtoeslag — rent allowance calculation
processes — list of allowed BPMN process keys
A municipality that has not been cleared to offer kinderbijslag cannot invoke that process endpoint even if a user submits a valid JWT. The processes allowlist is enforced in the backend's tenant middleware.
Tenant isolation in the backend¶
Every API request passes through the tenant middleware, which:
- Extracts the
municipalityclaim from the validated JWT - Loads the tenant configuration for that municipality
- Attaches the tenant context to
req.user - Ensures all downstream queries and audit log entries are scoped to that municipality
Process instances from one tenant are never returned to another. Audit log queries are always filtered by municipality.
User roles¶
Each municipality has two standard roles configured in the Keycloak realm:
| Role | Scope | Capabilities |
|---|---|---|
citizen |
Own submissions | Start processes, view own application status, view own results |
caseworker |
Municipality queue | Process applications, view all submissions for their municipality, update status |
admin |
System | Manage users, view audit logs, configure settings |
Roles are set as realm roles in Keycloak, mapped into the JWT via a protocol mapper, and validated by the backend's authorization middleware.