Skip to content

Flux Student Directory API reference

122 operations in 19 groups. Generated from the OpenAPI contract below.

Version 1.0Credentials required

Base URL: https://studentdirectory.app/api/v1

Programmatic access to a school's directory on Flux Student Directory: the roster, student and guardian detail, private stars, notes and lists, lunch menus, the school calendar, notices, documents, permission slips, polls, sign-ups, organizations (teams, clubs and classes), the staff directory and photos.

Every call needs credentials. There is no anonymous access, and a call acts as one real user of one or more schools: the response contains only what that person may see in the product, and a write succeeds only if that person may make it (a parent manages their own profile, stars, notes and lists and answers polls, sign-ups and permission slips; an Organization Editor also manages the organizations they were granted). Send the user's access token as Authorization: Bearer <token> on every request. Credentials for your application are issued by Flux: write to flux-support@fluxstudentdirectory.com to request access before you build.

School-administration operations (roster management, access and role management, registration allowlist, school settings), push device registration, account deletion and Flux's internal tools are intentionally not part of this reference.

All paths are relative to the server URL. Request and response bodies are JSON unless a route says otherwise. Errors share one shape ({ error: { code, message } }); 401 means the token is missing, expired or invalid, 403 that this user may not do this, and 404 that the thing does not exist or is not visible to this user. Treat unknown response fields as additive: new fields can appear without a version change.

Session

The signed-in user, the schools and roles they hold, and post-registration set-up.

GET/me

Get the current user and their school memberships

Returns every membership the caller has (a parent with children at two schools has two), with enough per-membership detail for the client to build its own school switcher and decide what to render. There is no server-side active school: the client keeps its own choice. is_fully_deactivated is true when every membership of the account is deactivated; the client should then show an account-deactivated screen instead of the roster.

Responses

  • 200 application/json MeResponse

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

POST/registration/confirm

Complete registration after email confirmation

Call once after a freshly confirmed sign-up, for example when the user opens the confirmation link in your app. It looks the caller's verified email up in the school invitations (the registration allowlist), sets up the matching school memberships and guardian links, and returns what was set up so the client can show a "here is what we found" confirmation screen.

Responses

  • 200 application/json RegistrationConfirmResult

    Provisioning complete (or already had been, safe to call twice)

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found

Categories

Per-school content categories shared by Documents and Notices (read-only here).

GET/schools/{schoolId}/categories

List the school's content categories

The per-school category vocabulary shared by Documents and Notices, in the order a Senior Admin arranged them. Any school member may read (parents need names/colors to render pills and filter chips). Not feature-gated.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    categoriesarray of CategoryYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

Directory

Roster, student detail, map markers and stars.

GET/schools/{schoolId}/students

List the roster

Every non-archived student at the school, sorted by last name, then first name. q matches first or last name (partial, case-insensitive); a 4-digit q also matches the graduation year exactly, and for admin callers it also searches linked parents' name, phone and email (it adds nothing for a parent caller). The full result set is always returned: there is no pagination yet, and one may be added in a future minor version without a new API version. full=true returns RosterStudentFull items instead of RosterStudent; they add latitude/longitude, the caller's own note and full guardian contact (id, email and phone, not just name and relation), so a client can cache the whole roster in one round trip. full=true ignores q and graduationYear.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
qquerystringNo

Free-text search across name (and, for admins, linked parent contact info). Ignored when full=true.

graduationYearqueryintegerNo

Exact-match filter. Ignored when full=true.

fullquerystring, one of "true"No

When "true", returns RosterStudentFull items (see above) instead of RosterStudent.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    studentsarray of RosterStudent or RosterStudentFullYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

GET/schools/{schoolId}/map-markers

List geocoded student pins for the map view

Every non-archived, geocoded student, except: for a non-admin (parent) caller, a student's pin is dropped when that student's address_visible is false. The caller's own linked students are never suppressed, and admins always see every geocoded student. Coordinates are computed by the server, so a client never needs to geocode. Supplying org, list or starred narrows the response to that one group's students instead of the whole school; an unknown or other-school organization, an archived organization for a non-admin, or another user's list is a 404. The visibility rule above still applies within the scoped set, so scoping never reveals a pin the caller could not otherwise see. Supplying none of the three returns the whole-school response.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
orgquerystringNo

Organization short_name; narrows the response to that org's roster.

listquerystring (uuid)No

Saved list id; narrows the response to that list's members. Owner-scoped.

starredquerystringNo

Any non-empty value narrows the response to the caller's starred students.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    markersarray of MapMarkerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/students/{studentId}

Get student detail

notes is always scoped to the caller's own notes on this student — never another guardian's or an admin's.

If the student has no coordinates yet (latitude/longitude both null) but has a full address, this endpoint geocodes on the caller's behalf inline and returns the freshly computed coordinates in the same response — the client should not attempt this itself.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 200 application/json StudentDetail

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/students/{studentId}/star

Star a student

Private per-user. Idempotent — starring an already-starred student is a no-op success.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 204

    Starred

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

DELETE/students/{studentId}/star

Unstar a student

Idempotent — unstarring a not-starred student is a no-op success.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 204

    Unstarred

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

GET/schools/{schoolId}/guardians

List every guardian at the school

The whole-school guardian directory, for a client-side cache behind "add to list" pickers. Any school member may call it. Each row is one person: registered (is_registered: true, and the id is the membership id) or pending (the id is the invitation id). Fields the caller may not see (for example a parent who chose to hide their contact details) come back null.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    guardiansarray of objectYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

Notes

Private notes a guardian keeps on a student. Never shared with other guardians.

GET/students/{studentId}/note

Get the caller's own note on a student

Returns note: null if the caller hasn't left one yet.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    noteNoteYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

PUT/students/{studentId}/note

Save (create/update/clear) the caller's own note

Upserts the caller's note keyed by (student, author). A blank/empty body deletes the note instead of storing an empty row - the response's note is null in that case.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
bodystringYes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    noteNoteYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

Lists

A user's private custom lists of students and parents.

GET/schools/{schoolId}/lists

List the caller's own custom lists at a school

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    listsarray of StudentListYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

POST/schools/{schoolId}/lists

Create a list

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
namestringYes
descriptionstring, nullableNo

Responses

  • 201 application/json StudentList

    Created

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

GET/lists/{listId}

Get a list and its students

Returns 404 (not 403) if the list doesn't belong to the caller — ownership is not revealed.

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Responses

  • 200 application/json StudentListDetail

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

PATCH/lists/{listId}

Rename a list

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
namestringYes

Responses

  • 200 application/json StudentList

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/lists/{listId}

Delete a list

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Responses

  • 204

    Deleted

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/lists/{listId}/students

Add a student to a list

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
student_idstring (uuid)Yes

Responses

  • 204

    Added

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/lists/{listId}/students/{studentId}

Remove a student from a list

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes
studentIdpathstring (uuid)Yes

Responses

  • 204

    Removed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/lists/{listId}/guardians

List a list's individual parent/guardian entries

Owner-only, same as every other list route.

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Responses

  • 200 application/json array of ListGuardian

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/lists/{listId}/guardians

Add a parent/guardian directly to a list

Not derived from a student being on the list. linked_student_id picks which of the parent's students to source an address from for the row.

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
guardian_membership_idstring (uuid)Yes
linked_student_idstring (uuid), nullableNo

Responses

  • 201 application/json ListGuardian

    Created

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/lists/{listId}/guardians/{guardianId}

Remove a parent/guardian entry from a list

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes
guardianIdpathstring (uuid)Yes

Responses

  • 204

    Removed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/lists/{listId}/message-recipients

Resolved "Message Everyone" recipient list for a list, including phone

The resolved "Message Everyone" recipient list for one of the caller's own lists, including phone numbers (masked by each guardian's privacy settings). Owner only.

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Responses

  • 200 application/json array of MessageRecipient

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/lists/{listId}/candidates

Minimal candidate list for the "add a parent" picker (list owner only)

Mirrors GET /organizations/{organizationId}/candidates - a parent adding another parent to their own list needs to search among every parent at the school, the same "any school member, not just my own" scope adding a student already has.

Parameters

NameInTypeRequiredDescription
listIdpathstring (uuid)Yes

Responses

  • 200 application/json array of OrganizationCandidate

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

Profile

The signed-in user's own contact information and visibility settings, and leaving a school.

GET/me/contact

Get the caller's own parent contact info

There is no separate guardian-profile table — a parent's name/phone is stored on their registration invitation(s), keyed by verified email, and this reads across every school/ student the caller is linked to, picking the most recently created row that actually has data filled in.

Responses

  • 200 application/json MyContact

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

PATCH/me/contact

Update the caller's own parent contact info

Updates the caller's own parent contact information. The change applies to every school and student the parent is linked to at once, because a parent has one identity across schools, matched by their verified email.

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
first_namestringYes
last_namestringYes
phonestringYes

Responses

  • 200 application/json MyContact

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

PATCH/me/profile-visibility

Set the caller's own contact visibility

Sets whether the caller's contact details (email and phone) are visible to other parents, across every school at once. full makes them visible and name_only hides them. Home-address visibility is a separate control: see PATCH /me/address-visibility.

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
profile_visibilitystring, one of "full", "name_only"Yes

Responses

  • 204

    Updated

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

PATCH/me/address-visibility

Set the caller's own home-address visibility

Sets whether the caller's family home address is visible to other parents. It applies to every student the caller is a linked guardian of. Admins and the family's own linked guardians always see the address. A caller with no linked students gets a success with no effect.

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
address_visiblebooleanYes

Responses

  • 204

    Updated

  • 400

    address_visible must be a boolean

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

POST/me/leave-school/{membershipId}

Leave a school immediately (no grace period)

Self-service, any role. Blocked with 409 if the caller is the last Senior Admin of that school (a school can never be left without one). Deletes the caller's own notes, stars, guardian links and list memberships for that school and removes the membership immediately, with no grace period.

Parameters

NameInTypeRequiredDescription
membershipIdpathstring (uuid)Yes

Responses

  • 204

    Left

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 409 application/json Error

    Caller is the school's only senior_admin

Photos

Student, parent, staff and organization photos, uploaded and downloaded through short-lived signed URLs.

POST/students/{studentId}/photo/upload-url

Get a pre-signed upload URL for a student's photo

The returned photo_key is deterministic (student-photos/{studentId}.{ext}) — re-uploading overwrites the previous photo rather than orphaning it, matching the existing convention. Max 5 MB, image/jpeg, image/png, image/webp, or image/gif only (enforced both by the upload-URL's constraints and re-validated when the client calls the confirm step below).

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
content_typestring, one of "image/jpeg", "image/png", "image/webp", "image/gif"Yes

Responses

  • 200 application/json PhotoUploadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

POST/students/{studentId}/photo

Confirm a completed photo upload

Call after successfully PUT-ing bytes to the URL from upload-url above.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
photo_keystringYes

Responses

  • 200

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

DELETE/students/{studentId}/photo

Remove a student's photo

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 204

    Removed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

GET/students/{studentId}/photo/download-url

Get a short-lived signed URL to view a student's photo

Clients must not cache or share this URL, and should re-request it each time a photo needs to be displayed rather than persisting it.

Parameters

NameInTypeRequiredDescription
studentIdpathstring (uuid)Yes

Responses

  • 200 application/json PhotoDownloadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Student has no photo

GET/parents/{parentId}/photo/download-url

Get a short-lived signed URL to view a parent/guardian's photo

Same 5-minute TTL signed GET URL shape as a student's own photo.

Parameters

NameInTypeRequiredDescription
parentIdpathstring (uuid)Yes

Responses

  • 200 application/json PhotoDownloadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Parent has no photo

POST/parents/{parentId}/photo

Confirm a completed parent photo upload

Stores photo_key on the parent's invitation row (an admin writes that one row; a parent uploading their own photo propagates it to every row with their email at the school, since a photo belongs to the person). photo_key must be the key issued for THIS parent. The superseded object is deleted once no other row references it. Allowed for an admin of the row's school, or the parent themselves (matched by sign-in email).

Parameters

NameInTypeRequiredDescription
parentIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
photo_keystringYes

Responses

  • 200 application/json object

    Stored.

    FieldTypeRequiredDescription
    photo_keystringYes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 500 application/json Error

    Response

DELETE/parents/{parentId}/photo

Remove a parent's photo

Deletes the object from storage and clears photo_key (same admin-versus-self propagation as the confirm route). Idempotent, so 204 even when there is no photo.

Parameters

NameInTypeRequiredDescription
parentIdpathstring (uuid)Yes

Responses

  • 204

    Removed.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

POST/parents/{parentId}/photo/upload-url

Get a pre-signed upload URL for a parent's photo

Same flow as the student photo upload-url. Allowed for an admin of the row's school or the parent themselves. Maximum size is enforced server side (413 above it).

Parameters

NameInTypeRequiredDescription
parentIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
content_typestring, one of "image/jpeg", "image/png", "image/webp", "image/gif"Yes
content_lengthintegerYes

Size of the file in bytes.

Responses

  • 200 application/json PhotoUploadUrl

    OK

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 413 application/json Error

    Too large

  • 500 application/json Error

    Response

GET/staff/{staffId}/photo/download-url

Get a short-lived signed URL to view a staff member's photo

Open to any current member of the staff member's school. 5-minute lifetime; request it each time rather than storing it.

Parameters

NameInTypeRequiredDescription
staffIdpathstring (uuid)Yes

Responses

  • 200 application/json PhotoDownloadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

Menus

Lunch, breakfast and snack menu reads.

GET/schools/{schoolId}/menus

List every menu entry for the school

Every menu entry for the school across all dates, for caching. The client filters to the visible week or day locally. 404s if the school has the lunch_menus feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    menusarray of MenuEntryYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The lunch_menus feature is toggled off for this school.

GET/schools/{schoolId}/menus/week

Get one week's menus

start must be a Sunday (ISO date); the response covers that date through six days later inclusive. 404s if the school has the lunch_menus feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
startquerystring (date)Yes

ISO date, must fall on a Sunday.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    menusarray of MenuEntryYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The lunch_menus feature is toggled off for this school.

GET/schools/{schoolId}/menus/day

Get one day's menus (all meal types)

404s if the school has the lunch_menus feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
datequerystring (date)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    menusarray of MenuEntryYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The lunch_menus feature is toggled off for this school.

POST/schools/{schoolId}/menus/{menuId}/image

Get (lazily generating if needed) or force-regenerate a lunch entry's AI image

Two callers hit this same route: the plain (no force) path is open to any authenticated member of the school (parent or admin) — it generates only when there's no image yet, or the entry's current items text no longer matches the hash the last generation used, otherwise it returns the existing image_url immediately with no regeneration. ?force=true always regenerates regardless of current state and is restricted to an admin (junior or senior) — this is the client's "Regenerate Image" button. Only valid for meal_type: lunch entries. 404s if the school has the lunch_menus feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
menuIdpathstring (uuid)Yes
forcequerybooleanNo

Always regenerate, even if an image already exists and its hash still matches. Admin only.

Responses

  • 200 application/json object

    OK — the (possibly freshly-generated, possibly already-existing) image.

    FieldTypeRequiredDescription
    image_urlstring (uri)Yes
    image_generated_atstring (date-time), nullableYes
  • 400 application/json Error

    The entry's meal_type is not lunch.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not a member of this school, or force=true was used by a non-admin.

  • 404 application/json Error

    Menu entry not found, or the lunch_menus feature is toggled off for this school.

  • 502 application/json Error

    Image generation failed (provider error, rate limit, or content-safety rejection). Never happens as a side effect of saving/importing a menu — this route is only ever called after a menu entry already exists.

Calendar

School and organization calendar events, repeating-event series and calendar subscriptions.

GET/schools/{schoolId}/calendar

List every calendar event for the school

Every calendar event for the school across all dates, for caching. The client filters to the visible day, week or month locally; there is no date-range parameter in this API version. 404s if the school has the calendar feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    eventsarray of CalendarEventYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The calendar feature is toggled off for this school.

POST/schools/{schoolId}/calendar/subscriptions

Create a standing.ics calendar subscription on the caller's behalf

Creates a standing .ics calendar subscription for the calling user, returning an unguessable token that a calendar app can poll at https://studentdirectory.app/api/calendar/subscribe/{token}/feed.ics. A set-and-forget alternative to inserting events directly. categories and tags are inclusion filters (empty means the full calendar); organizationId, if given, scopes the feed to that organization's events. 404s if the school has the calendar feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

application/json, optional. Type: object

FieldTypeRequiredDescription
namestringNo
categoriesarray of stringNo
tagsarray of stringNo
organizationIdstringNo

Responses

  • 201 application/json object

    Created

    FieldTypeRequiredDescription
    subscriptionobjectYes
  • 400 application/json Error

    organizationId does not belong to this school.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The calendar feature is toggled off for this school.

GET/organizations/{organizationId}/calendar-series

List an organization's (or class's, or department's) repeating events (admin or that organization's Editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    seriesarray of CalendarSeriesYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Organization not found or Calendar disabled

POST/organizations/{organizationId}/calendar-series

Create a repeating event on an organization's calendar (admin or that organization's Editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: CalendarSeriesCreateBody

Responses

  • 201 application/json object

    Created

    FieldTypeRequiredDescription
    seriesCalendarSeriesYes
    generated_countintegerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Organization not found or Calendar disabled

  • 422 application/json Error

    The rule or template is invalid

POST/organizations/{organizationId}/calendar-series/preview

Preview a repeating event on an organization's calendar (no writes)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: CalendarSeriesCreateBody

Responses

  • 200 application/json CalendarSeriesPreview

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Organization not found or Calendar disabled

  • 422 application/json Error

    The rule or template is invalid

GET/calendar-series/{seriesId}

Get a repeating event series (rule, template, summary, counts, flagged instances)

Parameters

NameInTypeRequiredDescription
seriesIdpathstring (uuid)Yes
includequerystring, one of "occurrences"No

occurrences adds every instance (date order) for the management screen. Admin or the series' calendar Editor only (403 otherwise).

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    seriesCalendarSeriesYes
    occurrencesarray of objectNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Series not found

PATCH/calendar-series/{seriesId}

Edit a series for this event, this and following, or all events

scope: this edits one instance (it becomes edited); following splits the series at occurrence_date and the response is the NEW series; all re-diffs every instance. Hand-edited instances are kept (kept_edited_count); a deleted instance is never resurrected.

Parameters

NameInTypeRequiredDescription
seriesIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
scopestring, one of "this", "following", "all"Yes
occurrence_datestring (date)No
fieldsobjectNo
ruleCalendarSeriesRuleNo
skip_closuresbooleanNo

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    seriesCalendarSeriesYes
    affected_countintegerYes
    kept_edited_countintegerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Series or occurrence not found

  • 422 application/json Error

    Invalid scope, rule or fields

DELETE/calendar-series/{seriesId}

Delete a series for this event, this and following, or all events

Parameters

NameInTypeRequiredDescription
seriesIdpathstring (uuid)Yes
scopequerystring, one of "this", "following", "all"Yes
occurrence_datequerystring (date)No

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    deleted_countintegerYes
    kept_edited_countintegerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Series or occurrence not found

  • 422 application/json Error

    Invalid scope or date

POST/organizations/{organizationId}/calendar-events

Create an event on an organization's own calendar

Requires an admin or a live Organization Editor grant on that organization, and the school's calendar feature to be on. Runs the same create logic as the web's organization-scoped event form (validation, editor authorization, audit log).

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
titlestringYes
descriptionstringNo
categorystringNo

Defaults to general.

tagsstringNo

Comma-separated.

start_datestring (date)Yes
end_datestring (date)No
start_timestringNo

HH:MM, 24-hour, school time zone.

end_timestringNo

HH:MM, 24-hour, school time zone.

locationstring, nullableNo

Optional venue; empty or null for none.

Responses

  • 201 application/json object

    Created.

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

PATCH/schools/{schoolId}/calendar-events/{eventId}

Edit a calendar event

A full-form re-submit (not a sparse patch), except location: omitted leaves it unchanged. Authorization is decided by the event itself: a school-wide event needs an admin, an organization event needs an admin or a live Organization Editor grant. Returns the updated event.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
eventIdpathstring (uuid)Yes

Request body

application/json, required. Type: CalendarEventInput

Responses

  • 200 application/json CalendarEvent

    The updated event (with its series summary fields when it belongs to a series).

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

DELETE/schools/{schoolId}/calendar-events/{eventId}

Delete a calendar event

A hard delete. Same per-event authorization as the edit route; if the caller is refused the event survives and the response is 403.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
eventIdpathstring (uuid)Yes

Responses

  • 204

    Deleted.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

Staff Directory

The school staff listing.

GET/schools/{schoolId}/staff

List the school's staff directory

Every staff member of the school, visible to any school member. 404s if the school has the staff_directory feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    staffarray of StaffMemberYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The staff_directory feature is toggled off for this school.

Document Library

Documents and attachments visible to the user, plus authoring for Organization Editors.

GET/schools/{schoolId}/documents

List Document Library entries visible to the caller

Every document currently visible to the caller, with the auto-populated "School Details" entry prepended first when present - matching the web /documents page exactly, for every caller (admin or parent alike; the unfiltered admin management view has no /v1 equivalent). 404s if the school has the document_library feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    documentsarray of DocumentYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The document_library feature is toggled off for this school.

POST/schools/{schoolId}/documents

Create a document

An admin, or an Organization Editor creating for an organization they hold a live grant on (organization_id; the grant is verified server side). Attachments are uploaded first through POST /schools/{schoolId}/attachments and passed back in attachments. 404 when the document_library feature is off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

application/json, required. Type: DocumentInput

Responses

  • 201 application/json object

    Created.

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

GET/schools/{schoolId}/documents/{documentId}/attachments/{attachmentId}/download-url

Get a short-lived signed URL for one Document Library attachment

A DocumentAttachment.key is a B2 private-bucket object key, not a downloadable URL by itself - this route signs one, matching the existing student/parent/organization-photo download-url pattern (5-minute TTL, same PhotoDownloadUrl response shape). The synthetic "School Details" entry has no attachments and is never a valid documentId here.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
documentIdpathstringYes
attachmentIdpathstring (uuid)Yes

Responses

  • 200 application/json PhotoDownloadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The document_library feature is toggled off, the document doesn't exist or isn't visible to the caller, or the attachment id doesn't belong to that document.

POST/schools/{schoolId}/attachments

Upload an attachment before its Document or Notice exists

The Bearer-token twin of the web's POST /api/attachments/upload: stores one file in the private bucket under the school's key prefix and returns its metadata (DocumentAttachment) WITHOUT tying it to any record. The client uploads each file as the user attaches it, then sends the whole attachments array with POST /schools/{schoolId}/documents (or the notices create), so the new document is created with its attachments and the audience's one-time "new document" alert names a complete document. Save rejects a key that is not this school's own.

Allowed for a school admin, or a user holding a live Organization Editor grant. Types: PDF, Word, Excel, JPEG, PNG. Maximum 10 MB.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

multipart/form-data, required. Type: object

FieldTypeRequiredDescription
filestring (binary)Yes
entity_typestring, one of "document", "notice"Yes

Selects the storage key prefix.

Responses

  • 201 application/json DocumentAttachment

    Uploaded.

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 413 application/json Error

    Too large

  • 500 application/json Error

    Response

GET/schools/{schoolId}/documents/{documentId}

Get one document

A single document's full detail, by id. The school-demographics sentinel id returns the auto-populated School Details entry. A document the caller may not see is a 404, the same as one that does not exist.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
documentIdpathstringYes

Responses

  • 200 application/json Document

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

PATCH/schools/{schoolId}/documents/{documentId}

Edit a document

A full re-submit of the document. Authorization is decided by the document (an admin, or an Organization Editor with a live grant on the organization the document belongs to). Returns the updated document, or null if it could not be re-read.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
documentIdpathstringYes

Request body

application/json, required. Type: DocumentInput

Responses

  • 200 application/json Document

    The updated document.

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

DELETE/schools/{schoolId}/documents/{documentId}

Delete a document

A hard delete, including cleanup of the document's stored attachments. If the caller is refused the document survives and the response is 403.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
documentIdpathstringYes

Responses

  • 204

    Deleted.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

Notices

Time-bounded notices visible to the user, with read markers, plus authoring for Organization Editors.

GET/schools/{schoolId}/notices

List notices visible to the caller

Every notice currently visible to the caller, for admins and parents alike: audience targeting and expiry are already applied, so an expired notice or one addressed to other people is not returned. 404s if the school has the notices feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    noticesarray of NoticeYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The notices feature is toggled off for this school.

POST/schools/{schoolId}/notices

Create a notice

An admin, or an Organization Editor creating for an organization they hold a live grant on (organization_id). Runs the same create logic as the web notice form (validation, audience, the one-time alert to the audience). 404 when the notices feature is off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

application/json, required. Type: NoticeInput

Responses

  • 201 application/json object

    Created.

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

POST/notices/{noticeId}/read

Mark a notice read

Marks a notice read for the caller. Private per user and idempotent (already-read is a successful no-op). The notice must be one the caller can see.

Parameters

NameInTypeRequiredDescription
noticeIdpathstring (uuid)Yes

Responses

  • 204

    Marked read.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 500 application/json Error

    Response

DELETE/notices/{noticeId}/read

Mark a notice unread

Idempotent. Removes the caller's own read marker.

Parameters

NameInTypeRequiredDescription
noticeIdpathstring (uuid)Yes

Responses

  • 204

    Marked unread (or it already was).

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

GET/rich-text-images/resolve

Resolve an inline rich-text image reference to a signed URL

Turns the stable flux-image://<key> reference embedded in rich-text content into a 5-minute signed download URL. Keys outside the upload shape (for example path traversal into another folder) are refused.

Parameters

NameInTypeRequiredDescription
keyquerystringYes

The image key from the flux-image:// reference.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    urlstring (uri)Yes
    expires_inintegerYes

    Seconds until the URL expires (300).

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found

POST/rich-text-images/upload-url

Get a pre-signed upload URL for an inline rich-text image

For the native editor's image button. PUT the raw bytes to upload_url, then embed flux-image://<key> in the content. Allowed for a user who is an admin at any of their schools, or who holds at least one live Organization Editor grant. The gate is deliberately coarse, since the real authorization is at read time.

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
content_typestring, one of "image/jpeg", "image/png"Yes
content_lengthintegerYes

Size in bytes.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    upload_urlstring (uri)Yes
    keystringYes
    expires_inintegerYes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 413 application/json Error

    Too large

  • 500 application/json Error

    Response

GET/schools/{schoolId}/notices/{noticeId}

Get one notice

One notice's full detail, by id, with is_read and audience_label. A notice the caller may not see answers 404, the same as one that does not exist.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
noticeIdpathstring (uuid)Yes

Responses

  • 200 application/json Notice

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

PATCH/schools/{schoolId}/notices/{noticeId}

Edit a notice

Fields omitted keep their current value. An admin may change audience, category and flags; an Organization Editor edits through organization_id and cannot change the audience. Existing attachments are kept. Returns the updated notice, or null if it could not be re-read.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
noticeIdpathstring (uuid)Yes

Request body

application/json, required. Type: NoticeInput

Responses

  • 200 application/json Notice

    The updated notice.

  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

DELETE/schools/{schoolId}/notices/{noticeId}

Delete a notice

A hard delete; the notice's read markers go with it. The caller needs the same rights as for an edit.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
noticeIdpathstring (uuid)Yes

Responses

  • 204

    Deleted.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

GET/schools/{schoolId}/notices/{noticeId}/attachments/{attachmentId}/download-url

Get a short-lived signed URL for one notice attachment

Same shape and 5-minute lifetime as the document attachment route. The notice must be visible to the caller; an unknown attachment is a 404.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
noticeIdpathstring (uuid)Yes
attachmentIdpathstring (uuid)Yes

Responses

  • 200 application/json PhotoDownloadUrl

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

Permission Slips

Permission slips addressed to the user and their children, with answers and a printable PDF.

GET/schools/{schoolId}/permission-slips

List permission slips visible to the caller

Every permission slip that has at least one of the caller's own linked students eligible for it. A slip never auto-hides once its required_by date passes: completed and outstanding slips both stay in the list, sorted by required_by ascending (most urgent first). 404s if the school has the permission_slips feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    permission_slipsarray of PermissionSlipYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The permission_slips feature is toggled off for this school.

POST/schools/{schoolId}/permission-slips

Create a permission slip

An admin, or an Organization Editor issuing for an organization they hold a live grant on (organization_id). Create only: permission slips cannot be edited or deleted through this API. 404 when the permission_slips feature is off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
titlestringYes
body_deltaobjectNo

Rich-text Delta document for the body.

audience_idstring (uuid), nullableNo
organization_idstring (uuid)No

Issue for this organization. An Organization Editor's only path.

required_bystring (date)Yes

The date the slip must be completed by.

Responses

  • 201 application/json object

    Created.

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

PATCH/schools/{schoolId}/permission-slips/{slipId}

Update a permission slip (admin, or an Editor of the organization it belongs to)

A full re-submit of the slip (title, body_delta, and required_by are required). Authorization comes from the access token, never the body: an admin of the school may also set audience_id (or point the slip at an organization with organization_id); an Editor of an organization may edit only slips addressed to that organization's own audience, may not pass audience_id, and may name only the organization the slip already belongs to. 403 otherwise.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
slipIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
titlestringNo
body_deltaobjectNo

Quill Delta document for the body.

audience_idstring (uuid), nullableNo

Admin only.

organization_idstring (uuid)No
required_bystring (date)No

Responses

  • 200 application/json object

    Updated.

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed (blank title, missing required-by date, body too long, invalid audience).

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/schools/{schoolId}/permission-slips/{slipId}

Delete a permission slip (admin, or an Editor of the organization it belongs to)

Hard delete (its recorded responses go with it). Same authorization as PATCH: an admin, or an Editor of the organization whose own audience the slip is addressed to; 403 for anyone else (the route re-reads the row to detect a silent refusal), 404 if it does not exist.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
slipIdpathstring (uuid)Yes

Responses

  • 204

    Deleted.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/permission-slips/{slipId}

Get one permission slip

Not found and not-eligible both collapse to a 404 — a caller can't distinguish "doesn't exist" from "not permitted" (mirrors GET /students/{studentId}).

Parameters

NameInTypeRequiredDescription
slipIdpathstring (uuid)Yes

Responses

  • 200 application/json PermissionSlip

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/permission-slips/{slipId}/pdf

Get a printable PDF export of the slip

A printable PDF of the slip. Returns the PDF bytes (Content-Type: application/pdf) rather than a redirect or signed URL. Available to the same callers as GET /permission-slips/{slipId}.

Parameters

NameInTypeRequiredDescription
slipIdpathstring (uuid)Yes
formatquerystring, one of "letter", "a4"No

Responses

  • 200 application/pdf string (binary)

    The generated PDF.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/permission-slips/{slipId}/respond

Answer the slip (Yes / No) for one or more linked students

Records the caller's answer per student: granted (Yes) or denied (No, with an optional reason). Two request shapes are accepted. Batch: { "answers": [{ "student_id", "decision", "reason"? }, ...] } sends every chosen student at once and is all-or-nothing: if any student is not the caller's own currently-targeted child, nothing is saved (403). Legacy single student: { "student_id" }, optionally with decision and reason; a missing decision is a grant and never overwrites an answer already on file. An answer can be changed (granted or denied, and the reason edited) any number of times until the slip's window closes, which is the day after required_by ends (UTC); after that the route returns 400 and the slip is read-only. Re-sending an unchanged answer is a harmless no-op. Moving back to granted clears the reason. reason is trimmed, kept only for a denial, and capped at 500 characters. The legacy response field completed_at is the first answer's timestamp.

Parameters

NameInTypeRequiredDescription
slipIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
answersarray of objectNo
student_idstring (uuid)No

Legacy single-student shape (use answers instead).

decisionstring, one of "granted", "denied"No
reasonstring, nullableNo

Responses

  • 200 application/json object

    Recorded (or unchanged, returned idempotently).

    FieldTypeRequiredDescription
    completed_atstring (date-time)Yes
    answersarray of objectYes
  • 400 application/json Error

    Invalid answers (missing student_id, unknown decision, over-long reason, duplicate student, empty list), or the slip's window has closed.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    At least one student is not the caller's own guardian-linked child currently targeted by this slip (nothing is saved).

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

Features

Per-school feature toggles, time zone and sidebar counts.

GET/schools/{schoolId}/features

Get the school's current feature toggle state

The school's current feature toggles as a map of feature key to boolean, so a client can hide or disable gated screens up front instead of discovering a disabled feature through a 404. Open to any school member.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    featuresFeatureToggles and objectYes

    The FeatureKey -> boolean map, plus the EFFECTIVE classes and departments flags: true only when the Operator made the feature available AND the school turned it on (opt-in at both levels, so absent/false means off - unlike the older keys, where a missing key means on).

    timezonestringNo

    The school's IANA timezone, e.g. America/New_York.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

GET/schools/{schoolId}/timezone

Get the school's IANA timezone

Open to any school member.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    timezonestringYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

GET/schools/{schoolId}/sidebar-counts

Get the caller's sidebar "needs attention" counts for Polls, Permission Slips and Sign-Ups

Per-user counts behind the sidebar badges (zero means show no badge). Every caller, admins included, gets the same per-user semantics as a parent; only the caller's own state is reported. A feature turned off for the school counts 0 (this route does not 404 on a disabled feature). polls: visible polls that are open right now and that the caller has not answered. permission_slips: slips for which at least one of the caller's linked students is targeted and not yet completed, and whose required_by date has not passed (counted through the end of that day in the viewer's zone); a slip counts once however many of the caller's children still owe it. signups: visible sign-ups that are open for responses, that the caller did not create or administer as an Organization Editor, and that the caller has not yet opened (GET /signups/{signupId} records the open).

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
tzquerystringNo

IANA time zone of the viewer (e.g. America/New_York), used for the permission-slip due-date cut-off. UTC when absent or invalid.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    pollsintegerYes
    permission_slipsintegerYes
    signupsintegerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

Watch

A compact roster and star payload for companion watch apps.

GET/schools/{schoolId}/watch-payload

Get the compact roster + star payload for WatchConnectivity relay

A compact roster and star payload for companion watch apps: deliberately minimal fields (no address, no photo, no notes). Star changes made on the watch go back through POST and DELETE /students/{studentId}/star, called by the paired phone.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json WatchPayload

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

Organizations

Teams, performing arts, clubs and classes: description, leadership, roster, results, schedule and photos. Reads are open to any school member; writes need an Organization Editor grant.

GET/schools/{schoolId}/organizations

List the school's organizations

Non-archived only by default; includeArchived=true is honored for admins only. Optional category filter (athletics, performing_arts, clubs, other). 404s if the organizations feature toggle is off. kind selects what is listed: absent or organization returns only ordinary organizations; kind=class or kind=department returns { organizations: [ClassOrDepartment] } and 404s unless that feature is active for the school. A hidden class roster yields only a count and the caller's own linked children, and a private class is not returned to a parent unless their child is on it.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
kindquerystring, one of "organization", "class", "department"No

organization (default) | class | department

categoryquerystring, one of "athletics", "performing_arts", "clubs", "other"No
includeArchivedquerybooleanNo

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    organizationsarray of OrganizationSummaryYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The organizations feature is toggled off for this school.

GET/schools/{schoolId}/organizations/{shortName}

Get an organization's full detail

Core fields, photo keys (not signed URLs - see the Photos routes below), leadership, roster, its filtered calendar events, and can_edit/can_manage for the caller. An archived organization 404s for a non-admin caller.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes
shortNamepathstringYes

Responses

  • 200 application/json Organization

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

PATCH/organizations/{organizationId}

Update an organization's description (Admin or org editor)

A name field in this route's body is silently ignored.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, optional. Type: object

FieldTypeRequiredDescription
description_deltaobjectNo

Quill Delta document for the description.

Responses

  • 200 application/json OrganizationSummary

    OK

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not an admin or a live org-editor grant for this organization.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/organizations/{organizationId}/schedule

A class's weekly meeting patterns

Anyone who can see the class. Each pattern is a weekly repeating series of source class_schedule whose events appear on the class calendar. 404 unless the organization is a class and Classes is active.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    patternsarray of ClassSchedulePatternYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not a class, or Classes disabled

POST/organizations/{organizationId}/schedule

Add a meeting pattern to a class (Admin or the class's Editor)

Generates the pattern's events on the class calendar (skipping school closures unless skip_closures is false). Weekly only; the date range is normally the class's term. At most 400 meetings.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: ClassSchedulePatternBody

Responses

  • 201 application/json object

    Created

    FieldTypeRequiredDescription
    patternClassSchedulePatternNo
    generated_countintegerNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

  • 422 application/json Error

    The pattern is invalid

POST/organizations/{organizationId}/schedule/preview

Preview the dates a meeting pattern would generate (no writes)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: ClassSchedulePatternBody

Responses

  • 200 application/json CalendarSeriesPreview

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

  • 422 application/json Error

    The pattern is invalid

PATCH/organizations/{organizationId}/schedule/{seriesId}

Replace one meeting pattern (regenerates only its own events)

Same body as create. Events someone edited by hand are kept; events added by hand are never touched.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
seriesIdpathstring (uuid)Yes

Request body

application/json, required. Type: ClassSchedulePatternBody

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    patternClassSchedulePatternNo
    affected_countintegerNo
    kept_edited_countintegerNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found

  • 422 application/json Error

    The pattern is invalid

DELETE/organizations/{organizationId}/schedule/{seriesId}

Remove a meeting pattern and the events it generated

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
seriesIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    deleted_countintegerNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found

GET/organizations/{organizationId}/end-of-term

A class's term dates, end-of-term plan and latest runs (Admin or the class's Editor)

plan is null when none is saved, and then nothing happens at the end of the term. The plan runs once per term, shortly after ends_on has finished in the SCHOOL's timezone.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    organization_idstring (uuid)No
    starts_onstring (date), nullableNo
    ends_onstring (date), nullableNo
    lifecyclestring, one of "active", "awaiting_dates", "archived"No
    planEndOfTermPlanNo
    runsarray of EndOfTermRunNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

PUT/organizations/{organizationId}/end-of-term

Save (or remove) the end-of-term plan

Body is an object with a plan key holding an EndOfTermPlan, or plan set to null to remove it. Every key of the plan is required; anything left at keep is untouched at the end of the term.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
planEndOfTermPlanYes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    planEndOfTermPlanNo
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

  • 422 application/json Error

    The plan is invalid

POST/organizations/{organizationId}/end-of-term/preview

Preview what running the saved plan now would change (no writes)

422 when no plan is saved or the class is archived.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json EndOfTermResult

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

  • 422 application/json Error

    No plan saved, or the class is archived

POST/organizations/{organizationId}/end-of-term/run

Run the saved end-of-term plan now ("Run reset now")

Runs the plan immediately. Idempotent per term (a second call answers already_ran: true), recorded in the school audit log and the class's run history, and the class's Editors are notified. Not undoable.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json EndOfTermResult

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not a class, or Classes disabled

  • 422 application/json Error

    No plan saved, or the class is archived

POST/organizations/{organizationId}/leaders

Add a leadership entry (Admin or org editor)

Either school_staff_id (validated against the organization's own school) or a freeform display_name/display_title. display_title doubles as the org-specific role label (e.g. "Head Coach") for both cases.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, optional. Type: object

FieldTypeRequiredDescription
school_staff_idstring (uuid), nullableNo
display_namestring, nullableNo
display_titlestring, nullableNo
display_orderintegerNo

Responses

  • 201 application/json OrganizationLeader

    Created

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

PATCH/organizations/{organizationId}/leaders/{leaderId}

Edit a leadership entry (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
leaderIdpathstring (uuid)Yes

Request body

application/json, optional. Type: object

FieldTypeRequiredDescription
school_staff_idstring (uuid), nullableNo
display_namestring, nullableNo
display_titlestring, nullableNo
display_orderintegerNo

Responses

  • 200 application/json OrganizationLeader

    OK

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/organizations/{organizationId}/leaders/{leaderId}

Remove a leadership entry (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
leaderIdpathstring (uuid)Yes

Responses

  • 204

    No Content

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/organizations/{organizationId}/members

List an organization's parent members

Roster-visible content - any school member can read it, not just admin/editor, matching the leaders/students routes' own visibility.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json array of OrganizationParentMember

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/members

Add a parent as an organization member

Independent of whether that parent's child is on the org's roster. For a Self-Sign-Up organization's own self-service join (no Editor/Admin grant required), see POST /organizations/{organizationId}/members/self-join instead.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
membership_idstring (uuid)Yes

Responses

  • 201 application/json OrganizationParentMember

    Created

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/organizations/{organizationId}/members/{membershipId}

Remove an organization member

Self-removal ("leave") is out of scope for this build - a self-joined member is removed the same way any member is, by an Editor or Admin.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
membershipIdpathstring (uuid)Yes

Responses

  • 204

    No Content

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/members/self-join

Self-join a Self-Sign-Up organization

Any parent at the school can join without Editor or Admin approval, but only when the organization's membership_mode is self_signup (fixed when the organization is created). The membership used is always the caller's own at the organization's school, never taken from the request body. Idempotent: joining an organization the caller already belongs to returns 204, not an error.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 201 application/json OrganizationParentMember

    Created

  • 204

    No Content (already a member)

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/organizations/{organizationId}/candidates

Minimal candidate list for the add-member/add-editor picker (Admin or org editor)

Excludes deactivated memberships; returns only id/email/role.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json array of OrganizationCandidate

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/organizations/{organizationId}/message-recipients

Resolved "Message Everyone" recipient list, including phone (Admin or org editor)

The resolved "Message Everyone" recipient list for an organization, including phone numbers: every guardian of every student on the roster (email and phone masked by each guardian's privacy settings) plus every parent member, deduplicated by email. Requires an Admin or an Editor of the organization.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json array of MessageRecipient

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

GET/organizations/{organizationId}/results

List an organization's results

Newest first, then sort_order. 404 when the organization's results feature is off - the same answer whether or not rows exist.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    resultsarray of OrganizationResultYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/results

Add a result (Admin or org editor)

At least one of title/competitor is required. calendar_event_id is optional and must be one of THIS organization's events. No link to any student.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: OrganizationResultInput

Responses

  • 201 application/json object

    Created

    FieldTypeRequiredDescription
    idstring (uuid)Yes
  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

PATCH/organizations/{organizationId}/results/{resultId}

Edit a result (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
resultIdpathstring (uuid)Yes

Request body

application/json, required. Type: OrganizationResultInput

Responses

  • 200 application/json OrganizationResult

    The updated result

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/organizations/{organizationId}/results/{resultId}

Delete a result (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
resultIdpathstring (uuid)Yes

Responses

  • 204

    Deleted

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/students

Add a student to the roster

Manual add only - no CSV bulk import. student_id is validated against the organization's own school.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
student_idstring (uuid)Yes

Responses

  • 201 application/json OrganizationRosterStudent

    Created

  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/organizations/{organizationId}/students/{studentId}

Remove a student from the roster (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
studentIdpathstring (uuid)Yes

Responses

  • 204

    No Content

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/photos/{slot}/upload-url

Get a presigned direct-to-B2 upload URL for one photo slot (Admin or org editor)

Same pre-signed-PUT-URL pattern as the student-photo routes. slot is 1, 2, or 3.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
slotpathstring, one of "1", "2", "3"Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
content_typestring, one of "image/jpeg", "image/png", "image/webp", "image/gif"Yes
content_lengthintegerYes

Bytes, max ~2.2 MB.

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    upload_urlstringYes
    photo_keystringYes
    expires_inintegerYes
  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

  • 413 application/json Error

    Declared content_length exceeds the server max.

GET/organizations/{organizationId}/photos/{slot}/download-url

Get a short-lived signed download URL for one photo slot

Open to any school member who can see the organization.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
slotpathstring, one of "1", "2", "3"Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    download_urlstringYes
    expires_inintegerYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/organizations/{organizationId}/photos/{slot}

Confirm a photo upload (Admin or org editor)

Called after the client PUTs bytes to the upload-url above, to store the resulting key.

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
slotpathstring, one of "1", "2", "3"Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
photo_keystringYes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    photo_keystringYes
  • 400 application/json Error

    Validation failed: a field is missing or invalid.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

DELETE/organizations/{organizationId}/photos/{slot}

Remove a photo (Admin or org editor)

Parameters

NameInTypeRequiredDescription
organizationIdpathstring (uuid)Yes
slotpathstring, one of "1", "2", "3"Yes

Responses

  • 204

    No Content

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

Notifications

Resolve a tapped link to the right school and mark a notification read.

POST/notifications/{notificationId}/read

Mark one of the caller's own notifications read

Marks one of the caller's own notifications read. One-directional (it is never marked unread again) and idempotent.

Parameters

NameInTypeRequiredDescription
notificationIdpathstring (uuid)Yes

Responses

  • 204

    Marked read

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

Polls

Polls visible to the user, with results where the poll allows them, and answering.

GET/schools/{schoolId}/polls

List polls visible to the caller

Every poll currently visible to the caller: audience targeting and the rule that a poll is hidden until it opens are already applied. closed and already_responded are computed per poll so a client can badge it without its own schedule logic. 404s if the school has the polls feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    pollsarray of PollYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The polls feature is toggled off for this school.

GET/polls/{pollId}

Get one poll's full detail, including questions/choices and (when visible) results

One poll's full detail, including questions and choices. The route has no school in its path; the poll's school is resolved for you. A poll that does not exist and one the caller cannot see both answer 404. results holds the poll's current results whenever its results_visibility mode allows them right now, and is null otherwise (for example never, or after_close before the poll has closed); do not compute results client-side.

Parameters

NameInTypeRequiredDescription
pollIdpathstring (uuid)Yes

Responses

  • 200 application/json PollDetail

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

POST/polls/{pollId}/respond

Submit answers to a poll

Submits the caller's answers. Every question in the poll must be answered exactly once in the same request (no partial submission). One immutable response is kept per poll and caller: a duplicate submission is rejected, not overwritten. A caller who is not in the poll's audience gets not_authorized, unlike the GET routes, which answer 404 for a poll the caller cannot see.

Parameters

NameInTypeRequiredDescription
pollIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
answersarray of PollAnswerInputYes

Responses

  • 200 application/json object

    Response recorded

    FieldTypeRequiredDescription
    submitted_atstring (date-time)Yes
  • 400 application/json Error

    The poll has closed, the answer set fails validation (missing/duplicate/malformed answers, or an answer referencing a question/choice outside this poll), or the caller already responded (including a concurrent double-submit race).

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not a member of this poll's audience.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

Sign-Ups

Volunteer and donation sign-ups visible to the user, and claiming, editing or withdrawing a response.

GET/schools/{schoolId}/signups

List sign-ups visible to the caller

Every sign-up currently visible to the caller: audience or organization-membership targeting, the rule that a sign-up is hidden until it opens, and the visible_until cutoff are already applied. closed and has_claims are computed per sign-up so a client can badge it without its own schedule logic. This is a summary view: no items and no claimant names (see GET /signups/{signupId}). 404s if the school has the signups feature toggle turned off.

Parameters

NameInTypeRequiredDescription
schoolIdpathstring (uuid)Yes

Responses

  • 200 application/json object

    OK

    FieldTypeRequiredDescription
    signupsarray of SignupYes
  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not authorized for this school/resource/role

  • 404 application/json Error

    The signups feature is toggled off for this school.

GET/signups/{signupId}

Get one sign-up's full detail, including every item's claimant list and remaining capacity

One sign-up's full detail. The route has no school in its path; the sign-up's school is resolved for you. A sign-up that does not exist and one the caller cannot see both answer 404. Items are split into time_slot_items (sorted chronologically) and other_items (in the order the organizer chose). Each item's claims array lists every current claimant's display name and note, remaining is its live capacity, and my_claim is the caller's own claim on that item, or null. Reading a sign-up marks it as opened by the caller, which clears it from their sidebar sign-up count.

Parameters

NameInTypeRequiredDescription
signupIdpathstring (uuid)Yes
prefetchquerystring, one of "1"No

Pass 1 for a background cache-warming fetch that is not the user opening the sign-up - skips recording the view, so it does not clear the sidebar badge.

Responses

  • 200 application/json SignupDetail

    OK

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

PATCH/signups/{signupId}

Edit a sign-up

A full re-submit. Authorization is decided by the sign-up (an admin, or an Organization Editor on one of its organizations). Returns the saved sign-up with its items split into time-slot items (sorted by start) and other items (sorted by position).

Parameters

NameInTypeRequiredDescription
signupIdpathstring (uuid)Yes

Request body

application/json, required. Type: SaveSignupBody

Responses

  • 200 application/json object

    The saved sign-up.

    FieldTypeRequiredDescription
    idstring (uuid)No
    titlestringNo
    descriptionstringNo

    Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

    description_deltaobject, nullableNo
    audience_idstring (uuid), nullableNo
    organization_idsarray of string (uuid)No
    opens_atstring (date-time), nullableNo
    closes_atstring (date-time), nullableNo
    visible_untilstring (date-time)No
    updated_atstring (date-time)No
    closedbooleanNo
    time_slot_itemsarray of SignupItemBodyNo
    other_itemsarray of SignupItemBodyNo
  • 400 application/json Error

    Validation failed

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

DELETE/signups/{signupId}

Delete a sign-up

A hard delete; its items, responses and organization links go with it. If the caller is refused the sign-up survives and the response is 403.

Parameters

NameInTypeRequiredDescription
signupIdpathstring (uuid)Yes

Responses

  • 204

    Deleted.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Not allowed

  • 404 application/json Error

    Not found

POST/signups/{signupId}/respond

Claim, edit, or withdraw a response to one of this sign-up's items

Claims, edits or withdraws the caller's response on one item of a sign-up, subject to the sign-up being open and the caller being in its audience (a caller outside the audience gets not_authorized, unlike the GET routes, which answer 404). action: "edit" with just item_id (and an optional note) updates the note on the caller's existing claim in place; adding to_item_id moves the claim to a different item of the same sign-up (the new claim is created before the old one is removed, so a full target item leaves the original claim intact). A to_item_id equal to item_id means no move. The response is intentionally minimal: item_id, claimed, note and remaining for the item the claim now lives on. Call GET /signups/{signupId} again for the full claimant list.

Parameters

NameInTypeRequiredDescription
signupIdpathstring (uuid)Yes

Request body

application/json, required. Type: object

FieldTypeRequiredDescription
actionstring, one of "claim", "edit", "withdraw"Yes
item_idstring (uuid)Yes

The item being claimed/edited/withdrawn - or, for edit with to_item_id set, the item being moved FROM.

to_item_idstring (uuid)No

edit only. If present and different from item_id, moves the claim to this item instead of just updating its note.

notestring, nullableNo

Optional free-text note. Ignored for withdraw. Trimmed; an empty/whitespace-only string is stored as null.

Responses

  • 200 application/json object

    Claimed, edited, or withdrawn successfully.

    FieldTypeRequiredDescription
    item_idstring (uuid)Yes

    The item the caller's claim now lives on (the target item, for a change) - or the item just withdrawn from, for withdraw.

    claimedbooleanYes

    False only after a successful withdraw.

    notestring, nullableYes
    remaininginteger, nullableYes

    item_id's current remaining capacity, recomputed after this write. Null for an uncapped item.

  • 400 application/json Error

    Missing/invalid action/item_id, the sign-up has closed, to_item_id doesn't belong to the same sign-up as item_id, the target item is full, the caller already claimed the target item (a concurrent double-claim race), or the caller had no existing claim to edit/withdraw.

  • 401 application/json Error

    The access token is missing, expired or invalid (not_authenticated). Also returned with the code mfa_required when the user has enrolled a second factor but the token has not completed it: show a two-factor code prompt, verify, and retry with the upgraded token.

  • 403 application/json Error

    Authenticated, but not a member of this sign-up's resolved audience.

  • 404 application/json Error

    Not found, OR exists but the caller has no access to it — this API deliberately does not distinguish the two (returning 403 instead of 404 for an existing-but-forbidden resource would confirm the resource's existence to a caller who shouldn't know that).

Schemas

Objects named in the request and response tables above.

CalendarSeriesRule

FieldTypeRequiredDescription
frequencystring, one of "daily", "weekly", "monthly"Yes
intervalintegerNo
weekdaysarray of integer, nullableNo

weekly only; defaults to the start date's weekday

monthly_modestring, one of "day_of_month", "nth_weekday", nullableNo
day_of_monthinteger, nullableNo
nthinteger, nullableNo

1 to 4, or -1 for the last

weekdayinteger, nullableNo
starts_onstring (date)Yes
ends_onstring (date), nullableNo
countinteger, nullableNo

CalendarSeriesTemplate

FieldTypeRequiredDescription
titlestringNo
descriptionstringNo
categorystring, one of "general", "performance", "closure", "athletic", "other"No
tagsstringNo

Comma-separated

start_datestring (date)No
end_datestring (date)No
start_timestringNo

HH:MM or empty for all day

end_timestringNo
locationstringNo

CalendarSeriesCreateBody

FieldTypeRequiredDescription
templateCalendarSeriesTemplateYes
ruleCalendarSeriesRuleYes
skip_closuresbooleanNo
event_idstring (uuid), nullableNo

ClassSchedulePatternBody

FieldTypeRequiredDescription
titlestringNo

Defaults to the class name

weekdaysarray of integerYes

Sunday is 0

intervalintegerNo

Every N weeks (default 1)

start_timestring, nullableNo
end_timestring, nullableNo
locationstring, nullableNo

Defaults to the class location

starts_onstring (date)Yes
ends_onstring (date)Yes
skip_closuresbooleanNo

ClassSchedulePattern

FieldTypeRequiredDescription
idstring (uuid)No

The series id; use it with PATCH/DELETE

summarystringNo
weekdaysarray of integerNo
intervalintegerNo
start_timestringNo
end_timestringNo
locationstringNo
titlestringNo
starts_onstring (date)No
ends_onstring (date)No
skip_closuresbooleanNo
instance_countintegerNo
edited_countintegerNo

EndOfTermPlan

FieldTypeRequiredDescription
outcomestring, one of "archive", "reset_and_continue"Yes
rosterstring, one of "keep", "clear"Yes
schedule_eventsstring, one of "keep", "delete"Yes
noticesstring, one of "keep", "archive"Yes
polls_signupsstring, one of "keep", "archive"Yes
documentsstring, one of "keep", "archive", "delete"Yes

Default keep

staffstring, one of "keep", "clear"Yes

Editor grants are never cleared

EndOfTermCounts

FieldTypeRequiredDescription
roster_clearedintegerNo
schedule_events_deletedintegerNo
schedule_seriesintegerNo
notices_archivedintegerNo
polls_archivedintegerNo
signups_archivedintegerNo
documents_changedintegerNo
staff_clearedintegerNo

EndOfTermResult

FieldTypeRequiredDescription
already_ranbooleanNo
dry_runbooleanNo
outcomestring, one of "archive", "reset_and_continue"No
countsEndOfTermCountsNo
next_starts_onstring (date), nullableNo
next_ends_onstring (date), nullableNo
lifecycle_afterstring, one of "active", "awaiting_dates", "archived"No
awaiting_datesbooleanNo

EndOfTermRun

FieldTypeRequiredDescription
idstring (uuid)No
ran_atstring (date-time)No
triggerstring, one of "scheduled", "manual"No
outcomestring, one of "archive", "reset_and_continue"No
term_starts_onstring (date), nullableNo
term_ends_onstring (date), nullableNo
next_starts_onstring (date), nullableNo
next_ends_onstring (date), nullableNo
lifecycle_afterstringNo
countsEndOfTermCountsNo

CalendarSeriesPreview

FieldTypeRequiredDescription
datesarray of string (date)Yes
countintegerYes
skipped_for_closuresarray of string (date)Yes
would_exceed_capbooleanYes
capintegerYes

CalendarSeries

FieldTypeRequiredDescription
idstring (uuid)Yes
organization_idstring (uuid), nullableNo
ruleCalendarSeriesRuleYes
templateCalendarSeriesTemplateYes
skip_closuresbooleanYes
sourcestring, one of "manual", "class_schedule"Yes
summarystringYes
instance_countintegerYes
edited_countintegerYes
first_datestring (date), nullableNo

Earliest start among the instances (null when none are left)

last_datestring (date), nullableNo

Latest end among the instances

anchor_event_idstring (uuid), nullableNo

The earliest instance; any instance opens the edit-all flow

flaggedarray of objectYes

NoticeInput

Body for creating or editing a notice. Attachments are not accepted through this route (a create starts with none; an edit keeps the existing ones).

FieldTypeRequiredDescription
titlestringNo
body_deltaobjectNo

Rich-text Delta document (present when saved through the rich-text editor). Omitted on edit leaves it untouched.

audience_idstring (uuid), nullableNo
organization_idstring (uuid)No

Publish for this organization. An Organization Editor's only path; the server sets the audience.

expires_atstring (date-time)No

Required on create. Omitted on edit keeps the current value.

category_idstring (uuid), nullableNo

Admins only. Omitted means uncategorized on create and unchanged on edit; null means uncategorized.

flagsarray of string, one of "urgent"No

Admins only. Omitted on edit leaves them as they are and an empty array clears them. Values outside the allowed set are rejected.

SignupItemBody

FieldTypeRequiredDescription
idstring (uuid), nullableNo

Existing item id when editing; null for a new item.

descriptionstringNo
quantityinteger, nullableNo
starts_atstring (date-time), nullableNo

Set for a time-slot item.

ends_atstring (date-time), nullableNo

SaveSignupBody

FieldTypeRequiredDescription
titlestringNo
description_deltaobjectNo

Rich-text Delta document for the description.

audience_idstring (uuid), nullableNo
organization_idsarray of string (uuid)No

Accepted on edit only. On create the sign-up is never organization-scoped.

opens_atstring (date-time), nullableNo
closes_atstring (date-time), nullableNo
visible_untilstring (date-time)No

Required.

itemsarray of SignupItemBodyNo

DocumentInput

Body for creating or editing a Document Library entry. An edit is a full re-submit; the synthetic school-demographics entry keeps its existing behaviour server side.

FieldTypeRequiredDescription
titlestringNo
body_deltaobjectNo

Rich-text Delta document, present when saved through the rich-text editor.

audience_idstring (uuid), nullableNo

Null = every school member.

organization_idstring (uuid)No

Publish for this organization, class or department. This is an Organization Editor's only path (the server sets the audience); the caller must hold a live grant.

attachmentsarray of DocumentAttachmentNo
category_idstring (uuid), nullableNo

Omitted means uncategorized on create and unchanged on edit; null means uncategorized.

CalendarEventInput

Event fields shared by the calendar-event create routes and the edit route. Every field is optional in the body but title and start_date are required to pass validation.

FieldTypeRequiredDescription
titlestringNo
descriptionstringNo
categorystringNo

Defaults to general.

tagsstringNo

Comma-separated.

start_datestring (date)No
end_datestring (date)No
start_timestringNo

HH:MM, 24-hour, in the school's time zone.

end_timestringNo

HH:MM, 24-hour, in the school's time zone.

locationstring, nullableNo

Optional venue. On create, empty or null means none. On edit, omitted leaves it unchanged and empty or null clears it.

Error

Every error response uses this shape.

FieldTypeRequiredDescription
errorobjectYes

MeResponse

FieldTypeRequiredDescription
user_idstring (uuid)Yes
emailstring (email)Yes
is_fully_deactivatedbooleanYes

True if every membership is currently in its post-deletion grace-period window.

address_visibleboolean, nullableNo

The caller's own home-address visibility to other parents, aggregated over their linked students (true only when all are visible). null when the caller has no linked students — nothing to hide — so native clients hide the home-address toggle.

membershipsarray of MembershipYes

Membership

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
school_namestringYes
school_logo_urlstring, nullableNo
school_latitudenumber (double), nullableNo

The school's geocoded latitude (schools.latitude), or null if the school hasn't been geocoded yet.

school_longitudenumber (double), nullableNo

The school's geocoded longitude (schools.longitude), or null if the school hasn't been geocoded yet.

school_statusstring, one of "active", "archived", "suspended"No

The school's lifecycle status. Anything other than active means the school is unavailable: every other endpoint returns no data for it, so clients show a "School unavailable" screen instead of the directory. Always present; defaults to active if the school row is missing.

school_themeThemeNo

The school's resolved theme, or null for the standard look. Already checked against the same format and accessibility rules the web applies.

rolestring, one of "senior_admin", "junior_admin", "parent"Yes
profile_visibilitystring, one of "full", "name_only"Yes

full = contact visible, name_only = contact hidden. Set via PATCH /me/profile-visibility.

deactivated_atstring (date-time), nullableNo

RegistrationConfirmResult

FieldTypeRequiredDescription
schoolsarray of objectYes
studentsarray of objectYes

Guardian

FieldTypeRequiredDescription
first_namestringNo
last_namestringNo
relationstring, nullableNo

RosterStudent

FieldTypeRequiredDescription
idstring (uuid)Yes
first_namestringYes
last_namestringYes
graduation_yearinteger, nullableNo
streetstring, nullableNo
citystring, nullableNo
statestring, nullableNo
zipstring, nullableNo
photo_keystring, nullableNo
is_starredbooleanYes
is_linkedbooleanYes

Whether the calling user is a linked guardian of this student.

guardiansarray of GuardianNo

RosterStudentFull

Same base fields as RosterStudent plus latitude/longitude, the caller's own note, and full guardian contact.

FieldTypeRequiredDescription
idstring (uuid)Yes
first_namestringYes
last_namestringYes
graduation_yearinteger, nullableNo
streetstring, nullableNo
citystring, nullableNo
statestring, nullableNo
zipstring, nullableNo
latitudenumber (double), nullableNo
longitudenumber (double), nullableNo
photo_keystring, nullableNo
is_starredbooleanYes
is_linkedbooleanYes

Whether the calling user is a linked guardian of this student.

noteNoteYes
guardiansarray of ParentContactYes

Note

At most one per (student, author) - no id, since GET/PUT are addressed by student, not by note identity.

FieldTypeRequiredDescription
bodystringYes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes

ParentContact

first_name/last_name/relation are never masked.

FieldTypeRequiredDescription
idstring (uuid)No
first_namestring, nullableNo
last_namestring, nullableNo
relationstring, nullableNo
emailstring (email), nullableNo
phonestring, nullableNo

StudentDetail

FieldTypeRequiredDescription
idstring (uuid)Yes
first_namestringYes
last_namestringYes
graduation_yearinteger, nullableNo
streetstring, nullableNo
citystring, nullableNo
statestring, nullableNo
zipstring, nullableNo
latitudenumber (double), nullableNo
longitudenumber (double), nullableNo
photo_keystring, nullableNo
is_starredbooleanYes
is_linkedbooleanYes
noteNoteYes
parentsarray of ParentContactYes

MapMarker

FieldTypeRequiredDescription
idstring (uuid)Yes
first_namestringYes
last_namestringYes
graduation_yearinteger, nullableNo
latitudenumber (double)Yes
longitudenumber (double)Yes

StudentList

FieldTypeRequiredDescription
idstring (uuid)Yes
namestringYes
descriptionstring, nullableNo
created_atstring (date-time)Yes

StudentListDetail

FieldTypeRequiredDescription
idstring (uuid)Yes
namestringYes
descriptionstring, nullableNo
created_atstring (date-time)Yes
studentsarray of objectYes

Theme

A school's or organization's resolved theme - the server derives it from 1-3 seed colors and has already enforced WCAG AA, so clients only apply it (no color math client-side). null where it appears means the standard look. Colors are #rrggbb; each token exists for both light and dark mode.

FieldTypeRequiredDescription
versionintegerYes
seedsarray of stringYes
tokensobjectYes

ThemeTokens

FieldTypeRequiredDescription
accentstringYes

Fill for primary buttons, the header and pills.

accentHoverstringYes
onAccentstringYes

Label color on an accent fill (black or white, always at least 4.5:1).

accentTextstringYes

The accent as text/links/tint on page surfaces.

accentMutedstringYes

Subtle accent-tinted background.

backgroundstringYes
surfacestringYes
borderstringYes
headerBackgroundstringYes
headerTextstringYes
highlightstringNo

Present only when the theme has a third seed.

highlightTextstringNo
onHighlightstringNo

MyContact

FieldTypeRequiredDescription
first_namestring, nullableNo
last_namestring, nullableNo
phonestring, nullableNo

MenuImportItem

FieldTypeRequiredDescription
namestringYes
descriptionstring, nullableNo

Free text. Nudged (in the web admin form/CSV template, not enforced) toward a visually descriptive sentence — this is what gets fed to on-device ImagePlayground image generation on iOS 18+/Apple Intelligence devices, concatenated with name.

categorystring, nullableNo
allergensstring, nullableNo

MenuEntry

FieldTypeRequiredDescription
idstring (uuid)Yes
datestring (date)Yes
meal_typestring, one of "breakfast", "am_snack", "lunch", "pm_snack", "other"Yes
itemsarray of MenuImportItemYes
notesstring, nullableNo

"vendor substitution due to weather closure".

sourcestring, nullableNo

Free text, not a DB enum — e.g.

recurrence_group_idstring (uuid), nullableNo
needs_reviewbooleanYes

True when a scraper found conflicting content awaiting an admin's keep/use decision — see pending_* fields.

pending_sourcestring, nullableNo
pending_itemsarray of MenuImportItem, nullableNo
pending_raw_textstring, nullableNo
image_urlstring (uri), nullableNo

Only ever populated for lunch rows; null until the first successful generation via POST.../menus/{menuId}/image. Deterministic per school+date — never changes across a regeneration.

image_generated_atstring (date-time), nullableNo

Updates on every (re)generation - the change-marker a client compares against its own cached copy, since image_url itself doesn't change.

CalendarEvent

FieldTypeRequiredDescription
idstring (uuid)Yes
titlestringYes
descriptionstringYes
categorystring, one of "general", "performance", "closure", "athletic", "other"Yes
tagsarray of stringYes

Free-form admin-entered tags (e.g. team/production names) for finer-grained filtering than category alone.

start_datestring (date)Yes
end_datestring (date)Yes
start_timestring, nullableNo

HH:MM:SS. Null = no specific time / all day (e.g. a closure spanning start_date..end_date).

end_timestring, nullableNo
locationstring, nullableNo

Optional venue shown under the event title. Additive - older clients ignore it. Accepted (as location, '' or null clears) on the calendar-event create/edit routes; omitted on an edit leaves it unchanged.

StaffMember

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
namestringYes
titlestring, nullableYes
emailstring (email), nullableYes
phonestring, nullableYes
locationstring, nullableYes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes

DocumentAttachment

FieldTypeRequiredDescription
idstring (uuid)Yes
filenamestringYes
content_typestringYes
sizeintegerYes

Bytes.

keystringYes

Backblaze B2 private-bucket object key - not a downloadable URL by itself.

Document

id and created_by are plain strings, not always a UUID: the synthetic "School Demographic Details" entry uses the sentinel id school-demographics and created_by: "system" rather than a real document's UUID/author id.

FieldTypeRequiredDescription
idstringYes

A document UUID, or the school-demographics sentinel.

school_idstring (uuid)Yes
titlestringYes
bodystringYes

Deprecated plain-text rendering of body_delta, kept only for app builds 4 and earlier. Read body_delta; this field will be removed.

body_deltaobjectNo

Quill Delta document: the rich-text body.

audience_idstring (uuid), nullableYes

Null = visible to every school member. Non-null audiences are already resolved server-side - every Document in this response is already visible to the caller.

attachmentsarray of DocumentAttachmentYes
created_bystringYes

A user UUID, or "system" for the auto-populated demographics entry.

created_atstring (date-time)Yes
updated_atstring (date-time)Yes
categorystring, nullableNo

The category's NAME (null = uncategorized). Additive.

category_idstring (uuid), nullableNo

A Category id; send category_id on create/edit (omitted on edit = unchanged, null = uncategorized).

category_colorstring, nullableNo

A palette key; null = derive a stable color from the name.

Category

A per-school content category shared by Documents and Notices. Any member reads; only a Senior Admin writes.

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
namestringYes

Unique per school, case-insensitive.

colorstring, one of "blue", "purple", "orange", "green", "pink", "teal", "indigo", "red", "mint", "cyan", "brown", "yellow", nullableYes

Palette key.

sort_orderintegerYes
created_atstring (date-time)Yes

Notice

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
bodystringYes

Deprecated plain-text rendering of body_delta, kept only for app builds 4 and earlier. Read body_delta; this field will be removed.

body_deltaobjectNo

Quill Delta document: the rich-text body.

audience_idstring (uuid), nullableYes

Null = visible to every school member. Non-null audiences are already resolved server-side - every Notice in this response is already visible to the caller.

expires_atstring (date-time)Yes

Already-expired notices are excluded from this response entirely, not just flagged.

created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
categorystring, nullableNo

The category's NAME (null = uncategorized). Additive.

category_idstring (uuid), nullableNo

A Category id; admins send category_id on create/edit (omitted on edit = unchanged).

category_colorstring, nullableNo

A palette key; null = derive from the name.

flagsarray of string, one of "urgent"No

Flexible notice flags, urgent first. Admins send flags on create/edit (omitted on edit = unchanged, [] clears; unknown flag = 400).

audience_labelobject or objectNo

Who the notice is for, as the apps show it. organization = the notice is addressed to an organization's system audience (render that organization's pill, linking to it); custom = a saved audience (show its name); null = school-wide.

PermissionSlip

students is only the caller's own guardian-linked students eligible for this slip (currently audience-matched, or already answered) - never every student targeted by the slip.

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
bodystringYes

Deprecated plain-text rendering of body_delta, kept only for app builds 4 and earlier. Read body_delta; this field will be removed.

body_deltaobjectNo

Quill Delta document: the rich-text body.

audience_idstring (uuid), nullableYes

Null = targeted at every active student at the school.

required_bystring (date)Yes

Informational due date - a plain calendar date, not a hide-after instant. Does not affect visibility; an outstanding or completed slip stays in the list past this date.

created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
studentsarray of objectYes

Poll

The list-route shape - also the base every field of PollDetail below extends.

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

audience_idstring (uuid), nullableYes

Null = visible to every school member. Non-null audiences are already resolved server-side - every Poll in this response is already visible to the caller.

results_visibilitystring, one of "live", "after_close", "never"Yes

The admin's own choice of when the audience can see results - "live" while voting is open, "after_close" only once closed (until results_visible_until), or "never".

results_visible_untilstring (date-time), nullableYes

Only meaningful when results_visibility is "after_close" - null for the other two modes.

opens_atstring (date-time)Yes

A poll isn't included in this list at all before it goes live - see the polls list route's own description.

closes_atstring (date-time)Yes
created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
closedbooleanYes

True once closes_at has passed.

already_respondedbooleanYes

True if the caller has already submitted a response to this poll (one immutable response per caller).

PollChoice

An admin-authored answer choice for a multiple_choice or multi_select PollQuestion. Never present for fill_in_blank questions.

FieldTypeRequiredDescription
idstring (uuid)Yes
labelstringYes
positionintegerYes

PollQuestion

FieldTypeRequiredDescription
idstring (uuid)Yes
question_typestring, one of "multiple_choice", "multi_select", "fill_in_blank"Yes
promptstringYes
positionintegerYes
choicesarray of PollChoiceYes

Populated for multiple_choice/multi_select questions, in position order; always empty for fill_in_blank.

min_selectionsinteger, nullableNo

multi_select only - null means 1 (at least one choice required). Always null for other question types.

max_selectionsinteger, nullableNo

multi_select only - null means no cap beyond however many choices exist. Always null for other question types.

PollQuestionResults

One question's aggregated results - never includes respondent identity, only aggregate counts/text.

FieldTypeRequiredDescription
idstring (uuid)Yes
question_typestring, one of "multiple_choice", "multi_select", "fill_in_blank"Yes
promptstringYes
choice_talliesarray of objectYes

multiple_choice/multi_select only - one entry per admin-authored choice, in choice position order, count 0 if nobody picked it. Empty for fill_in_blank.

text_answersarray of stringYes

fill_in_blank only - free-text answers in submission order. Empty for multiple_choice/multi_select.

total_responsesintegerYes

PollDetail

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

audience_idstring (uuid), nullableYes

Null = visible to every school member. Non-null audiences are already resolved server-side - every Poll in this response is already visible to the caller.

results_visibilitystring, one of "live", "after_close", "never"Yes

The admin's own choice of when the audience can see results - "live" while voting is open, "after_close" only once closed (until results_visible_until), or "never".

results_visible_untilstring (date-time), nullableYes

Only meaningful when results_visibility is "after_close" - null for the other two modes.

opens_atstring (date-time)Yes

A poll isn't included in this list at all before it goes live - see the polls list route's own description.

closes_atstring (date-time)Yes
created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
closedbooleanYes

True once closes_at has passed.

already_respondedbooleanYes

True if the caller has already submitted a response to this poll (one immutable response per caller).

questionsarray of PollQuestionYes
resultsarray of PollQuestionResults, nullableYes

The poll's current results, or null when the poll's own results_visibility mode doesn't allow them to this caller right now - see the /polls/{pollId} route's own description. Never reimplement this gate client-side.

PollAnswerInput

Exactly one of choice_id/answer_text/choice_ids is populated, matching the question's own type - choice_id for multiple_choice, answer_text for fill_in_blank, choice_ids (a non-empty array, size within the question's min_selections/max_selections) for multi_select. Every question in the poll must appear exactly once in a respond request's answers array.

FieldTypeRequiredDescription
question_idstring (uuid)Yes
choice_idstring (uuid), nullableYes
answer_textstring, nullableYes
choice_idsarray of string (uuid)No

multi_select only - the set of chosen choice IDs. Omitted/null for other question types.

Signup

The list-route shape - also the base every field of SignupDetail below extends. The web parent UI itself never surfaces this field either.

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

opens_atstring (date-time)Yes

A sign-up isn't included in this list at all before it goes live - see the signups list route's own description.

closes_atstring (date-time)Yes

No further claims, edits, or withdrawals are accepted once this passes.

visible_untilstring (date-time)Yes

The sign-up is no longer shown to its audience at all once this passes, independent of closes_at.

created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
closedbooleanYes

True once closes_at has passed.

has_claimsbooleanYes

True if the caller currently holds at least one claim anywhere on this sign-up.

SignupItemClaim

One current claimant on a SignupItem. Never includes the claimant's user id.

FieldTypeRequiredDescription
display_namestringYes
notestring, nullableYes

SignupItem

One claimable item (or time slot) on a sign-up.

FieldTypeRequiredDescription
idstring (uuid)Yes
descriptionstringYes
quantityinteger, nullableYes

The claim cap for this item, or null for uncapped.

starts_atstring (date-time), nullableYes

Set together with ends_at for a time-slot item; both null for a plain (non-timed) item.

ends_atstring (date-time), nullableYes
remaininginteger, nullableYes

Live remaining capacity (quantity minus current claim count), recomputed fresh on every read. Null for an uncapped item, never negative.

claimsarray of SignupItemClaimYes

Every current claimant on this item, in claim order.

my_claimobject, nullableYes

The caller's own claim on this item (their own note only - their own name isn't repeated back to them), or null if they haven't claimed it.

SignupDetail

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
titlestringYes
descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

opens_atstring (date-time)Yes

A sign-up isn't included in this list at all before it goes live - see the signups list route's own description.

closes_atstring (date-time)Yes

No further claims, edits, or withdrawals are accepted once this passes.

visible_untilstring (date-time)Yes

The sign-up is no longer shown to its audience at all once this passes, independent of closes_at.

created_bystring (uuid)Yes
created_atstring (date-time)Yes
updated_atstring (date-time)Yes
closedbooleanYes

True once closes_at has passed.

has_claimsbooleanYes

True if the caller currently holds at least one claim anywhere on this sign-up.

time_slot_itemsarray of SignupItemYes

Items with a starts_at/ends_at set, sorted chronologically.

other_itemsarray of SignupItemYes

Items with no starts_at/ends_at, in admin-chosen order.

FeatureToggles

One boolean per known FeatureKey — a key with no explicit false entry is always present here as true (missing from the school's stored config means enabled, not omitted from this map).

FieldTypeRequiredDescription
lunch_menusbooleanYes
document_librarybooleanYes
directory_mapbooleanYes
noticesbooleanYes
staff_directorybooleanYes
calendarbooleanYes
pollsbooleanYes
permission_slipsbooleanYes
signupsbooleanYes

PhotoUploadUrl

FieldTypeRequiredDescription
upload_urlstring (uri)Yes

Pre-signed B2 PUT URL. PUT the raw image bytes here directly — do not route them through this API.

photo_keystringYes

Deterministic key (student-photos/{studentId}.{ext}) — pass back unchanged to the confirm endpoint.

expires_inintegerYes

Seconds until upload_url expires.

PhotoDownloadUrl

FieldTypeRequiredDescription
download_urlstring (uri)Yes
expires_inintegerYes

Seconds until download_url expires. Currently 300 (5 minutes), matching the existing web signed-URL TTL.

WatchPayload

FieldTypeRequiredDescription
school_idstring (uuid)Yes
generated_atstring (date-time)Yes
studentsarray of objectYes

OrganizationSummary

The list-route shape - core fields only, no leaders/roster/events/photos.

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
categorystring, one of "athletics", "performing_arts", "clubs", "other"Yes
namestringYes
short_namestringYes

Unique per school, not globally.

descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

archived_atstring (date-time), nullableYes
created_atstring (date-time)No
updated_atstring (date-time)No
themeobject, nullableNo

List route only. The organization's own resolved theme, or null to inherit the school's.

can_editbooleanNo

List route only. Whether the caller may edit this organization (admin, or a live editor grant); drives Student Detail's organization pill controls.

Organization

FieldTypeRequiredDescription
idstring (uuid)Yes
school_idstring (uuid)Yes
categorystring, one of "athletics", "performing_arts", "clubs", "other"Yes
namestringYes
short_namestringYes

Unique per school, not globally.

descriptionstringYes

Deprecated plain-text rendering of description_delta, kept only for app builds 4 and earlier. Read description_delta; this field will be removed.

description_deltaobjectNo

Quill Delta document: the rich-text description.

archived_atstring (date-time), nullableYes
created_atstring (date-time)No
updated_atstring (date-time)No
themeThemeNo

This organization's own resolved theme, or null to inherit the school's (see school_theme on /me).

can_editbooleanYes

Admin or a live org-editor grant - can edit description/photos/leadership/roster.

photo_1_keystring, nullableYes

B2 key, not a URL - fetch a signed URL via GET.../photos/{slot}/download-url.

photo_2_keystring, nullableYes
photo_3_keystring, nullableYes
leadersarray of OrganizationLeaderYes
rosterarray of OrganizationRosterStudentYes
eventsarray of CalendarEventYes

Empty if the calendar feature toggle is off.

featuresmap of booleanNo

Per-organization opt-in flags that APPLY to this organization's category (today results); a missing/false key means off. Set with PATCH /organizations/{id}/settings { features: { results: true } } (admin only).

noticesarray of objectYes

The notices that target this organization (its own audience, or a composite audience that includes it) that the caller is in the audience for, newest first. Empty if the notices feature toggle is off.

documentsarray of objectNo

The documents that target this organization (its own audience, or a composite audience that includes it) that the caller is in the audience for, plus - for an editor/admin - the organization's own. Empty if the document_library feature toggle is off.

permission_slipsarray of objectNo

The permission slips that target this organization that the caller (a guardian of an eligible student) can respond to, most urgent first, plus - for an editor/admin - the organization's own. Empty if the permission_slips feature toggle is off.

can_managebooleanYes

Admin only - archive/re-enable, category/short_name, editor grants.

OrganizationLeader

FieldTypeRequiredDescription
idstring (uuid)Yes
school_staff_idstring (uuid), nullableYes
namestringYes
titlestring, nullableYes

The org-specific role label (e.g. Resolved from display_title, falling back to the linked staff record's own title only when display_title is unset.

display_orderintegerYes

OrganizationResult

A team/organization-level result; never linked to a student.

FieldTypeRequiredDescription
idstring (uuid)Yes
organization_idstring (uuid)Yes
calendar_event_idstring (uuid), nullableYes

Optional link to one of this organization's events. Deleting the event keeps the result (it becomes standalone).

result_datestring (date)Yes
titlestringYes

e.g. '200m Freestyle', 'Board 1'.

competitorstringYes
outcomestring, one of "win", "loss", "tie", "no_contest", nullableYes
score_summarystringYes

Free text: '21-14', '1:04.32', '3rd of 12'.

notes_deltaobjectYes

Quill Delta (rich-text notes).

sort_orderintegerYes
created_atstring (date-time)No
updated_atstring (date-time)No

OrganizationResultInput

FieldTypeRequiredDescription
result_datestring (date)Yes
calendar_event_idstring (uuid), nullableNo
titlestringNo
competitorstringNo
outcomestring, one of "win", "loss", "tie", "no_contest", nullableNo
score_summarystringNo
notes_deltaobjectNo
sort_orderintegerNo

Optional; assigned (appended within the event/day) when omitted.

OrganizationRosterStudent

FieldTypeRequiredDescription
idstring (uuid)Yes
first_namestringYes
last_namestringYes
graduation_yearinteger, nullableYes
photo_keystring, nullableYes

OrganizationParentMember

A parent membership of an organization, independent of the student roster or leadership. added_by is null for a self-service join and the adding user's id for an Editor/Admin-added row.

FieldTypeRequiredDescription
idstring (uuid)Yes
membership_idstring (uuid)Yes
user_idstring (uuid), nullableYes
emailstring, nullableYes
added_bystring (uuid), nullableYes
created_atstring (date-time)Yes

OrganizationCandidate

A minimal membership row for the add-member/add-editor picker.

FieldTypeRequiredDescription
idstring (uuid)Yes
emailstring, nullableYes
rolestringYes

MessageRecipient

A resolved "Message Everyone" candidate for an organization or list - deduplicated by email, with phone included. A recipient with neither email nor phone is never returned.

FieldTypeRequiredDescription
idstring (uuid)Yes
namestringYes
emailstring, nullableYes
phonestring, nullableYes

ListGuardian

An individual parent/guardian added directly to a Custom List, not derived from a student being on the list.

FieldTypeRequiredDescription
idstring (uuid)Yes
guardian_membership_idstring (uuid)Yes
linked_student_idstring (uuid), nullableYes
user_idstring (uuid), nullableYes
emailstring, nullableYes
added_bystring (uuid)Yes
created_atstring (date-time)Yes