All postsBuilding for US school districts · Part 4

OneRoster, honestly: the spec versus what districts actually send

The fields that carry the weight, the ones that lie, and the endpoints nobody fills in. A working guide to implementing a OneRoster consumer that survives September.

OneRoster is the closest thing US K-12 has to a common language for who is in which class. It's also a specification that describes considerably more than any district will send you.

Which means implementing it correctly and implementing it usefully are two different projects. This post is the second one.

Illustration of a large blueprint of a building where only a few rooms are lit, representing the small subset of the OneRoster specification districts actually provide
The specification is the whole blueprint. What arrives is a handful of lit rooms.

What it is

OneRoster is a 1EdTech specification for exchanging roster and gradebook data. Two versions are in live use. 1.1 is everywhere. 1.2 is better and increasingly required in procurement. Build for 1.1, accept 1.2, and expect to meet both, sometimes from the same district for different schools.

It comes in two bindings and three service profiles.

CSV. A zip of files (manifest.csv, orgs.csv, users.csv, academicSessions.csv, courses.csv, classes.csv, enrollments.csv and optionally more) dropped on SFTP, usually nightly. The manifest declares which files are present and whether it's a full set or a delta. Older, less fashionable, still extremely common.

REST. A read API with OAuth 2.0 client credentials, paginated and filterable. Nicer to work with, and what you should ask for. Just know that "we support the REST API" can mean anything from a fully conformant implementation to a thin wrapper someone wrote once.

The profiles: Rostering is the core, who exists and who's in what. Gradebook covers line items, results and score passback. Resources covers content association. Most integrations are rostering only. A district offering you gradebook has usually thought about this harder than average.

The five fields that carry the weight

sourcedId

The identifier for every object, and the closest thing you get to a primary key.

Treat it as a stable external identifier, never your internal one, for the reason covered in the data model of a school: when a district migrates SIS, these change. Everything below assumes you have an identities table between the feed and your users.

status and dateLastModified

Together these are the entire delta mechanism, and both have traps.

status is active or tobedeleted. There's no hard delete. And here's the part that catches people: a record disappearing from a feed has not necessarily been deleted. It may simply not be in this file. Absence is not deletion. Only tobedeleted is, and even then it means stop showing this, not destroy the history.

dateLastModified is how you ask for only what changed. In REST that's a filter. In CSV it's the manifest's delta mode.

Both are only as good as the provider's implementation, and a meaningful number of providers touch dateLastModified on every row every night, which turns your delta into a full sync. Detect that rather than trusting it. Hash the row and compare.

role

In 1.1 the vocabulary is small: student, teacher, parent, guardian, relative, aide, administrator, proctor. In 1.2 it's richer and can be expressed per-org.

The important behaviour isn't in the vocabulary. It's that roles live on enrolments, not on people. The same human is a teacher in one class and, in some districts, a student in a professional learning course in another. Storing user.role is a decision you'll regret in a district that runs staff PD through the same system.

enrollment.primary

A boolean on teacher enrolments. In a co-taught section one teacher is flagged primary and the other isn't. This answers "who is the teacher of record" for gradebook ownership, for reporting, and for who gets the email.

It's also frequently wrong, or absent, or true for both. Have a deterministic fallback and make it visible rather than silent.

grades

CEDS-aligned grade codes as strings: IT, PR, PK, TK, KG, 01 through 12, PS, UG, Other.

Note it's an array on a class. Mixed-grade sections are real, particularly in elementary, special education and rural districts.

What districts actually send

This is where the specification and reality separate.

The spec says What arrives
A demographics endpoint exists Most districts decline to share it. Build as though it's absent
Users have email addresses Frequently absent for elementary students, by policy
Sessions nest year, term, grading period Often a single session called "2026-2027" with nothing inside it
Classes are instructional You'll also get "Homeroom", "Study Hall", "DO NOT USE" and a section with 400 students
Enrolments are unique Duplicates happen. Same user, same class, two rows, sometimes different roles

Two more worth knowing:

Courses and classes carry local codes you'll need. courseCode, classCode, subjects. They're how district staff recognise a section. Surfacing the district's own code in your interface instead of your generated title makes support conversations dramatically shorter.

If your gradebook logic requires grading periods, you need a fallback for the districts whose sessions are thin, or you'll crash them into a support ticket.

Reading the feed correctly

A few behaviours that separate an implementation that survives from one that doesn't.

Never replace the world. The naive nightly job (truncate, reload) destroys every internal id you've issued, breaks every foreign key from your own domain, and makes the identity table pointless. Diff instead.

Order matters on ingest. Orgs, then academic sessions, then courses, then classes, then users, then enrolments. Anything else means resolving references to rows that don't exist yet, and the usual workaround produces users with no name who live forever.

Paginate defensively. REST implementations vary in how they handle limit and offset under concurrent writes. A large district paginating through enrolments while the SIS is being updated can hand you duplicates or skips. Sort by a stable key and reconcile against a count.

Expect partial availability. Any endpoint can be slow or down while others work. A sync that aborts the whole run because courses timed out has just decided nothing updates tonight.

Keep the raw payload, briefly. When a teacher says "my class is wrong", the first question is always what the district actually sent. Retaining source rows for a bounded window, inside your retention schedule, turns an unanswerable ticket into a two-minute lookup.

If you're writing to the gradebook profile

Three notes.

lineItems are the assessable things and belong to a class and an academic session, so the grading period matters. If the district's sessions are thin, this is exactly where it hurts.

results carry the score and a status.

categories group line items in a way the district's gradebook understands. If your categories don't map to theirs, teachers see your scores land in the wrong bucket and weight incorrectly.

Most of what goes wrong here is common to the LTI route too, so it's covered properly in grade passback that lands.

On certification

1EdTech runs conformance certification for OneRoster, and certified products appear on a public register. It's a genuine signal, it means somebody ran the test suite, and districts increasingly ask for it.

It's also not a claim to make before you have it, and it certifies a specific product at a specific version rather than a company. Worth pursuing once you have something stable enough to certify. Worth being precise about until then.

The short version

  • Build for 1.1, accept 1.2, support CSV and REST.
  • sourcedId is external, never your primary key.
  • Absence is not deletion. tobedeleted ends visibility, never retention.
  • Verify dateLastModified rather than trusting it.
  • Roles live on enrolments. Grade levels are strings. Demographics is absent.
  • Diff, don't replace. Ingest in dependency order. Keep the raw payload briefly.

Common questions

What is OneRoster?
A 1EdTech specification for exchanging roster and gradebook data between a school district and an application. It defines orgs, academic sessions, courses, classes, users and enrolments, and is delivered either as a REST API or as a nightly CSV drop.
What is the difference between OneRoster 1.1 and 1.2?
Version 1.1 is the most widely deployed. Version 1.2 improves the gradebook service, adds resources, and supports richer roles expressed per organisation. Build for 1.1, accept 1.2, and expect to meet both, sometimes from the same district.
If a record disappears from a OneRoster feed, has it been deleted?
No. Absence is not deletion. A record may simply not be present in a delta payload or a partial share. Only a status of tobedeleted signals deletion, and even then it means stop showing the record rather than destroy its history.
Is OneRoster certification required to sell to districts?
It is not required, but 1EdTech conformance certification is increasingly asked for in procurement and certified products appear on a public register. It certifies a specific product at a specific version rather than a company.

Working on something that has to survive a district?