Skip to Content

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_BACKOFFICE

A 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:

OutcomeHTTP statusWhen
Child not in caller’s org (or does not exist)404Existence non-disclosure (cross-org)
Child in org but not linked to this parent403In-org but unlinked (FU-15)
Child linked200Proceed 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:

  1. Typed DTO allow-list (parent-portal.schemas.ts): the response schema contains only parent-safe fields. θ scores, SkillStatus enum tokens, profile codes, internal IDs, and benchmark numbers cannot leak because they are not declared in the schema at all.
  2. applyParentStricterFilter (canonical FR-LANG-6 belt from modules/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/children

Returns 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}/diagnostics

Returns 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}/growth

Returns 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=BOY

Returns 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.