StudyLyon — multi-tenant ERP / School Management API. This package designs the
Organizations module client (Flutter, forward-looking spec) against the implemented
NestJS backend. All endpoints, DTO fields, schemas, events, queue routes, and wire
contracts are derived directly from src/modules/organizations/**, src/modules/settings/**,
src/modules/feature-flags/**, src/modules/rbac/**, src/modules/auth/**,
src/infrastructure/workers/tenant-purge.worker.ts, and studylyon-blueprint/04-Modules/Organizations.md.
No feature is invented; gaps are flagged in §10 and the module Assumptions & Open Questions.
Organizations is the tenant root of the platform: it owns the lifecycle of every
institution (creation, profile, branding, settings, feature flags, subscription, offboarding)
and, together with Auth's register, provisions the tenant's first administrator. It is the
foundation every other module's data hangs off — every business document carries the
tenant's tenantId (slug) and inherits its configuration (timezone, currency, academic year,
attendance rules, theme, feature toggles).
Super Admin (platform): create tenants, review all tenants, suspend/delete offboarding
tenants; never operate inside a tenant's data.
Org Admin: see and maintain the institution's identity (name, slug, domain, contact,
address, logo), timezone/currency/academic year, branding, settings groups, and feature
flags from one place; invite a co-admin.
Invited admin: accept invitation, log in, and inherit the same org-admin surface.
Every school/coaching on the platform is a tenant. Without Organizations, there is no
tenant to scope anything to: auth registers users into a tenant, RBAC seeds roles per
tenant, settings/feature-flags are keyed by tenantId. This module is the first surface a
customer touches (Super Admin provisioning) and the first place misconfiguration (wrong
timezone, wrong working days) damages the whole institution's daily operation.
Client scope flag (matches 00-shared/01 §9): native mobile apps are out of Phase 1
(PRODUCT_REQUIREMENTS_DOCUMENT.md:144); these docs are forward-looking full client specs
against the implemented API. Module-specific "forward-looking" marks apply to push/QR/deep links.
Organization surface is admin-only (/organization destination, 00-shared/05 §2).
The tenant identifier in JWT and every tenant-scoped collection is the org slug string;
the org document's _id is a separate ObjectId (see OQ-1 for the self-view gap).
Client-side validation (lengths, hex color regex, working-day ranges) is (proposed) —
the backend DTOs impose no min/max beyond @IsEmail, enums, and object types.
(planned) items come from studylyon-blueprint/04-Modules/Organizations.md and
docs/IMPLEMENTATION_PLAN.md (R2 provider, rate-limit tiers, institution type).
No self-org endpoint.GET /organizations/:id needs the org _id, but JWT carries only the slug (auth.service.ts:461). Client cannot fetch its own org profile without a cross-tenant listing. Resolution (planned):GET /organizations/me (or slug-resolved lookup); until then client may use GET /organizations?q=<slug> as stopgap (see OQ-4 risk).
Org Overview screen
OQ-2
register does not verify the org exists or is active (auth.service.ts:60-69). Any slug registers an admin. Client should surface "tenant not found" guidance; server validation (planned).
Registration journey
OQ-3
OrganizationUpdated event interface exists (organization-created.event.ts:10-16) but is never emitted by update(). No audit signal for profile changes today.
Audit trail
OQ-4
OrganizationsRepository is not tenant-scoped (organizations.repository.ts:15-19 — only isDeleted filter) and the controller has no JWT/RBAC guard (organizations.controller.ts:1-90). A tenant admin calling GET /organizations today sees every tenant (slug + domain). Client must gate the tenants list + tenant detail to platform admin only; server guard/scope (planned).
Isolation, tenants list
OQ-5
Blueprint: "Offboarding triggers TenantPurgeJob" (Organizations.md:61) — but remove() only soft-deletes; nothing enqueuesQUEUE.TENANT_PURGE. Worker purges all soft-deleted docs ≥30 days across all models. Enqueue + per-tenant purge payload (planned).
Offboarding
OQ-6
Blueprint lists a separate organization_settings collection (Organizations.md:7); code keeps settings embedded in organizations.settingsand a generic settings collection. Two surfaces exist; sync between them is unspecified.
Settings UI
OQ-7
No organization.create permission in permissions.constants.ts:1-97 — create is platform-only by role design; client hardcodes it as such.