Skip to content

Organizations

Organizations represent companies, departments, or other entities that users can be affiliated with. They support hierarchical structures (parent/child), role-based membership, and domain-based association.

Data Model

Organization

Field Type Description
id uuid Primary key
name varchar(255) Unique name (within active orgs)
address text Physical or mailing address
website varchar(500) Web URL
domain varchar(255) Email domain (e.g. acme.com) for auto-association
parent_id uuid FK to parent organization (nullable)
contacts jsonb Array of contact objects
created_by uuid FK to user who created the org
deleted_at timestamp Soft-delete sentinel (1970-01-01 = active)
created_at timestamp Creation timestamp
updated_at timestamp Last update timestamp

Organization Membership

Field Type Description
id uuid Primary key
organization_id uuid FK to organization
user_id uuid FK to user
role varchar(50) member, signatory, or approver
added_by uuid FK to user who added this member
created_at timestamp When the membership was created

A unique constraint on (organization_id, user_id, role) allows a user to hold multiple distinct roles in the same organization.

Contact Object (JSON)

{
  "name": "Jane Doe",
  "email": "jane@acme.com",
  "phone": "+1-555-0100",
  "title": "Chief Technology Officer"
}

All fields except name are nullable.

Membership Roles

Role Description
member User whose primary affiliation is with this organization. The org typically controls their email domain and account.
signatory User authorized to approve actions or sign documents on behalf of the organization.
approver User authorized to approve administrative actions, such as authorizing other members to join projects or groups.

These roles are extensible -- new values can be added without schema changes since the column is a plain varchar.

Hierarchy

Organizations support arbitrary parent-child hierarchies via the parent_id self-reference. The system prevents circular references by walking the parent chain before accepting a new parent_id value.

API Endpoints

All endpoints are under /api/organizations. Authentication is required for all routes.

List Organizations

GET /api/organizations?page=1&pageSize=20&search=acme&sortBy=name&sortOrder=asc&includeDeleted=false

Permission: org:read

Returns a paginated list of organizations with member counts and parent org names.

Create Organization

POST /api/organizations
Content-Type: application/json

{
  "name": "Acme Corp",
  "domain": "acme.com",
  "website": "https://acme.com",
  "address": "123 Main Street, Springfield",
  "parentId": null,
  "contacts": [
    { "name": "Jane Doe", "email": "jane@acme.com", "phone": null, "title": "CTO" }
  ]
}

Permission: org:manage

Returns 201 with the full organization detail including members and children.

Get Organization

GET /api/organizations/:id

Permission: org:read

Returns the organization with its members, child organizations, and parent name.

Update Organization

PUT /api/organizations/:id
Content-Type: application/json

{
  "name": "Acme Inc",
  "domain": "acme.com",
  "website": "https://acme.com",
  "contacts": [...]
}

Permission: org:manage

All fields are optional. Returns 409 if a name conflict exists.

Delete Organization (Soft)

DELETE /api/organizations/:id

Permission: org:manage

Soft-deletes the organization. Returns 200 with a confirmation message.

Restore Organization

POST /api/organizations/:id/restore

Permission: org:manage

Restores a soft-deleted organization.

Add Member

POST /api/organizations/:id/members
Content-Type: application/json

{
  "userId": "uuid-of-user",
  "role": "signatory"
}

Permission: org:manage

Assigns a role to a user. A user can hold multiple roles in the same organization. Returns 409 if the exact user+role combination already exists.

Remove Member Role

DELETE /api/organizations/:id/members/:userId/:role

Permission: org:manage

Removes a specific role assignment from a user.

List Child Organizations

GET /api/organizations/:id/children

Permission: org:read

Returns an array of { id, name } for direct children.

Permissions

Permission Description Default Roles
org:read View organizations and memberships admin, approver, resource_manager, requestor
org:manage Create, edit, delete organizations and manage members admin

Audit Events

The following actions are logged in the audit trail:

  • organization.created
  • organization.updated
  • organization.soft_deleted
  • organization.restored
  • organization.permanently_deleted
  • organization.member_added (includes userId and role in metadata)
  • organization.member_removed (includes userId and role in metadata)

Frontend

The Organizations feature is accessible from the sidebar (requires org:read permission) and provides:

  • Organization List (/organizations): Paginated table with name, domain, parent, member count, and CRUD actions.
  • Organization Detail (/organizations/:id): Full view with contact cards, child organization tags, and a members table with role badges. Supports adding/removing members with role selection.

Migration

Migration 020_organizations.ts creates the organization and organization_membership tables, the v_organization soft-delete view, and indexes for name uniqueness, domain lookups, and parent traversal.

Future Extensibility

  • New membership roles: Add values like billing, technical_contact, or executive without schema changes.
  • Project sponsorship: Add a sponsor_org_id FK column to the project table to link projects to sponsoring organizations.
  • Domain-based auto-association: The OrganizationService.resolveByDomain() method can match user email domains to organizations during onboarding.
  • Org-scoped permissions: Role-based permissions can be extended to be org-scoped (e.g., "approver for Acme Corp only").