SPEC 002 — Organization Management
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P0 — Launch-Critical |
| Backend | equa-server/modules/organizations/ |
| API | equa-server/modules/api/src/endpoints/organization-endpoints.ts |
| Frontend | equa-web/src/modules/organization/, equa-web/src/modules/organization-dashboard/ |
1. Feature Purpose
Organizations are the top-level entity in Equa. Every cap table, agreement, document, and member belongs to an organization. This module handles creating and configuring organizations (company type, equity structure, operating agreement), managing members and their roles, tracking addresses with content-addressable hashing, and enforcing member limits per plan.2. Current State (Verified)
2.1 Backend
| Component | Path |
|---|---|
| Module root | equa-server/modules/organizations/ |
| API endpoints | equa-server/modules/api/src/endpoints/organization-endpoints.ts |
2.2 Frontend
| Component | Path | Description |
|---|---|---|
| Organization module | equa-web/src/modules/organization/ | Org management, securities, legends, portfolio |
| Dashboard module | equa-web/src/modules/organization-dashboard/ | Dashboard, recent actions, capital summary |
2.3 Permission Model
RBAC enforced through three join tables:| Table | Purpose |
|---|---|
| OrganizationsRoles | Maps roles to organizations |
| MembersRoles | Maps roles to members within an organization |
| PermissionsRoles | Maps granular permissions to roles |
3. Data Model
Organizations
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| name | varchar | NOT NULL |
| operatingAgreement | hash | Content-addressed reference |
| companyType | int | Enum (LLC, C-Corp, S-Corp, etc.) |
| equityStructure | int | Enum |
| ownershipTransferability | int | Enum |
| agent | uuid | FK → registered agent |
| logo | uuid | FK → file storage |
| ein | varchar | |
| website | varchar | |
| creator | uuid | FK → Users |
| phoneNumber | varchar | |
| registrationNumber | varchar | |
| startDate | date | |
| legacyDataRoom | boolean | Flags orgs migrated from legacy system |
OrganizationDetailsTable
| Column | Type | Constraints |
|---|---|---|
| name | varchar | |
| companyType | int | |
| businessAddress | varchar | |
| phoneNumber | varchar | |
| registrationNumber | varchar | |
| startDate | date |
OrganizationTemplates
| Column | Type | Constraints |
|---|---|---|
| organization | uuid | FK → Organizations |
| type | varchar | Template category |
| content | hash | Content-addressed template body |
Members
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| fullName | varchar | NOT NULL |
| citext | ||
| organization | uuid | FK → Organizations |
| user | uuid | FK → Users, nullable |
| isIndividual | boolean | Individual vs. entity member |
| title | varchar |
MemberLimits
| Column | Type | Constraints |
|---|---|---|
| organization | uuid | FK → Organizations |
| memberLimit | int | Max members allowed by plan |
Addresses
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| owner | uuid | FK → Organizations or Members |
| street1 | varchar | |
| street2 | varchar | nullable |
| street3 | varchar | nullable |
| country | varchar | |
| city | varchar | |
| postalCode | varchar | |
| province | varchar |
HashedAddresses
Content-addressable address records. The hash is derived from the address fields, enabling deduplication and immutable references from agreements and other documents.4. API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /api/v1/organizations | Yes | Create a new organization |
| GET | /api/v1/organizations | Yes | List organizations for current user |
| GET | /api/v1/organizations/:id | Yes | Get organization details |
| PUT | /api/v1/organizations/:id | Yes | Update organization settings |
| DELETE | /api/v1/organizations/:id | Yes | Delete organization (owner only) |
| GET | /api/v1/organizations/:id/members | Yes | List members |
| POST | /api/v1/organizations/:id/members | Yes | Add a member |
| PUT | /api/v1/organizations/:id/members/:memberId | Yes | Update member details |
| DELETE | /api/v1/organizations/:id/members/:memberId | Yes | Remove a member |
| GET | /api/v1/organizations/:id/dashboard | Yes | Dashboard data (recent actions, capital summary) |
| PUT | /api/v1/organizations/:id/templates | Yes | Set organization templates |
5. Frontend Components
| Component | Path | Description |
|---|---|---|
| Organization module | equa-web/src/modules/organization/ | Org settings, securities config, legends, portfolio view |
| Organization dashboard | equa-web/src/modules/organization-dashboard/ | Summary dashboard with recent actions and capital overview |
| Securities management | equa-web/src/modules/organization/ | Security type configuration within org module |
| Legends management | equa-web/src/modules/organization/ | Share certificate legend templates |
| Portfolio view | equa-web/src/modules/organization/ | Member portfolio overview within org context |
6. Business Rules
- Creator is default owner — The user who creates an organization is assigned the owner role automatically.
- Member limits — Each organization has a
memberLimit(set by billing plan). Adding members beyond the limit is rejected. - Members can be entities —
isIndividual = falseallows companies, trusts, and other entities as members. - Content-addressed documents —
operatingAgreementand templatecontentare stored as hashes referencing immutable content, ensuring auditability. - HashedAddresses — Addresses are content-addressed so that the same physical address used across agreements produces a single canonical reference.
- RBAC scoped to organization — OrganizationsRoles, MembersRoles, and PermissionsRoles control who can view, edit, or administer each organization.
- Legacy data room flag — Organizations migrated from the legacy system have
legacyDataRoom = trueto gate compatibility behavior. - Unique email per org — Member email (citext) is unique within an organization context to prevent duplicate invitations.
7. Acceptance Criteria
- User can create a new organization with name, company type, and equity structure
- Organization settings (EIN, website, phone, registration number, start date) can be updated
- Creator is automatically assigned the owner role
- Members can be added up to the plan’s member limit
- Attempting to exceed the member limit returns an error
- Members can be individual persons or entities (
isIndividualflag) - Addresses are stored and deduplicated via content-addressable hashing
- Operating agreement and templates are stored as immutable content-addressed hashes
- Organization dashboard displays recent actions and capital summary
- RBAC prevents unauthorized users from viewing or modifying organization data
- Organization can be deleted by the owner only
8. Risks
| Risk | Impact | Mitigation |
|---|---|---|
| Member limit bypass via race condition | Exceeds paid plan capacity | Use database-level constraint or serialized check |
| Orphaned members after org deletion | Data integrity issues | Cascade delete or soft-delete with cleanup job |
| Content-addressed hash collision | Wrong document served | Use SHA-256 or stronger; collision probability negligible |
| Legacy data room flag not checked consistently | Legacy orgs see incorrect UI | Centralize flag check in middleware or service layer |
| RBAC misconfiguration allows cross-org access | Unauthorized data exposure | Integration tests for permission boundaries; audit logging |
| EIN / registration number stored in plaintext | Regulatory exposure | Evaluate encryption at rest for sensitive identifiers |