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 (CRM Module)

Exact endpoint ↔ screen mapping. Base URL https://api.<domain>/api/v1, bearer auth, envelope shapes per 00-shared/07_API_Conventions.md. All routes under @Controller('crm') (crm.controller.ts:30), JWT-guarded (crm.controller.ts:29); RBAC permission decorators are not applied yet (perms exist at permissions.constants.ts:34-36; enforcement (planned), IMPLEMENTATION_PLAN.md §5.1).


1. Endpoint table

Leads

#MethodPathScreenParams/BodyPermSource
1GET/crm/leadsS1page (1), limit (20), status (enum)crm.readcrm.controller.ts:37-45
2POST/crm/leadsS3CreateLeadDtocrm.lead.managecrm.controller.ts:47-51
3GET/crm/leads/:idS2crm.readcrm.controller.ts:53-57
4PATCH/crm/leads/:idS3/S2UpdateLeadDto (partial)crm.lead.managecrm.controller.ts:59-63
5POST/crm/leads/:id/follow-upsS2CreateFollowUpDtocrm.lead.managecrm.controller.ts:65-69
6POST/crm/leads/:id/convertS4crm.lead.managecrm.controller.ts:71-75

Campaigns

#MethodPathScreenParams/BodyPermSource
7GET/crm/campaignsS10page, limitcrm.readcrm.controller.ts:77-81
8POST/crm/campaignsS11CreateCampaignDtocrm.campaign.managecrm.controller.ts:83-87

Admissions

#MethodPathScreenParams/BodyPermSource
9POST/crm/admissionsS7CreateAdmissionDtocrm.lead.managecrm.controller.ts:91-95
10GET/crm/admissions/statsS5crm.readcrm.controller.ts:97-101
11GET/crm/admissionsS5page, limit, statuscrm.readcrm.controller.ts:103-111
12GET/crm/admissions/:idS6crm.readcrm.controller.ts:113-117
13PATCH/crm/admissions/:idS6UpdateAdmissionDto (partial)crm.lead.managecrm.controller.ts:119-123
14POST/crm/admissions/:id/documentsS6CreateAdmissionDocumentDtocrm.lead.managecrm.controller.ts:125-132
15POST/crm/admissions/:id/schedule-interviewS6/S8ScheduleInterviewDtocrm.lead.managecrm.controller.ts:134-141
16POST/crm/admissions/:id/decisionS6/S9AdmissionDecisionDtocrm.lead.managecrm.controller.ts:143-147
17POST/crm/admissions/:id/convertS6crm.lead.managecrm.controller.ts:149-153

2. Response shapes

  • List endpoints (1, 7, 11): envelope data: Lead[]/Campaign[]/Admission[] + meta: {page, limit, totalItems, totalPages, hasNext, hasPrevious} via buildPaginationMeta (crm.service.ts:75,79,191,193; admission.service.ts:68-72).
  • Detail (3, 12) & mutations: data = document, no meta.
  • Stats (10): data: {new?: 0, submitted?: n, ..., total, conversionRate: x.x} (admission.service.ts:227-237); counts keyed by AdmissionStatus enum.

3. Sort & filter contract (exact)

ListServer sort (fixed)Filters supportedSource
LeadscreatedAt: -1status onlylead.repository.ts:24-36 (sort at :31)
AdmissionssubmittedAt: -1status onlyadmission.repository.ts:25-35 (sort at :29)
CampaignscreatedAt: -1nonecampaign.repository.ts:21-31 (sort at :25)

No q, no sort, no assignedTo/source query params anywhere in CRM — client-side filtering per 04 §6.

4. Domain rules → status codes

RuleCodeMessage (verbatim)Source
Duplicate lead email409Lead with email "{email}" already exists.crm.service.ts:42-47
Lead not found404Lead not found.crm.service.ts:82-86,98
Convert already-converted400Lead is already converted.crm.service.ts:120-122
Convert closed lead400Cannot convert a closed lead.crm.service.ts:123-125
Convert missing placement400Lead must have grade, academic year, and class assigned for conversion.crm.service.ts:145-149
Admission not found404Admission not found.admission.service.ts:75-79
Decide from wrong status400Admission in status "{status}" cannot be decided.admission.service.ts:141-145
Edit closed admission400Admission in status "{status}" is not editable.admission.service.ts:257-269
Convert non-approved400Only approved admissions can be converted.admission.service.ts:174-178
Convert missing placement400Admission must have grade, academic year, and class for conversion.admission.service.ts:179-183

Cross-tenant access → 404 via scopedFilter (base.repository.ts:20-30). Validation 400s carry details[{field, message}] (00-shared/07 §3).

5. Events emitted (consumers (planned))

EventEmitted atPayload keysSource
LeadCreatedPOST leadsleadId, firstName, lastName, email, sourcecrm.service.ts:50-63
LeadConvertedPOST leads/:id/convertleadId, studentId, userIdcrm.service.ts:166-173
AdmissionSubmittedPOST admissionsadmissionId, emailadmission.service.ts:54-57
AdmissionApproveddecision approveadmissionId, emailadmission.service.ts:158-162
AdmissionRejecteddecision rejectadmissionId, emailadmission.service.ts:163-167
AdmissionConvertedPOST admissions/:id/convertadmissionId, studentIdadmission.service.ts:219-223

Waitlist decision emits no event today.

6. Data model (wire field names)

  • Lead: firstName, middleName?, lastName, email, phone?, source, status, gradeId?, sectionId?, academicYearId?, classId?, notes?, assignedTo?, followUps?: [{note, scheduledAt, completedAt?, createdBy?, createdAt}], convertedAt?, convertedToStudentId?, closedAt?, closedReason?, metadata? + BaseSchema (tenantId, createdBy, updatedBy, isDeleted, deletedAt, deletedBy, version, createdAt, updatedAt) — lead.schema.ts:41-99; BaseSchema per 00-shared/01 §10.
  • Admission: firstName, middleName?, lastName, email, phone?, gradeId?, academicYearId?, classId?, status, documents?: [{type, fileId, filename, uploadedBy?, uploadedAt}], workflow?: [{from, to, approver?, comment?, at}], interview?: {scheduledAt, panel?, mode?, feedback?}, conversion?: {studentId, convertedAt}, notes?, submittedAt?, decidedAt?admission.schema.ts:91-144.
  • Campaign: name, description?, type, status, startDate?, endDate?, targetAudience?, metrics?: {leadsGenerated?, converted?, sent?, opened?, clicked?}, metadata?campaign.schema.ts:23-56.

7. API gaps ((planned) / gaps to raise)

GapImpactNotes
No lead search/qcan't find by nameglobal search incl. leads (planned) (IMPLEMENTATION_PLAN.md §3)
No assignedTo/source list filters"my leads" is client-sideserver filters (planned)
No nextContactDate fieldfollow-up due derived from followUps[] client-sideadd field or dedicated endpoint (proposed)
No lead/admission DELETEno hard/soft delete in UIBaseRepository.softDelete exists (base.repository.ts:68-74); endpoint (planned)
No campaign detail/PATCHcampaigns read-only after create(planned)
No campaign metrics writemetrics schema-only(planned)
No document download/upload endpointsfileId opaquestorage wiring (planned)
No admission assignedTono owner for applications(proposed)
Admission workers (reminder/expiry)stale apps not auto-handledqueues named in 00-shared/01 §6; findStale ready (admission.repository.ts:53-62)
RBAC decoratorsall routes = any authed userenforcement (planned) (IMPLEMENTATION_PLAN.md §5.1)
Lead → batch conversion (coaching)school-only flow today(planned) (IMPLEMENTATION_PLAN.md §6.6)