Skip to Content

Managers & Principals

Managers and Principals are two distinct leadership roles at different levels.

RoleLevelScope
ManagerOrganizationScoped to specific schools via scopedSchoolIds; [] means org-wide
PrincipalSchoolLeads exactly one school (PrincipalProfile.schoolId)

Two creation paths

  1. Create the user with POST /api/users and primaryPersona: MANAGER (or PRINCIPAL), which creates the login account and the persona profile together.
  2. Assign the role/profile to an existing user with POST /api/managers or POST /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.

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.