Organization
Data Entity
Description
A tenant organization on the Meander platform. Represents any legal entity (national association, regional body, local chapter) that subscribes to Meander. Owns users, module configuration, terminology labels, Bufdir reporting, and accounting integrations. Forms the root of the multi-tenant isolation boundary.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Surrogate primary key. Generated server-side (UUIDv4). | PKrequiredunique |
name |
string |
Display name of the organization (e.g. 'Norges Handikapforbund'). Must be unique within the same parent. | required |
slug |
string |
URL-safe, lowercase, hyphenated identifier derived from name (e.g. 'norges-handikapforbund'). Used in API paths and deep links. | requiredunique |
organization_type |
enum |
Structural tier of this organization in the national hierarchy. | required |
parent_organization_id |
uuid |
FK to organizations.id. NULL for top-level national organizations. Enables the multi-organization hierarchy (NHF: 12 landsforeninger → 9 regioner → 1,400 lokallag). | - |
status |
enum |
Lifecycle state. Inactive orgs retain data but API access is blocked. Suspended is a temporary admin-initiated hold. | required |
contact_email |
string |
Primary administrative contact email for the organization. Used for system notifications and onboarding. | required |
contact_phone |
string |
Primary contact phone number in E.164 format (e.g. '+4722334455'). | - |
address |
json |
Postal address. Structure: { street, city, postal_code, country_code }. Used on Bufdir reports and legal documents. | - |
country_code |
string |
ISO 3166-1 alpha-2 country code (e.g. 'NO'). Drives locale defaults and legal compliance rules. | required |
locale |
string |
Default BCP-47 locale tag for this organization (e.g. 'nb-NO', 'se-NO' for Sami). Controls default UI language and date/number formatting. | required |
bufdir_org_code |
string |
Official Bufdir organization code required for government grant reporting. Must be set before Bufdir report generation is permitted. | unique |
accounting_system |
enum |
Which external accounting system this organization integrates with for reimbursement export. 'none' disables the accounting connector. | required |
logo_url |
string |
URL of the organization's logo stored in object storage. Displayed in the admin portal header and reports. | - |
website_url |
string |
Public website of the organization. Shown on the sales website and in external-facing pages. | - |
description |
text |
Short public-facing description of the organization's mission. Used on the sales website and demo booking flow. | - |
max_hierarchy_depth |
integer |
Maximum allowed depth of child organization nesting for this root org. Defaults to 5 to prevent runaway recursion in CTE queries. | required |
trial_ends_at |
datetime |
If set, the organization is on a trial plan. After this date, the status is automatically transitioned to 'inactive' unless upgraded. | - |
created_at |
datetime |
Timestamp when the organization record was created. Set once by the server. | required |
updated_at |
datetime |
Timestamp of the last modification to any field on this record. | required |
deleted_at |
datetime |
Soft-delete timestamp. NULL means the record is active. Non-NULL records are excluded from all normal queries via a default scope. | - |
Database Indexes
idx_organizations_slug
Columns: slug
idx_organizations_parent_id
Columns: parent_organization_id
idx_organizations_status
Columns: status
idx_organizations_bufdir_code
Columns: bufdir_org_code
idx_organizations_deleted_at
Columns: deleted_at
idx_organizations_name_parent
Columns: name, parent_organization_id
Validation Rules
name_not_blank
error
Validation failed
slug_format
error
Validation failed
valid_contact_email
error
Validation failed
valid_contact_phone
error
Validation failed
parent_org_must_exist_and_be_active
error
Validation failed
bufdir_code_unique
error
Validation failed
valid_country_code
error
Validation failed
valid_locale
error
Validation failed
accounting_system_enum
error
Validation failed
logo_url_safe
error
Validation failed
Business Rules
unique_name_within_parent
Two organizations with the same parent_organization_id cannot share the same name. Enforced by a composite unique index on (name, parent_organization_id). Prevents confusion in NHF's deep hierarchy (e.g. two 'Oslo' local chapters under different regions).
no_circular_hierarchy
An organization cannot be set as its own ancestor. Before persisting a parent_organization_id change, a recursive CTE walks the prospective parent chain and rejects the update if the current org's id appears in it.
max_hierarchy_depth_enforced
The depth of the organization tree rooted at any national org must not exceed max_hierarchy_depth. Checked at insert/update of parent_organization_id.
bufdir_code_required_before_report_generation
Bufdir report generation is blocked unless bufdir_org_code is non-null and the organization status is 'active'. The report service checks this before creating any bufdir_reports record.
soft_delete_only
Organizations are never hard-deleted. Deletion sets deleted_at to now() and transitions status to 'inactive'. All child organizations, memberships, and reports are preserved for audit and Bufdir compliance.
block_delete_with_active_members
An organization cannot be soft-deleted while it has active organization_memberships (users with non-null left_at). The delete service must verify zero active memberships first.
status_transition_audit
Every change to the status field (active → suspended, suspended → inactive, etc.) must produce an entry in audit_logs with the actor's user id, previous value, and new value.
module_always_on_set_protected
The always-on modules (authentication-access-control, home-navigation, accessibility, help-support, profile-management) cannot be disabled via module_configurations for any organization. The module configuration service rejects any toggle that targets these area IDs.
trial_expiry_auto_deactivation
When trial_ends_at is in the past and status is still 'active', the organization is automatically transitioned to 'inactive' on the next authentication request or by a scheduled job. Users receive an email notification.