9 · The data this touches, and how a child’s record is gated
The roster is the sharpest surface here, so it carries the most refusals
The read this API is most often used for is a roster read, and a roster is a list of children. Three separate mechanisms stand between a key and that list, and they are not alternatives to one another.
The allowlist decides whether the route is public at all. The scope decides whether this key may reach it. The database’s row-level policies decide which rows exist for this principal — and because a key’s context carries one school and an empty district list, that confinement is enforced by the engine rather than by a filter in application code that somebody could forget to apply. Underneath all three sit the consent gates on the record itself.
Outbound the rule is simpler and absolute: webhook payloads carry identifiers, integer cents and state. Never a name, never a photo, never an email address. Resolving an identifier costs you a call back through the scoped API, which is mildly inconvenient and precisely the point — it puts every read back under the same walls, and it keeps children’s names out of third-party queues, retry buffers and log aggregators you and we do not control.
Records belong to the school. They are not sold and they are not shared with outside companies. An integration you build reads them under the school’s authority, on the school’s behalf, for as long as the school says so.