Organizations and business units
The Organizations and business units screen lets you build the hierarchy of the tenant: the organizations, the
business units that belong to them (with an optional parent unit), their status and their description. Every change is
versioned and audited. Portal path: /admin/organizations. It is used by the tenant administrators (tenant_admin);
the other members of the tenant read it.
What the screen shows
Two tables, one under the other.
Organizations, with Refresh and New organization (shown to tenant_admin only):
| Column | Content |
|---|---|
| Slug | Identifier of the organization. |
| Name | Display name. |
| Description | Free text (2000 characters at most). |
| Status | active or archived. |
| Version | Version number, incremented at each change. |
| Updated by | Author of the last change. |
| Updated | Date of the last change. |
Business units, with the filters Organization and Parent unit (identifiers) and the Filter button, then
Refresh and New business unit (shown to tenant_admin only):
| Column | Content |
|---|---|
| Slug | Identifier of the unit. |
| Name | Display name. |
| Organization | Identifier of the organization of the unit. |
| Parent unit | Identifier of the parent unit, empty for a top unit. |
| Status | active or archived. |
| Version | Version number. |
| Updated | Date of the last change. |
A click on a line opens a side panel with the line itself (there is no detail route). Both lists are paged by slug. Empty states: No organization and No business unit.
Actions
| Button or action | What it does | API call | Roles |
|---|---|---|---|
| Opening the screen, Refresh | Reads the organizations. | GET /v1/organizations | Member of the tenant |
| New organization then Create | Form Slug (required), Name (required), Description, Metadata (JSON, {} by default). | POST /v1/organizations | tenant_admin |
| Edit (organization) | Dialog Name, Description, Status (active, archived), Expected version, then Edit. | PATCH /v1/organizations/{id} | tenant_admin |
| Organization, Parent unit then Filter | Narrows the business units to one organization or one parent unit. | GET /v1/business-units | Member of the tenant |
| New business unit then Create | Form Organization (required, suggested from the organizations), Parent unit (suggested from the units), Slug (required), Name (required), Description, Metadata (JSON). | POST /v1/business-units (suggestions: GET /v1/organizations, GET /v1/business-units) | tenant_admin |
| Edit (business unit) | Same dialog as for an organization. | PATCH /v1/business-units/{id} | tenant_admin |
| Click on a line | Shows the line in a side panel. | none | Member of the tenant |
Expected version protects against concurrent changes: when it is filled and differs from the current version, the
server refuses the change with version_conflict.
Roles
From services/control-plane/src/eai/domains/tenancy/hierarchy.py: the lists need a principal inside a tenant
(require_tenant); creation and changes require tenant_admin (platform_admin always passes). The portal hides the
create buttons for the other roles (create.roles: ["tenant_admin"]) but shows Edit to everyone; the server
refuses it with forbidden.
Typical tasks
Create an organization and its business units
- Create the organization. Button New organization: Slug (
retail), Name, Description, then Create. Expected result: the toast Created and a line at version 1. If it fails:conflict(HTTP 409) when the slug exists;invalid_metadata(HTTP 422) when the metadata is refused. - Create a top business unit. Button New business unit: choose the Organization, leave Parent unit
empty, type Slug and Name, then Create. Expected result: a new line whose Parent unit is empty.
If it fails:
not_foundwhen the organization identifier is unknown. - Create a child unit. Same form with Parent unit set to the unit created above. Expected result: a line with
its parent. If it fails:
invalid_hierarchy(HTTP 422) when the parent belongs to another organization.
Archive an organization without losing a concurrent change
- Read the current version. Look at the Version column of the line. Expected result: a number, for example
3. If it fails: click Refresh. - Edit with the expected version. Row menu > Edit, set Status to
archivedand Expected version to3, then Edit. Expected result: the toast Done, statusarchived, version4. If it fails:version_conflict(HTTP 409) means someone changed it meanwhile; reload and start again.
List the units of one organization
- Filter. Paste the organization identifier in Organization and click Filter. Expected result: only the
units of that organization. If it fails:
not_foundwhen the identifier is not a valid one.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| No New organization button | Your roles do not contain tenant_admin. | Ask a tenant administrator. |
forbidden (HTTP 403) on Edit | Only tenant_admin changes the hierarchy. | Ask a tenant administrator. |
version_conflict (HTTP 409) | Expected version differs from the current version. | Refresh, read the new version, apply your change again. |
invalid_hierarchy (HTTP 422) | The parent unit belongs to another organization. | Choose a parent of the same organization. |
invalid_metadata (HTTP 422) | The metadata breaks a constraint of the database. | Correct the JSON of Metadata. |
conflict (HTTP 409) on Create | The slug already exists. | Choose another slug. |