Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

12 — API Mapping (Organizations Module)

Exact endpoints per screen. Wire contract per 00-shared/07: base /api/v1, Bearer JWT, success {success:true,message:"OK",data,meta?,timestamp,requestId}, error envelope with codes. Only shapes in code are used. (planned)/(forward-looking) marked.


E1 — Create organization

EndpointPOST /api/v1/organizations (organizations.controller.ts:23-27)
Guardnone in code today (OQ-4); client restricts to platform admin; JWT+RBAC guard (planned)
RequestCreateOrganizationDto (create-organization.dto.ts:57-112) — name required; slug/domain/contact/address/timezone/currency/plan/branding/metadata optional
Response201 envelope, data = org doc (status forced onboardingorganizations.service.ts:55); no meta
Errors400 VALIDATION_ERROR; 409 DUPLICATE_RESOURCE (slug/domain, :40-50); 429 RATE_LIMITED; 5xx
ClientS8 create form; loading spinner; on success → tenant detail + slug handoff
Cachenone (write)
Realtimeevent OrganizationCreated → in-app queue job org-created-notification (event-queue-map.ts:13) — notification to tenant admins (forward-looking) UI

E2 — List organizations (tenants)

EndpointGET /api/v1/organizations?page&limit&sort&q (organizations.controller.ts:29-33)
Paramspage ≥1 default 1; limit 1–100 default 20; sort (-field desc); q$or regex on name/slug case-insensitive (organizations.service.ts:78-83); default sort -createdAt (:85-87)
Responsepaginated: data array + meta {page,limit,totalItems,totalPages,hasNext,hasPrevious} (pagination-query.dto.ts:32-54)
Errors400 (bad params); 429; 5xx
ClientS7 tenants list; search debounce 300 ms; infinite scroll on hasNext; pull-to-refresh bypasses cache
Cacheclient paginated cache sl:{tenantId}:orgs:{query} TTL 5 min (volatile); server Redis cache n/a (no cache key in code for this route)
Security noterepo is not tenant-scoped (organizations.repository.ts:15-19) — platform-only gating mandatory (OQ-4)

E3 — Get organization

EndpointGET /api/v1/organizations/:id (organizations.controller.ts:35-40)
Response200 envelope, data = org doc
Errors400 VALIDATION_ERROR (bad ObjectId → CastError mapping http-exception.filter.ts:47-55); 404 RESOURCE_NOT_FOUND (organizations.service.ts:72); cross-tenant id → 404 (no leak)
ClientS1/S8 detail; :id resolution: platform = from route; self = OQ-1 ((planned) /organizations/me; stopgap GET /organizations?q=<tenantId slug>)
Cacheno client cache (detail views, 00-shared/06 §3.3); stale-while-revalidate OK

E4 — Update organization

EndpointPATCH /api/v1/organizations/:id (organizations.controller.ts:42-47)
RequestUpdateOrganizationDto (update-organization.dto.ts:61-139) — partial; $set merge, version +1 (organizations.repository.ts:49-58)
Response200 updated doc
Errors404; 409 slug/domain conflict (organizations.service.ts:107-122); 400 enums invalid
ClientS2 form, S8 status/plan switchers, S3 branding save; conflict → inline field error
RealtimeOrganizationUpdated interface exists (organization-created.event.ts:10-16) but not emitted — no audit signal (OQ-3)

E5 — Delete organization (soft)

EndpointDELETE /api/v1/organizations/:id (organizations.controller.ts:49-54)
Behavioursoft delete: isDeleted:true, deletedAt, deletedBy + version (organizations.repository.ts:60-66); returns 200, data = null (void handler)
Errors404 (organizations.service.ts:158)
ClientS8 typed-confirm dialog; snackbar "purged after 30 days"; row removed
Purgeworker TENANT_PURGE (tenant-purge.worker.ts:17-47): deletes all models' isDeleted && deletedAt < now−30d, idempotent; enqueue from module (planned) OQ-5; GDPR erasure for users (:27-30,49-56)

E6 — Get embedded settings

EndpointGET /api/v1/organizations/:id/settings (organizations.controller.ts:56-62)
Responsedata = org.settings ?? {} — object or {} (never null)
Errors404 (via findById)
ClientS4 form load

E7 — Update embedded settings

EndpointPATCH /api/v1/organizations/:id/settings (organizations.controller.ts:64-72)
RequestUpdateOrganizationSettingsDto (update-organization-settings.dto.ts:4-30)
Response200 updated org doc (settings replaced wholesale — organizations.service.ts:134)
Errors404; 400 (workingDays elements must be numbers)
ClientS4 "Save all" — always full merged object; wipe risk if partial (OQ-6)

E8 — Get org feature-flag map

EndpointGET /api/v1/organizations/:id/feature-flags (organizations.controller.ts:74-79)
Responsedata = Record<string,boolean> (metadata.featureFlags ?? {}organizations.service.ts:139-142)
ClientS5 merge source

E9 — Update org feature-flag map

EndpointPATCH /api/v1/organizations/:id/feature-flags (organizations.controller.ts:81-89)
Requestraw body Record<string,boolean>no DTO, no whitelist; non-boolean → 400 VALIDATION_ERROR (type check on cast, transform:false for raw body — actually plain body bypasses DTO: values not validated server-side except by schema typing; see OQ note)
Response200 updated map (full replace 'metadata.featureFlags': flagsorganizations.service.ts:144-154)
ClientS5 optimistic toggle → full map submit; rollback on error

E10 — Standalone settings collection

EndpointsGET /api/v1/settings?group= (settings.controller.ts:24-29); GET /settings/:key (:31-35); PUT /settings upsert (:37-41); PUT /settings/bulk (:43-47); DELETE /settings/:key (:49-53) — all JwtAuthGuard-protected (:19)
RequestUpdateSettingDto — key/value/group/label/description (update-setting.dto.ts:5-26); upsert on {tenantId,key} unique (setting.schema.ts:38)
Responsesetting doc(s); 404 on missing key (settings.service.ts:19-21)
ClientS4 reference tabs (read by group); writes owned by feature modules (cross-module)

E11 — Standalone feature flags

EndpointsGET /api/v1/feature-flags?module= (feature-flags.controller.ts:23-28); GET /feature-flags/enabled (:30-34); GET /feature-flags/:key (:36-40); PUT upsert (:42-46); PUT /bulk (:48-52); DELETE /:key (:54-58) — JwtAuthGuard-protected
RequestUpdateFeatureFlagDto — key/enabled (required), label/description/module optional (update-feature-flag.dto.ts:4-27); upsert writes enabled+label only (feature-flag.repository.ts:32-43)
Responseflag doc; 404 missing key (feature-flags.service.ts:28-30)
ClientS5 catalog (labels/descriptions/module grouping); delete via menu

E12 — Register (first admin)

EndpointPOST /api/v1/auth/register (auth.controller.ts:30-36) — public, @RateLimit auth 5/min (:31)
RequestRegisterDto (register.dto.ts:4-29) — firstName/lastName/email/password(≥8)/phone?/tenantId (slug)
Response201 data {accessToken, refreshToken} (auth.service.ts:95-99); side effects: user created + auth_account, member org_admin, role seeding, UserRegistered event → welcome email (event-queue-map.ts:7)
Errors409 email exists (auth.service.ts:56-58); 400; 429
Clientonboarding flow; no org-existence check (OQ-2)

E13 — RBAC (invite admin)

EndpointsGET /api/v1/rbac/members (rbac.controller.ts:57-61); POST /rbac/members (:63-67); PATCH /rbac/members/:id (:69-73); DELETE /rbac/members/:id (:75-79); GET /rbac/roles (:27-31) — all org_admin + JwtAuthGuard (:19-23)
RequestAddMemberDto {userId, roles[]}
ClientS6 list/invite/role change/remove

Client contract summary (all screens)

ConcernRule
AuthBearer JWT; 401 → single-flight refresh → replay; fail → session expiry (00-shared/06 §3.6)
Optimisticonly flag toggles (E9); all other writes show server result (07 §9)
IdempotencyPATCH/DELETE retry-safe; no Idempotency-Key support confirmed (B6)
Offlinereads from last-good cache + banner; writes blocked (no module offline queue)
Paginationpage/limit/sort/q + meta exact (07 §5); infinite scroll driven by hasNext
RealtimeWS topics n/a for org config today; (forward-looking) org.branding.updated, org.feature-flags.updated
Error mapping00-shared/06 §5 table: 400 field errors, 403 hide/deny, 404 empty/not-found, 409 inline conflict, 429 backoff, 5xx generic+requestId