From e3a9ea5f0b42423998f8c307f7a115d3f8de9757 Mon Sep 17 00:00:00 2001 From: KTibow Date: Tue, 22 Sep 2026 13:15:38 -0700 Subject: [PATCH] Add per-method examples with fuller response payloads to JSON-API.md Reorganize the Examples section into one header per JSON-API method (in catalog order), each with its request and a fuller, anonymized in-session response: GetChildListData (district config + childrenList), StudentClassList (both loadAllTerms modes + todayScheduleInfoData), GetStudentClasesForGivenDay(-Response), GetStudentClassTime, Gradebook, GetStudentAttendanceList, GetStudentPastAttendanceData, GetStudentInfoData, GetCalendarData, GetCalendarAssignmentDetails, GetStudentDocuments, GetStudentDocumentContent, GetStudentHWNotes, GetPXPContentMessage, GetUserDefinedModule, GetSchoolInformationData, GetContentMyAccountData, GetAcknowledgementsData, GetHallPassSetup, GetHealthData, GetStudentDisciplineData/FeeData, GetSynergyMailUnreadCount, GetSynergyMailGetConversations, TestSystemCall. Corrects the child-list payload key (childrenList, not childInfos), documents extra error envelopes, and upgrades catalog entries verified live (o -> check). --- JSON-API.md | 528 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 459 insertions(+), 69 deletions(-) diff --git a/JSON-API.md b/JSON-API.md index 7dc7f32..6427b42 100644 --- a/JSON-API.md +++ b/JSON-API.md @@ -175,11 +175,31 @@ Every call returns **HTTP 200, even on failure**. Check the body's `error` field } ``` +**Example Response (module disabled district-wide):** +```json +{ + "error": {"code": "400", + "message": "2100 - School District has not enabled access for the Fees Module.", + "stackTrace": null}, + "data": null +} +``` + +**Example Response (server-side failure with a support ID):** +```json +{ + "error": {"code": "500", + "message": "Error: Please contact your school office for assistance. (ID: XXXXX)", + "stackTrace": null}, + "data": null +} +``` + **Notes:** - `code "2100"` — the feature is not enabled at this school; treat as empty data, not as a failure. -- `code "400"` — e.g. past attendance not available for this school. -- `code "500"` — generic server error. +- `code "400"` — e.g. past attendance not available for this school, or a module the district has disabled; the message often embeds a `2100 -` prefix even though the code is `400`. +- `code "500"` — generic server error. The `(ID: XXXXX)` suffix is a server-side event ID for the district's support desk; it varies per failure. Some `GetSynergyMail*` calls return this at districts even when `pxpMessagesData.supportingSynergyMail` is `true`. - Don't treat unseen codes as benign; log them. - This matters most for rarely-used areas (documents especially), where an empty result and an error look identical if you only check `data`. @@ -281,7 +301,7 @@ All of these are JSON-API calls: `POST /api/v1/mobile/PXPWebServices/`. | `GetStudentPastAttendanceData` | `childIntID` | `reportPastAtteendanceXML` *(sic)* | ✅ | | `GetStudentInfoData` | `childIntID` | `studentInfoXML`, `studentInfoDetailXML` | ✅ | | `GetCalendarData` | `childIntID`, optional date-window fields | `calendarListingData` | ✅ | -| `GetCalendarAssignmentDetails` | assignment GUID fields | `calendarAssignmentDetails` | ◦ | +| `GetCalendarAssignmentDetails` | calendar event fields (`AGU`, `DGU`, `dguInternal`, `dgU2`, `viewType`, `addLinkData`, `dayType`) | `calendarAssignmentDetails` | ✅ | | `GetStudentDocuments` | `childIntID`, `languageCode` | `studentDocuments` | ✅ | | `GetStudentDocumentContent` | `childIntID`, `documentGU` | `studentAttachedDocumentData` (base64 PDF inline) | ◦ | | `GetStudentHWNotes` | `childIntID`, `gu` | `gbhwNotesDatas` | ✅ | @@ -291,20 +311,20 @@ All of these are JSON-API calls: `POST /api/v1/mobile/PXPWebServices/`. | `GetUserDefinedModule` | `childIntID`, `moduleIndex` | `allModuleRecordData` | ✅ | | `GetFlexScheduleData` | `childIntID`, date fields | `studentFlexScheduleListingXML` | ◦ | | `UpdateFlexSchedule` | `removeStudentFromSection=false`, section fields | — | ◦ | -| `GetSchoolInformationData` | `childIntID` | `studentSchoolInfoListing` | ◦ | +| `GetSchoolInformationData` | `childIntID` | `studentSchoolInfoListing` | ✅ | | `GetSchoolPayUrl` | — | payment-portal URL | ◦ | | `GetSchoologoResponse` | *(none)* | `schoolAndDistrictLogo` (base64 images) | ✅ | | `GetSoundFile` / `SaveSoundFile` | sound id / data | `soundFileData` | ◦ | | `GenerateAuthToken` | *(session)* | `authToken` (SSO token for portal web-views) | ◦ | | `GetAckData` | `processActivation=false` | `acctResult`, `ackStatmentData`, `parentOrStudentData` | ◦ | -| `GetAcknowledgementsData` | `childIntID` | `parentAcknowledgementMain` | ◦ | +| `GetAcknowledgementsData` | `childIntID` | `parentAcknowledgementMain` | ✅ | | `GetAcknowledgementDetailsMobile` | acknowledgement id | `acknowledgement` | ◦ | | `UpdateAckFromMyAccount` / `UpdateParentAcknowledgement` | `ackUpdateListing` | — | ◦ | | `UpdateEmergencyResponse` | emergency contact answers | — | ◦ | | `UpdateStudentAbsence` | `absenceReportListings`, `absenceReportPastList`, `reportingOption` | `requestResult` | ◦ | | `UpdateAttachPhotoResponse` | `photoAttachDocumentData` | — | ◦ | | `UpdateMyAccountData` | `pxpMobileUpdateMyAccountData` | — | ◦ | -| `GetContentMyAccountData` | `childIntID` | `pxpMyAccountData` | ◦ | +| `GetContentMyAccountData` | `childIntID` | `pxpMyAccountData` | ✅ | | `GetStudentDisciplineData` | `childIntID` | `studentDisciplineListing` | ◦ | | `GetStudentFeeData` | `childIntID` | `studentFeeData` | ◦ | | `GetStudentSpecialEdData` | `childIntID` | `specialEdData` | ◦ | @@ -313,7 +333,7 @@ All of these are JSON-API calls: `POST /api/v1/mobile/PXPWebServices/`. | `GetStudentsVideoMeeting` / `GetParentsVideoMeeting` | meeting fields | `meetingsForUserResponse`, `videoCallResponseModel` | ◦ | | `GetHallPassData` | `getOnlyScheduedPases=false` *(sic)* | `studentHallPassXML` | ◦ | | `GetHallPassHistory` | `onlyOverTimeLimit=false` | `passesData`, `statsData`, `summaryData` | ◦ | -| `GetHallPassSetup` | `childIntID` | `hallPassRoomSetupXML` | ◦ | +| `GetHallPassSetup` | `childIntID` | `hallPassRoomSetupXML` | ✅ | | `UpdateHallPass` | pass action fields | `studentHallPassXML` | ◦ | | `GetHealthData` | `getDatahealthConditions=false`, `getDatahealthImmunizations=false`, `getDatahealthVisits=false` | `studentHealthData` | ◦ | | `GetCounselorVisitBasicData` | `getPositionInLine=false` | `counselorVisiNthInLine` | ◦ | @@ -328,13 +348,13 @@ All of these are JSON-API calls: `POST /api/v1/mobile/PXPWebServices/`. | `UploadGBDocumentDataForStudentAssigment` | `gbDocumentDataObj` | — | ◦ | | `UpdateDeviceToken` | push token, `reactNativeApp=false` | — (registers device for push) | ◦ | | `UpdateNotificationPrefs` | `mobileUserSettings`, `notificationListing` | — | ◦ | -| `TestSystemCall` | *(none)* | connectivity smoke test | ◦ | +| `TestSystemCall` | *(none)* | connectivity smoke test (`data` is a bool) | ✅ | | `GetSynergyMailGetConversations` | `pageToLoad=0` | `conversations`, `isLastPageLoaded`, `totalUnreadConversationMessages` | ◦ | | `GetSynergyMailMessage` | message id fields | `synergyMailDataXML` | ◦ | | `GetSynergyMailIGetMessageBody` | message id fields | `synergyMailMessageBodyXML` | ◦ | | `GetSynergyMailGetAttachment` | attachment id fields | `attachmentXML` | ◦ | | `GetSynergyMailInboxCount` | folder fields | `synergyMailInboxCountXML` | ◦ | -| `GetSynergyMailUnreadCount` | — | `messageCount` | ◦ | +| `GetSynergyMailUnreadCount` | — | `messageCount` | ✅ | | `GetSynergyMailGetContactList` | — | `contactGroupList` | ◦ | | `GetSynergyMailGetSchoolList` | — | `organizationList` | ◦ | | `GetSynergyMailGetStaffList` | — | `organizationStaffList` | ◦ | @@ -355,21 +375,52 @@ All of these are JSON-API calls: `POST /api/v1/mobile/PXPWebServices/`. ### Examples [Top](#TOC) -#### Child list +One section per method, in the order of the [method catalog](#method-catalog). Every request is `POST /api/v1/mobile/PXPWebServices/` with the bearer token; only the inner `request` object and the `data` payload are shown. Values are anonymized; `N`/`NN` stand for numbers and `H:MM` for clock times so district-specific schedules aren't leaked. + +#### GetChildListData ```json {"arguments":{"request":"{\"legacyAppRequest\":false,\"secondaryLogin\":false}"}} ``` -→ +→ `data.children` (trimmed): ```json -{"error": null, "data": {"children": {"childInfos": [ - {"childIntID": 0, "studentGU": "", "name": "", "schoolName": "", "...": "..."} -]}}} +{ + "districtName": "", + "districtRootURL": "https://.edupoint.com/api/v1/mobile/PXPWebServices/", + "appVersion": "…", "mobileClientTimeout": NN, + "showGradeBookModule": true, "showAttendanceModule": true, + "showSynergyMailModule": true, "showDisciplineModule": false, + "showHealthVisitModule": false, "…": "…", + "acknowledgementsPresent": false, + "childrenList": [ + {"childIntID": 0, "studentGU": "", "studentSSY": "", + "childName": "", "childFirstName": "", + "childPermID": "", "ID": "", "orgYearGU": "", + "organizationName": "", "grade": "NN", + "accessGU": "", "photo": "", "events": null, + "concurrentSchools": [], + "supportingFutureAttendance": false, "supportingPastAttendance": false, + "showArriveLate": true, "showByClass": true, "showLeaveEarly": true, + "daysInFutureToAcceptAttendance": NN, "daysInPastToAcceptAttendance": NN, + "attendanceReasonForFutureAtt": [ + {"reasonGU": "", "reasonType": 6, "description": "Illness", + "reasonTypeIcon": ""} + ], + "attendanceInstText": "", + "counselorName": null, "hallPassStatus": "", "hallPassCountToday": 0, + "studentAttendanceType": 1, + "allModules": [ + {"name": "Synergy Mail", "module": "21", "isEnabled": "Y", + "iconUrl": "Images/PXP/ModuleIcons/icon_Messages.png", + "moduleUrl": "PXP2_Messages.aspx", "moduleOrder": 0.0, + "pxpModuleCfgGU": null, "organizationYearGU": null} + ]} + ]} ``` -Child selection is a parameter (`childIntID`), not a stateful step — there is no "select child" call. `0` is the logged-in student on StudentVUE. Don't confuse `childIntID` (request ordinal) with `studentGU` (response GUID). +This is the app's boot call and doubles as the district configuration dump: dozens of `show*Module`/`supporting*` flags, per-student attendance-reporting settings, and the district's enabled modules in `children.allModules` (also copied per child) — useful for deciding which calls to attempt. The student entry key is **`childrenList`**. Child selection is a parameter (`childIntID`), not a stateful step — there is no "select child" call; `0` is the logged-in student on StudentVUE. Don't confuse `childIntID` (request ordinal) with `studentGU` (response GUID). -#### Class schedule +#### StudentClassList ```json {"arguments":{"request":"{\"childIntID\":0,\"loadAllTerms\":true,\"conSchOrgYearGU\":\"\",\"conSchTermIndex\":\"-1\",\"termIndex\":\"-1\"}"}} @@ -383,13 +434,13 @@ Child selection is a parameter (`childIntID`), not a stateful step — there is {"termIndex": 0, "termCode": 1, "termName": "Semester 1", "beginDate": "MM/DD/YYYY", "endDate": "MM/DD/YYYY", "schoolYearTrmCodeGU": "", "schoolName": "", - "orgYearGU": "", "termDefCodes": [{"termDefName": "S1"}]} + "orgYearGU": "", "termDefCodes": [{"termDefName": "S1"}, {"termDefName": "FY"}]} ], "studentClassScheduleForTerms": [ {"thisTermIndex": 0, "beginDate": "MM/DD/YYYY", "endDate": "MM/DD/YYYY", "orgYearGU": "", "classLists": [ - {"period": "1", "courseTitle": "", "roomName": "", + {"period": "N", "courseTitle": "", "roomName": "", "teacher": "", "teacherEmail": "", "sectionGU": "", "teacherStaffGU": "", "meetingDays": null, "excludePVUE": false, @@ -400,25 +451,54 @@ Child selection is a parameter (`childIntID`), not a stateful step — there is } ``` -With `loadAllTerms: true` classes live under `studentClassScheduleForAllTerms.studentClassScheduleForTerms[].classLists[]`; the single-term mode (`loadAllTerms: false`) returns a flat `classLists` under `studentClassSchedule`. Handle both. `orgYearGU` here is the value `Gradebook` wants as `concurrentSchOrgYearGU`. +With `loadAllTerms: true` classes live under `studentClassScheduleForAllTerms.studentClassScheduleForTerms[].classLists[]`. With `loadAllTerms: false` the same call returns `data.studentClassSchedule` — a flat `classLists` for the selected `termIndex` plus today's bell times: + +```json +{"studentClassSchedule": { + "termIndex": 0, "termIndexName": "Semester 1", "errorMessage": "", + "includeAdditionalStaffWhenEmailingTeachers": false, + "classLists": [ + {"period": "N", "courseTitle": "", "roomName": "", + "teacher": "", "teacherEmail": "", + "sectionGU": "", "teacherStaffGU": "", + "meetingDays": null, "excludePVUE": false, + "additionalStaffInformation": [], "additionalStaffInformationXMLs": []} + ], + "termLists": ["…same term objects as above…"], + "todayScheduleInfoData": {"date": "M/D/YYYY", "schoolInfos": [ + {"schoolName": "", "bellSchedName": "", + "classes": [ + {"period": "NN", "className": " -
", + "startTime": "H:MM AM", "endTime": "H:MM AM", + "startDate": "MM/DD/YYYY H:MM:SS AM", "endDate": "MM/DD/YYYY H:MM:SS AM", + "roomName": "", + "teacherName": "", "teacherEmail": "", + "staffGU": "", "sectionGU": "", + "emailSubject": "RE: Period NN, Section
", + "teacherURL": "", + "classURL": "", "attendanceCode": "", "hideClassStartEndTime": false} + ]}]}, + "concurrentSchoolStudentClassSchedules": []}} +``` + +Handle both shapes. `orgYearGU` here is the value `Gradebook` wants as `concurrentSchOrgYearGU`. `todayScheduleInfoData` is the only reliable source of bell times — see the `GetStudentClasesForGivenDay` notes for why. `teacherURL` is an HTML fragment embedding JavaScript `SMApp.composeEx({to: [{RecipientList: 0, GU: ""}], subject: "…", messageText: ""})` calls for Synergy Mail compose; `staffGU`/`emailSubject` carry the same data in usable form. + +#### GetStudentClasesForGivenDay / GetStudentClasesForGivenDayResponse -#### Today's classes +Two names for the same dedicated "today's classes" call; the app uses the `Response`-suffixed one. ```json {"arguments":{"request":"{\"childIntID\":0,\"schDate\":\"MM/DD/YYYY\",\"dayType\":0}"}} ``` -→ `data.todayScheduleInfo` (trimmed): +→ `data.todayScheduleInfo`: ```json -{ - "date": "MM/DD/YYYY", - "attendance": {"...": "..."}, - "schools": [{"schoolName": "", "...": "..."}] -} +{"date": "M/D/YYYY", "dateToLoad": "YYYY-MM-DDT00:00:00-07:00", + "attendance": null, "schools": []} ``` -Both `GetStudentClasesForGivenDay` and `GetStudentClasesForGivenDayResponse` exist; the app calls the `Response`-suffixed name. Class rows here follow the `schools[]` shape rather than the flat `classLists`. `dayType` distinguishes normal/alternate (flex) schedules. +Reproducibly `attendance: null, schools: []` mid-class on a regular school day — across both method names, both `dayType` values (normal/alternate flex schedules), and arbitrary `schDate` values; the response always echoes the current day, so `schDate` appears to be ignored. Treat empty `schools` as "no data", not "no school". The `date` fields use `M/D/YYYY` (no zero-padding), while `dateToLoad` is ISO-8601 with the local UTC offset. Get bell times from `StudentClassList`'s `todayScheduleInfoData` instead. -#### Current class +#### GetStudentClassTime ```json {"arguments":{"request":"{\"childIntID\":0}"}} @@ -426,15 +506,16 @@ Both `GetStudentClasesForGivenDay` and `GetStudentClasesForGivenDayResponse` exi → `data.studentClassNow` (trimmed): ```json { - "period": null, "courseTitle": null, "sectionID": null, "room": null, - "staffName": null, "startTime": null, "endtime": null, - "minutesRemainingToEndClass": null, - "resultCode": 4, - "currentClass": "For the current day, no Schedule Information was found for the selected student." + "period": "N", "courseTitle": "", "sectionID": "
", + "room": "", "staffName": "", + "startTime": null, "endtime": null, + "minutesRemainingToEndClass": "N", "minutesAfterClassStart": "NN", + "resultCode": 1, + "currentClass": "Period: N, Course Title: , Section ID:
, Room: , Staff Name: , All Day Code: Present" } ``` -Outside school hours the class fields are `null` and `resultCode` != 0 with a message in `currentClass`. +`currentClass` is a pre-rendered human-readable summary (it embeds today's attendance code); build your own UI from the individual fields, not by parsing it. `startTime`/`endtime` stay `null` even mid-class; the minute counters are strings. Outside school hours every class field is `null` and `resultCode` != 0 (4 = nothing found) with a message in `currentClass`, e.g. `"For the current day, no Schedule Information was found for the selected student."` #### Gradebook @@ -446,21 +527,46 @@ Outside school hours the class fields are `null` and `resultCode` != 0 with a me { "type": "Traditional", "errorMessage": null, + "hideStandardGraphInd": false, + "hideMarksColumnElementary": false, + "hidePointsColumnElementary": false, + "hidePercentSecondary": false, + "displayStandardsData": false, + "gbStandardsTabDefault": false, "reportingPeriods": [ - {"index": "0", "gradePeriod": "S1", "startDate": "MM/DD/YYYY", "endDate": "MM/DD/YYYY"} + {"index": "0", "gradePeriod": "", "startDate": "M/D/YYYY", "endDate": "M/D/YYYY"}, + {"index": "1", "gradePeriod": "", "startDate": "M/D/YYYY", "endDate": "M/D/YYYY"} ], - "reportingPeriod": {"...": "..."}, + "reportingPeriod": {"index": "0", "gradePeriod": "", "startDate": "M/D/YYYY", "endDate": "M/D/YYYY"}, "courses": [ - {"period": "1", "title": "", "courseName": "…", "courseID": "…", + {"period": "N", "title": " (
)", "courseName": "", "courseID": "
", "room": "", "staff": "", "staffEMail": "", - "staffGU": "", "imageType": null, "usesRichContent": false, + "staffGU": "", "imageType": "social", + "highlightPercentageCutOffForProgressBar": NN, "usesRichContent": false, "marks": [ - {"markName": "S1", "shortMarkName": "S1", - "calculatedScoreString": "", "calculatedScoreRaw": "…", - "gradeCalculationSummary": {"...": "..."}, - "assignments": [{"...": "..."}], - "assignmentsSinceLastAccess": [], - "standardViews": []} + {"markName": "S1MT", "shortMarkName": "S1MT", + "calculatedScoreString": "", "calculatedScoreRaw": "NN.N", + "standardViews": [], + "gradeCalculationSummary": [ + {"type": "Classwork", "weight": "NN%", "points": "NN.NN", "pointsPossible": "NN.NN", + "weightedPct": "NN.NN%", "calculatedMark": ""}, + {"type": "Assessment", "weight": "NN%", "points": "0.00", "pointsPossible": "0.00", + "weightedPct": "0.00%", "calculatedMark": "0"}, + {"type": "TOTAL", "weight": "100%", "points": "NN.NN", "pointsPossible": "NN.NN", + "weightedPct": "100.00%", "calculatedMark": ""} + ], + "assignments": [ + {"gradebookID": NN, "measure": "", "type": "Classwork", + "date": "M/D/YYYY", "dueDate": "M/D/YYYY", + "score": "N", "displayScore": "N out of N", "scoreCalValue": "N", "scoreMaxValue": "N", + "scoreType": "Raw Score", "points": "N / N", "point": "N", "pointPossible": "N", + "timeSincePost": "Nd", "totalSecondsSincePost": NNNNNN.0, + "notes": "", "measureDescription": "", + "teacherID": NN, "studentID": NNNNN, + "hasDropBox": false, "dropStartDate": "M/D/YYYY", "dropEndDate": "M/D/YYYY", + "resources": [], "standards": []} + ], + "assignmentsSinceLastAccess": []} ]} ], "standardsGradebook": null @@ -469,21 +575,84 @@ Outside school hours the class fields are `null` and `resultCode` != 0 with a me Assignments are inline in `courses[].marks[].assignments[]` — no second fetch. The quarter is a request parameter: valid `reportPeriod` values come from `reportingPeriods[].index`, so a full year takes one call per quarter. `standardsGradebook` is a second mode some districts use. `assignmentsSinceLastAccess` reflects per-account read state tracked by the server. -#### Attendance +Field quirks: mark names can be mid-term markers (`S1MT`), not just final-term codes; `calculatedScoreRaw` is a string; `totalSecondsSincePost` is a float; all score fields are duplicated in several forms (`score`/`displayScore`/`points`/`point`…) with `scoreType` distinguishing `Raw Score` from letter/percent grades; `gradebookID` here is the same id the calendar uses as `DGU`. Numeric lookups (`gradebookID`, `teacherID`, `studentID`) are numbers, not strings. If a mark has no weighted categories `gradeCalculationSummary` is a list with just the `TOTAL` row. + +#### GetStudentAttendanceList ```json {"arguments":{"request":"{\"childIntID\":0}"}} ``` -→ `data` keys `dailyAttendance` and `periodAttendance` (summary/entry objects). Whole-year history is a separate call, `GetStudentPastAttendanceData` (response key `reportPastAtteendanceXML` — Edupoint's spelling); schools that don't expose it return `error.code "400"`. +→ `data.dailyAttendance` can be `null`, with `data.periodAttendance` carrying everything: +```json +{ + "type": "Period", "startPeriod": 0, "endPeriod": NN, "periodCount": NN, + "schoolName": "", + "absences": [ + {"absenceDate": "MM/DD/YYYY", "reason": "", "reason2": null, + "note": "", "dailyIconName": "", + "codeAllDayReasonType": "", "codeAllDayDescription": "", + "periods": []}, + {"absenceDate": "MM/DD/YYYY", "reason": "", "reason2": null, + "note": "", "dailyIconName": "icon_excused.gif", + "codeAllDayReasonType": "icon_excused.gif", "codeAllDayDescription": "", + "periods": [ + {"number": "N", "name": "", "note": "", "reason": "", + "course": "", "staff": "", + "staffEMail": "", "iconName": "", + "schoolName": "", "staffGU": "", "orgYearGU": ""} + ]} + ], + "totalExcused": [{"number": N, "total": N}, {"number": N, "total": N}, {"...": "..."}], + "totalTardies": [{"number": N, "total": N}, {"...": "..."}], + "totalUnexcused": [{"number": N, "total": N}, {"...": "..."}], + "totalActivities": [{"number": N, "total": N}, {"...": "..."}], + "totalUnexcusedTardies": [{"number": N, "total": N}, {"...": "..."}], + "concurrentSchoolsLists": [] +} +``` + +The `total*` arrays have one `{number, total}` entry per period. An absence with an empty `periods` array is a pending/not-yet-verified entry. Whole-year history is a separate call, `GetStudentPastAttendanceData` (response key `reportPastAtteendanceXML` — Edupoint's spelling); schools that don't expose it return an error: -#### Student info +#### GetStudentPastAttendanceData ```json {"arguments":{"request":"{\"childIntID\":0}"}} ``` -→ `data.studentInfoXML` (photo, schedule summary) and `data.studentInfoDetailXML` — demographics, contacts, guardian info. Both are JSON objects despite the `XML` names. Contains PII. +→ +```json +{"error": {"code": "400", + "message": "Attendance data not available for this school", + "stackTrace": null}, + "data": null} +``` + +#### GetStudentInfoData -#### Calendar +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.studentInfoXML` can be `null`; `data.studentInfoDetailXML` carries the demographics. All PII — key list only: +```json +{"type": "Detail", "formattedName": "", "permID": "", + "gender": "<…>", "grade": "<…>", "address": "<…>", "lastNameGoesBy": "…", + "nickName": "…", "birthDate": "<…>", "eMail": "<…>", "phone": "<…>", + "homeLanguage": "…", "currentSchool": "", "track": "…", + "homeRoomTch": "", "homeRoomTchEMail": "", + "homeRoomTchStaffGU": "", "orgYearGU": "", "homeRoom": "", + "counselorName": "", "counselorEmail": "", + "counselorStaffGU": "", "photo": "", + "emergencyContacts": ["…"], "physician": {"name": "…", "hospital": "…", "phone": "…", "extn": "…"}, + "dentist": {"name": "…", "office": "…", "phone": "…", "extn": "…"}, + "userDefinedGroupBoxes": ["…"], "lockerInfoRecords": [], + "studentBusAssignments": ["…"], + "showStudentBusAssignmentInfo": false, "showPhysicianAndDentistInfo": false, + "showStudentInfo": true, "showFrontLineSpedURL": false, + "showFrontLine504URL": false, "showFrontLineParentPortalURL": false} +``` + +Both keys are JSON objects despite the `XML` names. `userDefinedGroupBoxes` holds district-configured extra fields (`UserDefinedGroupBox.GroupBoxLabel` + `UserDefinedItems[].ItemLabel`/`Value`). + +#### GetCalendarData ```json {"arguments":{"request":"{\"childIntID\":0}"}} @@ -494,19 +663,51 @@ Assignments are inline in `courses[].marks[].assignments[]` — no second fetch. "schoolBegDate": "MM/DD/YYYY", "schoolEndDate": "MM/DD/YYYY", "monthBegDate": "MM/DD/YYYY", "monthEndDate": "MM/DD/YYYY", "eventLists": [ - {"date": "MM/DD/YYYY", "title": "", "icon": null, + {"date": "MM/DD/YYYY", "title": "First Day of the School", "icon": null, "AGU": null, "dayType": 0, "startTime": "", "link": null, - "DGU": null, "dguInternal": null, "dgU2": null} + "DGU": null, "dguInternal": null, "dgU2": null, + "viewType": null, "addLinkData": null, "evtDescription": null}, + {"date": "MM/DD/YYYY", "title": "Holiday", "icon": null, + "AGU": null, "dayType": 1, "startTime": "All Day", "link": null, + "DGU": null, "dguInternal": null, "dgU2": null, + "viewType": null, "addLinkData": null, "evtDescription": null}, + {"date": "MM/DD/YYYY", + "title": " () : - Score: NN.NN", + "icon": "assignment.png", + "AGU": "0", "dayType": 2, "startTime": "", "link": "ASSIGNMENTS", + "DGU": "NNNNNN", "dguInternal": "NNNNNN", "dgU2": null, + "viewType": "2", "addLinkData": "", "evtDescription": null} ] } ``` -Called with just `childIntID` the server returns the current month window. Events with `AGU`/`DGU` set reference assignments; the detail fetch is `GetCalendarAssignmentDetails`. +Called with just `childIntID` the server returns the current month window. Mid-semester the list mixes three event kinds — school events, holidays, and gradebook assignments (`period` inside a title is the class period). `dayType` doubles as an event-kind discriminator here: `0` plain school event, `1` no-school day, `2` assignment. On assignment events `DGU`/`dguInternal` is the gradebook assignment id (equal to `Gradebook`'s `assignments[].gradebookID`) and `addLinkData` is the section `GUID` — the inputs for `GetCalendarAssignmentDetails`. Assignment `title`s already embed the score; there is no separate score field. + +#### GetCalendarAssignmentDetails + +Pass a calendar assignment event's fields through: + +```json +{"arguments":{"request":"{\"childIntID\":0,\"AGU\":\"0\",\"DGU\":\"NNNNNN\",\"dguInternal\":\"NNNNNN\",\"dgU2\":null,\"viewType\":\"2\",\"addLinkData\":\"\",\"dayType\":2}"}} +``` +→ `data.calendarAssignmentDetails`: +```json +{ + "classGU": null, "assignment": null, + "hideStandardGraphInd": false, "hideMarksColumnElementary": false, + "hidePointsColumnElementary": false, "displayStandardsData": false, + "assignmentEventDetailLists": [] +} +``` -#### Documents +The container comes back with `assignmentEventDetailLists: []` for past assignments; the exact input combination that populates it is still unknown. -List: `GetStudentDocuments` with `{"childIntID":0,"languageCode":"en"}` → `data.studentDocuments`: +#### GetStudentDocuments +```json +{"arguments":{"request":"{\"childIntID\":0,\"languageCode\":\"en\"}"}} +``` +→ `data.studentDocuments` (trimmed): ```json { "showDateColumn": true, "showDocNameColumn": true, "showDocCatColumn": true, @@ -515,24 +716,33 @@ List: `GetStudentDocuments` with `{"childIntID":0,"languageCode":"en"}` → `dat "studentDocumentDatas": [ {"documentGU": "", "documentFileName": ".pdf", "documentDate": "MM/DD/YYYY", "documentType": "Report Card", - "studentGU": "", "documentComment": ""} + "studentGU": "", "documentComment": " Sem 1 Final Mark"}, + {"documentGU": "", "documentFileName": ".pdf", + "documentDate": "MM/DD/YYYY", "documentType": "Unofficial Transcript", + "studentGU": "", "documentComment": "Transcript "}, + {"documentGU": "", "documentFileName": ".pdf", + "documentDate": "MM/DD/YYYY", "documentType": "Course History", + "studentGU": "", "documentComment": "Grad Profile "} ], - "documentSetupDatas": {"...": "..."} + "documentSetupDatas": [], + "documentsDiv": "…", "directions": "…" } ``` +`documentFileName` is a server-side GUID-named file, not the human title — the display name comes from `documentType`/`documentComment`. `documentType` values include: Report Card, Unofficial Transcript, Course History, Test Report, Parent Letter, attendance letters, surveys. + Fetch: `GetStudentDocumentContent` with `{"childIntID":0,"documentGU":""}` → `data.studentAttachedDocumentData.documentDatas[]`: ```json {"documentGU": "", "studentGU": "", "docDate": "MM/DD/YYYY", - "fileName": ".pdf", "category": "Report Card", "notes": "", - "docType": "Report Card", "base64Code": "JVBERi0xLjQKJ…", + "fileName": ".pdf", "category": "", "notes": "", + "docType": "PDF", "base64Code": "JVBERi0xLjQKJ…", "gbStudentID": null, "gbTeacherID": null, "gbGradeBookID": null} ``` -The PDF is inline base64 in `base64Code` (decodes to `%PDF-…`) — no URL, no second host. Observed `documentType` values: Report Card, Test Report, Parent Letter, attendance letters, surveys. The `gb*` fields suggest gradebook attachments ride the same envelope. +The PDF is inline base64 in `base64Code` (decodes to `%PDF-…`) — no URL, no second host. The `gb*` fields suggest gradebook attachments ride the same envelope. -#### Homework / class notes +#### GetStudentHWNotes ```json {"arguments":{"request":"{\"childIntID\":0,\"gu\":\"\"}"}} @@ -544,28 +754,208 @@ The PDF is inline base64 in `base64Code` (decodes to `%PDF-…`) — no URL, no `gu` selects a section (GUID from the schedule); empty returns the container only. Writing is `UpdateStudentHWNotes` with `gbhwNotesUpdateData`. -#### Messages +#### GetPXPContentMessage + +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.pxpMessagesData` (trimmed): +```json +{ + "supportingSynergyMail": true, + "messageListings": [], + "synergyMailMessageListingByStudents": [ + {"studentGU": "", + "synergyMailMessageListings": [ + {"attachmentDatas": [], "iconURL": "images/PXP/", "ID": null, + "beginDate": "MM/DD/YYYY 00:00:00", "type": 1, + "subject": "Progress report period '' is ending on MM/DD/YYYY", + "content": null, "endDate": null, + "read": false, "deletable": true, "from": null, + "subjectNoHTML": "Progress report period '' is ending on MM/DD/YYYY", + "module": 7, "email": null, "staffGU": null, "smMsgPersonGU": null}, + {"attachmentDatas": [], "iconURL": "images/PXP/", "ID": null, + "beginDate": "MM/DD/YYYY", "type": 1, + "subject": "Attendance notes for MM/DD/YYYY", + "content": null, "endDate": null, + "read": false, "deletable": true, "from": null, + "subjectNoHTML": "Attendance notes for MM/DD/YYYY", + "module": 1, "email": null, "staffGU": null, "smMsgPersonGU": null} + ]}]} +``` + +District/school alert messages. `messageListings` is the legacy shape (empty here); live messages arrive under `synergyMailMessageListingByStudents[]`, grouped per student. `module` identifies the emitting module (`7` = gradebook progress-report notices, `1` = attendance notes). `ID`/`from`/`content` can all be `null` for auto-generated notices. Replaces the SOAP `GetPXPMessages`; message attachments are fetched through the mail family below. + +#### GetUserDefinedModule + +```json +{"arguments":{"request":"{\"childIntID\":0,\"moduleIndex\":0}"}} +``` +→ `data.allModuleRecordData`: +```json +{"moduleRecords": []} +``` + +Empty when the district has no user-defined module content configured. + +#### GetSchoolInformationData + +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.studentSchoolInfoListing` (trimmed): +```json +{ + "school": "", "principal": "", + "schoolAddress": "", "schoolAddress2": "", + "schoolCity": "", "schoolState": "", "schoolZip": "", + "phone": "", "phone2": "", "URL": "", + "principalEmail": "", "principalGu": "", + "staffLists": [ + {"name": "", "eMail": "", + "title": "HS TEACHER", "phone": "", "extn": "", "staffGU": ""}, + {"name": "", "eMail": "", + "title": "HS SECRETARY OFFICE", "phone": "", "extn": "", "staffGU": ""} + ]} +``` + +A full staff directory — hundreds of entries at a large school, all teachers included with their `staffGU` (matching the schedule's `teacherStaffGU`). Titles are raw Synergy position codes (`HS TEACHER`, `HS SECRETARY OFFICE`, `HS SPED PARAEDUC`, …). + +#### GetContentMyAccountData + +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.pxpMyAccountData` (trimmed) — the account holder's contact/notification settings. Contains the student's name, address, phone and e-mail (all PII): +```json +{ + "formattedName": "", + "userID": "", + "homeAddress": "
, ", + "mailAddreess": "Same as Home Address", + "phoneNumbers": "C:
* * Indicates primary contact phone", + "adultID": null, + "canEditParentDemographicData": false, + "enableParentEmployerUpdates": false, + "enableParentPrimaryLanguageUpdates": false, + "enableParentNameUpdates": false, + "firstName": null, "lastName": null, "employer": null, + "primaryLanguageCode": null, "primaryLanguage": null, + "eMail": "", + "eMail1": null, "eMail2": null, "eMail3": null, "eMail4": null, "eMail5": null, + "eMailEnabled": false, "eMailVisible": false, + "hidePaperlessReportcard": true, "paperlessReportcardSetting": false, + "supporting_Email_Notification_ForStudentVUE": true, + "chkNotifyAttendance": false, "chkNotifyAttendanceEnabled": false, + "chkNotifyGrade": false, "chkNotifyGradeEnabled": false, + "chkNotifyGradebook": false, "chkNotifyGradebookEnabled": false, + "chkNotifyDiscipline": false, "chkNotifyHealth": false, + "showNotificationAttendance": false, + "showNotificationAttendanceEmail": false, "showNotificationAttendanceSMS": false, "showNotificationAttendanceVoice": false, + "showNotificationGrades": false, + "showNotificationGradesEmail": false, "showNotificationGradesSMS": false, "showNotificationGradesVoice": false, + "showNotificationAssignment": false, + "showNotificationAssignmentEmail": false, "showNotificationAssignmentSMS": false, "showNotificationAssignmentVoice": false, + "…": "…" +} +``` + +Field groups: `mailAddreess` is Edupoint's spelling; addresses/phones are `
`-separated HTML with a `*` primary marker; each `chkNotify*` toggle is paired with a `chkNotify*Enabled` flag and `showNotification*Email`/`SMS`/`Voice` variants; `eMail`…`eMail5` each have `Enabled`/`Visible` flags; the rest are self-edit permissions (`canEditParentDemographicData`, `*UpdatePermission`) and misc settings. + +#### GetAcknowledgementsData + +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.parentAcknowledgementMain`: +```json +{"parentAcknowledgementDatas": []} +``` + +Empty outside signature windows. + +#### GetHallPassSetup + +```json +{"arguments":{"request":"{\"childIntID\":0}"}} +``` +→ `data.hallPassRoomSetupXML` (a JSON object despite the name): +```json +{"hallPassMaxDaysForFuturePass": "NN", "hallPassCountUp": false, + "allowStudentsToEndHallPass": false, "hallPassRoomTypes": []} +``` + +Returned even where the pass calls fail (see below); empty `hallPassRoomTypes` means the feature has no rooms configured. The rest of the family — `GetHallPassData` (`getOnlyScheduedPases`, *sic*), `GetHallPassHistory` (`onlyOverTimeLimit`), `UpdateHallPass` — is district-dependent and often disabled. + +#### GetHealthData + +```json +{"arguments":{"request":"{\"getDatahealthConditions\":false,\"getDatahealthImmunizations\":false,\"getDatahealthVisits\":false}"}} +``` +→ +```json +{"error": {"code": "500", + "message": "Error: Please contact your school office for assistance. (ID: XXXXX)", + "stackTrace": null}, + "data": null} +``` + +The three booleans select the sections of `studentHealthData`; districts gate this heavily, and the generic `500` is what closed gates look like. + +#### GetStudentDisciplineData / GetStudentFeeData ```json {"arguments":{"request":"{\"childIntID\":0}"}} ``` -→ `data.pxpMessagesData` — district/school alert messages. Replaces the SOAP `GetPXPMessages`; message attachments are fetched through the mail family below. +→ both return the module-disabled error with their own module name: +```json +{"error": {"code": "400", + "message": "2100 - School District has not enabled access for the Discipline Module.", + "stackTrace": null}, + "data": null} +``` +```json +{"error": {"code": "400", + "message": "2100 - School District has not enabled access for the Fees Module.", + "stackTrace": null}, + "data": null} +``` + +#### GetSynergyMailUnreadCount + +```json +{"arguments":{"request":"{}"}} +``` +→ `data.messageCount`: +```json +{"error": null, "data": {"messageCount": 1}} +``` -#### Student mail (Synergy Mail) +The whole mail stack is a `GetSynergyMail*` / `UpdateSynergyMail*` family (see the [method catalog](#method-catalog)): compose goes `GetSynergyMailRecipientSearch` / `GetSynergyMailRecipientAddressing` → `GetSynergyMailSaveNewMessage`; read/delete `GetSynergyMailSaveReadOrDeleteMsg`; move `GetSynergyMailMoveMessage`; folders `GetSynergyMailUpdateFolder`; signatures `GetSynergyMailUpdateSignatures`; inbox counters `GetSynergyMailInboxCount` and this method; address books `GetSynergyMailGetContactList`/`GetSchoolList`/`GetStaffList`/`GetStudentList`/`GetTeacherList` (teachers also come from the schedule — `GetSynergyMailGetTeacherList` returns `studentClassScheduleForAllTerms`). Don't assume the whole family works because one member does — see below. -The whole mail stack is a `GetSynergyMail*` / `UpdateSynergyMail*` family (see the [Method catalog](#method-catalog)). Inbox paging: +#### GetSynergyMailGetConversations ```json {"arguments":{"request":"{\"pageToLoad\":0}"}} ``` → ```json -{"error": null, "data": {"conversations": {"...": "..."}, - "isLastPageLoaded": false, "totalUnreadConversationMessages": 0}} +{"error": {"code": "500", + "message": "Error: Please contact your school office for assistance. (ID: XXXXX)", + "stackTrace": null}, + "data": null} ``` -Compose: `GetSynergyMailRecipientSearch` / `GetSynergyMailRecipientAddressing` → `GetSynergyMailSaveNewMessage`. Read/delete: `GetSynergyMailSaveReadOrDeleteMsg`; move: `GetSynergyMailMoveMessage`; folders: `GetSynergyMailUpdateFolder`; signatures: `GetSynergyMailUpdateSignatures`. Unread counters: `GetSynergyMailUnreadCount`, `GetSynergyMailInboxCount`. Address books: teachers come from the schedule (`GetSynergyMailGetTeacherList` returns `studentClassScheduleForAllTerms`), plus staff/student/contact/school lists. +Even with mail enabled (`pxpMessagesData.supportingSynergyMail: true`, unread count above returning `1`), the conversations/address-book calls can fail with the generic `500` — including `GetSynergyMailInboxCount` and the `GetSynergyMailGet*List` family. The `(ID: XXXXX)` suffix is a server-side event ID for the district's support desk; it varies per failure. + +#### TestSystemCall -#### Hall passes and health +```json +{"arguments":{"request":"{}"}} +``` +→ `data` is a bare boolean: +```json +{"error": null, "data": true} +``` -Hall passes (district-dependent, often disabled): `GetHallPassSetup`, `GetHallPassData` (`getOnlyScheduedPases` — *sic*), `GetHallPassHistory` (`onlyOverTimeLimit`), `UpdateHallPass`. Health: `GetHealthData` with three booleans — `getDatahealthVisits`, `getDatahealthImmunizations`, `getDatahealthConditions` — returning `studentHealthData` with the requested sections; districts gate this heavily. +Connectivity smoke test; no payload beyond the boolean.