core PK: id 12 required 3 unique

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.

21
Attributes
6
Indexes
10
Validation Rules
27
CRUD Operations

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
btree unique

Columns: slug

idx_organizations_parent_id
btree

Columns: parent_organization_id

idx_organizations_status
btree

Columns: status

idx_organizations_bufdir_code
btree unique

Columns: bufdir_org_code

idx_organizations_deleted_at
btree

Columns: deleted_at

idx_organizations_name_parent
btree unique

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
on_create

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
on_update

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
on_create

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
always

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
on_delete

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
on_delete

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
on_update

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
always

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
always

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.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage