How to sync PowerSchool SIS with Kastr

Kastr can consume the documented OneRoster subset from an authorised provider endpoint or CSV ZIP export. For a proprietary PowerSchool export, a district-maintained job can use the separate JSON roster API. These routes do not establish a PowerSchool plugin, partnership or certified compatibility.

Last reviewed 2026-08-04

PowerSchool ENROLL_STATUS values, and what your export should do with each
ValuePowerSchool meaningTreat asInclude in the export?Can this person be messaged?
0Actively enrolledActiveYesYes
-1Pre-registered / pre-enrolledPre-enrolledYes, deliberatelyYes, this is your August welcome audience
-2InactiveWithdrawnRoster API: leave out, and the next sync withdraws them. CSV import: include the row, marked withdrawnShould not be
2Transferred out of districtWithdrawnAs for -2Should not be
3Graduated / historicalHistoricalNoNo
4Imported historical recordIgnoredNoNo

Districts customise status codes, so confirm yours against your own Students table rather than trusting this list. The rule that matters: pick one filter and keep it stable. Changing the filter between two nightly runs is indistinguishable, to a diff engine, from thousands of students withdrawing overnight.

Getting the data out: three routes

Data Export Manager, scheduled. The path most districts take. Build a student export and a contacts export, schedule both nightly, drop them to an SFTP location you control. Effort is a few hours, most of it deciding which contact fields you want. This is the route we recommend for a first sync because everything is visible and nothing is magic.

A saved PowerQuery pulled by your own job. If your team is comfortable with PowerQueries and the PowerSchool API, you can have a district-side script pull the query result, reshape it into JSON entries and POST them to the roster endpoint without touching disk. Fewer moving parts, more code to own.

PowerSchool's OneRoster output through a broker. If you already run Clever or ClassLink against PowerSchool, that feed is already normalised and you should use it, with one caveat that costs districts a fortnight: guardian phone numbers are frequently not in the broker share, which leaves you with a roster that cannot send SMS. Check the guardian fields before you commit to that route.

A supported OneRoster feed uses Integrations, OneRoster and its preview/apply workflow. A custom export job uses the separate JSON API, which applies a valid POST immediately. Reconcile counts and guardian coverage on either route.

Contacts, not the legacy parent fields

PowerSchool's modern Contacts model replaced the old Mother, Father and Guardian columns on the student record, and a lot of district exports still read the legacy fields because that is what the script did in 2015. Those columns are frequently stale, frequently blank, and cannot express a third adult or a custody restriction.

Export from Contacts. The fields worth carrying, and what your export job should do with each:

  • Contact record and relationship type → a guardian entry whose guardianOfSourceId is the student. A roster entry has no relationship field, so the relationship type decides who goes in; Kastr does not store it.
  • Emergency priority / contact order → which contacts you send. A roster entry has no rank field.
  • Custody or guardianship flag → whether the adult is in the export at all. This is the field that decides whether an adult should receive anything, and it is worth auditing before your first send rather than after.
  • "Receives mail" / correspondence flags → your district's decision about whether that adult belongs in the export. There is no consent field in a roster entry; SMS consent and opt-outs are applied in Kastr when each text is sent.
  • Phone type → which number goes in the entry's phone field. Send a mobile: a landline cannot take SMS, and Kastr has no voice channel to reach it. Getting this wrong is how districts end up texting a work switchboard.

Contact-point records store the value, validation state, consent state, source and rank. SMS delivery checks consent for the selected number and honours opt-outs. A source label does not automatically replace the primary phone number or change delivery priority.

The July rollover, and why it is the dangerous night

Every PowerSchool district has one week in July where schools roll, grade levels increment, next-year enrolments activate and last year's seniors go historical. Depending on how your export filter is written, the file produced during that window can be missing most of your district.

Two specific patterns cause it. First, an export filtered on the current school year runs before the year rolls and returns almost nothing. Second, an export filtered on ENROLL_STATUS = 0 runs after schools roll but before enrolments activate, and returns the same almost-nothing.

What happens in Kastr: the run computes that accepting the batch would withdraw more than half of active records, refuses with an HTTP 409, records aborted_guardrail, and leaves yesterday's roster in place. In a 12,000-student district that is the difference between an aborted job and 9,000 families quietly detached from their students.

During rollover: pause the affected automatic source, check school scope and expected changes, then use a native OneRoster preview or inspect a custom JSON batch before posting. Resume only after the district confirms the results.

Every run, including the aborted ones, is written to a per-organisation, append-only audit log whose entries are chained with SHA-256, so the record of what happened on rollover night is not something anyone can quietly tidy up afterwards.

One human, one record

PowerSchool districts hit this constantly: a parent with children at three schools, a teacher who is also a parent at the building where she teaches, a paraprofessional who is a guardian for a niece. Systems that key identity on enrolment produce three, four, five copies of the same adult, and every one of them gets its own text message.

Kastr stores one person and attaches effective-dated roles. She is a guardian at the middle school from August 2024, a teacher at the elementary from August 2019, and both facts are true simultaneously without duplication. Selecting a student in the composer auto-expands to their guardians, and the expansion de-duplicates, so a family with two children in the same broadcast gets one message rather than two.

Audiences resolve as the district, a school, a class, specific people or a saved group, so "message everyone at Lincoln Middle" is a school audience you pick at send time. Grade-level targeting is the current limit: "all of 7th grade" is a saved group you build once, not a filter you pick at send time.

Questions people actually ask

Does Kastr have a native PowerSchool plugin in the marketplace?

This repository implements standards-based OneRoster ingestion, not a proprietary PowerSchool connector. If your provider exposes a compatible, authorised OneRoster endpoint, Kastr can read it directly; other exports can be mapped to the custom JSON API or typed CSV template. Validate the actual provider configuration.

Which PowerSchool ENROLL_STATUS values should I include in the export?

Include 0 for active students, and include -1 for pre-registered students if you want to message incoming families before the year starts, which most districts do in August. Exclude graduated and imported historical records entirely. Confirm the codes against your own instance, since districts customise them, and then keep the filter stable, changing it between runs looks exactly like a mass withdrawal to a diff engine.

How do I get guardian mobile numbers out of PowerSchool Contacts rather than the legacy fields?

Build the contacts export from the Contacts model rather than the Mother, Father and Guardian columns on the student record. Carry relationship type, contact order, custody flag, correspondence flag, phone number and phone type, and use them in your job to decide which adults go in and which number goes in the phone field. Phone type is what lets you send the mobile rather than a landline, which Kastr cannot text.

What happens to messages queued for a student who was withdrawn overnight?

They skip that person. The roster records the withdrawal, and audience resolution filters out suppressed and retired people when a message is actually sent, not when it was queued. A queued message, or one composed later to a saved group, leaves out anyone the sync has withdrawn, with no separate audience change.

Can Kastr write attendance or message logs back into PowerSchool?

No. There is no write-back path of any kind. PowerSchool stays the system of record and Kastr never modifies it. If you want message history alongside SIS data, pull it from our REST API into your warehouse rather than expecting it to appear in PowerSchool.

One price. Every feature. Locked for three years.

$3.50 per student per year under 5,000 students. No tiers or add-on modules. Normal messaging is included under a published fair-use allowance, with transparent cost recovery only above it.