Managers & Principals
Managers and Principals are two distinct leadership roles at different levels.
| Role | Level | Scope |
|---|---|---|
| Manager | Organization | Scoped to specific schools via scopedSchoolIds; [] means org-wide |
| Principal | School | Leads exactly one school (PrincipalProfile.schoolId) |
Two creation paths
- Create the user with
POST /api/usersandprimaryPersona: MANAGER(orPRINCIPAL), which creates the login account and the persona profile together. - Assign the role/profile to an existing user with
POST /api/managersorPOST /api/principals.
Use path 1 for a brand-new person; use path 2 to grant the role to a user that already exists.
Create a Manager
curl -X POST https://localhost:3000/api/managers \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "userId": 1, "scopedSchoolIds": ["clx4z2k0u0000xyz1234abcde"] }'Needs MANAGE_MANAGERS (Admin-only). An empty scopedSchoolIds makes the manager org-wide.
Returns 409 (CONFLICT, “User is already a Manager”) if the user already holds the Manager role,
including when two concurrent requests race on the same userId.
Create a Principal
curl -X POST https://localhost:3000/api/principals \
-H "Authorization: Bearer <accessToken>" \
-H "Content-Type: application/json" \
-d '{ "userId": 1, "schoolId": "clx4z2k0u0000xyz1234abcde", "tier": "STANDARD" }'Returns 201 PrincipalDetailResponse. MANAGE_PRINCIPALS: an Admin holds it at any scope; a
Manager holds it when the target schoolId is within their scopedSchoolIds (or they are org-wide).
The principal ↔ school link is bidirectional. Creating a PrincipalProfile with schoolId=X sets
School.principalUserId; unassigning nulls it. Wave 1 allows one principal per school.
Organization rollup (admin utility)
GET /api/organizations/{id}/rollup (VIEW_ORG, Admin or a Manager on their own org) returns the
school list and totalStudents. The byMacroDomain field is always an empty object {} in this
endpoint. Macro-domain data is served by the three dedicated manager routes described below.
Manager org-level API
The manager dashboard is backed by three aggregate read-only routes, all gated on VIEW_ORG
(Manager or Admin). A Manager with a non-empty scopedSchoolIds sees only their scoped schools
(all-or-nothing enforcement). A cross-org orgId returns 404 without disclosing existence.
Overview
GET /api/orgs/{orgId}/overview returns OrgOverviewDTO: per-school rollup cards containing
schoolNameAr, totalStudents, classCount, needsSupportCount, and lastUpdatedAt. This is the
manager home screen.
Comparisons
GET /api/orgs/{orgId}/comparisons returns OrgComparisonDTO: status-count distributions across
all six macro-domain tiles for every school in the org, ordered alphabetically. There is no
preferential ranking between schools. Each entry is a status distribution, not a numeric average and
not a single overall percentage.
Trends
GET /api/orgs/{orgId}/trends returns OrgTrendsDTO: a time-series per school per macro-domain
tile. The x-axis buckets snapshots by calendar month (YYYY-MM derived from computedAt) in
ascending order. This bucketing is by calendar month, not by BOY/MOY/EOY assessment windows.
For the school-level reads behind a principal’s dashboard, see Standards & Benchmarks (school overview + heatmap) and Reporting & Parent PDF.
Approval authority
Tier-3 activation approval is admin-seated in Wave 1 (APPROVE_TIER_3_ACTIVATION), and the
reading-specialist is admin-seated via specialistRole. See Core Concepts.