Parent Portal API
The parent web portal (WP-PWP-BE) exposes four read-only endpoints that surface a child’s growth to their linked parent. All four endpoints share the same permission gate and the same two-belt no-leak posture.
Permission gate
VIEW_OWN_CHILDREN | VIEW_PARENT_REPORTS | RUN_BACKOFFICEA parent persona holds both VIEW_OWN_CHILDREN and VIEW_PARENT_REPORTS. A Teacher, Student,
or Principal token holds none of these and receives 403. RUN_BACKOFFICE is reserved for
super-admin support access.
Permission alone is not sufficient. Each :childId route additionally verifies that the calling
parent is linked to that specific child (see IDOR closure below).
IDOR closure
The parent’s identity is derived from ctx.userId (the authenticated session). There is no
:parentId segment in any path, so it is structurally impossible to request another parent’s
children by substituting an ID in the URL.
For the three :childId routes, the handler resolves the child’s StudentProfile within the
caller’s org and then calls canReachStudent(..., { allowParent: true }) against the
ParentStudent junction:
| Outcome | HTTP status | When |
|---|---|---|
| Child not in caller’s org (or does not exist) | 404 | Existence non-disclosure (cross-org) |
| Child in org but not linked to this parent | 403 | In-org but unlinked (FU-15) |
| Child linked | 200 | Proceed with response |
The 404 / 403 discrimination is intentional: returning 403 for a cross-org child would
confirm the child exists in the system.
Two-belt no-leak posture
Every response passes two independent safety layers:
- Typed DTO allow-list (
parent-portal.schemas.ts): the response schema contains only parent-safe fields. θ scores,SkillStatusenum tokens, profile codes, internal IDs, and benchmark numbers cannot leak because they are not declared in the schema at all. applyParentStricterFilter(canonical FR-LANG-6 belt frommodules/language-safety): runs on every serialized string in the response. It blocks any internal identifier, numeric score, or clinical label that might otherwise slip through inside an allowed string field.
Both layers are required. Neither alone is sufficient.
Endpoints
List children
GET /api/parent/childrenReturns the list of children linked to the authenticated parent via ParentStudent. Each entry
contains the child’s first name, grade level, school name, and last-active timestamp. No student
IDs, class IDs, or score values appear.
An empty array is a valid response when the parent has no linked children yet.
Diagnostic history
GET /api/parent/children/{childId}/diagnosticsReturns the completed diagnostic history for the specified child. Each entry carries a
status-label only (Arabic categorical description); no raw θ, no percentages, and no SkillStatus
enum tokens are included.
Growth over time
GET /api/parent/children/{childId}/growthReturns a macro-domain status trajectory showing how the child’s status on each growth region has changed across assessment windows. Values are Arabic categorical labels only. No numbers appear at any level.
At-home recommendations
GET /api/parent/children/{childId}/recommendations?window=BOYReturns up to five at-home growth suggestions drawn from the pre-generated parent narrative. The suggestions come from the SPOT profile template library and are never generated at runtime by an LLM (V-5 no-LLM invariant).
The window query parameter (default BOY) selects the assessment window.
GL-005 halt payload
If the child has no active profile assignment (for example, the diagnostic is still in progress),
the endpoint returns an HTTP 200 with a safe GL-005 halt payload instead of fabricating
suggestions. The halt payload is a short, warm Arabic message that explains more data is needed.
It never falls back to a generic statement.
Fail-closed behavior
All four endpoints return 503 if the language-safety rule set is unavailable. The server refuses
to ship a response without confirming that the parent-stricter filter can run. This is the same
posture used by the parent PDF endpoint (WP-09-BE).
Never store or log the raw response bodies from these endpoints in a context visible to non-parent roles. The typed DTOs guarantee that no internal identifiers reach the wire, but server-side logging of full response payloads could still expose the child’s name or school name to log viewers.
Related
- Reporting and Parent PDF: the parent growth PDF endpoint and its permission model
- Parent Child Links: how to link a parent to a child (
ParentStudentjunction) - Language Safety: parent-stricter filter and growth-language rules
- Core Concepts - RBAC: full permission table