For schools and vendors

Roster mapping protocol — school, EduSpaze, vendor

Roster mapping protocol — school, EduSpaze, vendor

How a school's staff and pupils come to exist correctly inside a vendor's app, stay correct for a year, and disappear cleanly at the end of it. Both the school and the vendor are asked to comply with this. EduSpaze provides the mechanism and holds both sides to it. Last updated 2026-09-06.

Read alongside the vendor integration guide (the technical contract) and the school admin guide.

1. The agreement in one paragraph

The school owns identity. EduSpaze brokers it. The vendor never receives it. A pupil and a member of staff reach a vendor app as a *code* — RB-4213, STF-0007 — together with the school's id and the codes of the classes they belong to. No name, no email address, no date of birth, ever. The vendor builds its accounts on those codes; the school keeps them stable; EduSpaze guarantees that the same code means the same person on the launch, in the roster and in the telemetry that comes back.

Everything below exists to make one sentence true: the vendor can always answer "which class is this person in", and can never answer "who is this person".

2. The four things being mapped

ObjectOwnerIdentifier the vendor seesChanges when
School (tenant)EduSpazetenant — the school's EduSpaze idnever
Pupilthe schoolsubjectStudent.externalId, from the school's own SISnever, if the school complies with §4
Member of staffthe schoolsubjectSTF-nnnn, minted by EduSpaze or set from the school's staff codenever
Class / groupthe schoolopaque id + a teaching label (5A, Reading group Blue)at rollover, and when the school reorganises

Pupil and staff subjects share one namespace, so a vendor keeps one subject column and one account table for both. Subjects are unique within a school: two schools may both use STF-0001, and the pair (tenant, subject) is the real key.

3. The lifecycle

Eight stages. Each names what each party does, and what EduSpaze records.

S0 · Agreement

PartyDoes
VendorSigns the Universal Data Sharing Agreement: what is passed, retention, deletion on deprovisioning. Declares its integration tier.
SchoolApproves the app commercially (pilot, bundle or direct) and names the person accountable for the roster.
EduSpazeRecords the tier and the agreement on the vendor's listing. Nothing is provisioned yet.

Nothing in this protocol overrides the DSA; where they differ, the DSA wins and EduSpaze should be told so this document can be corrected.

S1 · The school prepares its roster

The school imports pupils (School → Students → Import, CSV) with externalId taken from its SIS. This is the moment that decides whether the next twelve months work — see §4.

Staff already exist as accounts; EduSpaze mints STF-nnnn for each the first time one is needed, or the school can set its own staff codes.

PartyDoes
SchoolImports pupils with stable externalIds; creates classes and groups; assigns pupils to classes and teachers to classes.
EduSpazeValidates the file, reports every rejected row with a reason, records the import on the school's audit log.
VendorNothing.

S2 · The school approves the app, and its scope

The school decides which pupils may use the app — by class, by group, or by year — and that decision creates the connections. A pupil with no connection does not exist to that vendor: they are absent from the roster, refused on launch, and rejected in telemetry.

Staff scope follows pupil scope. A member of staff may open a vendor app when they teach a class that is in scope. That is deliberate: it needs no second list to maintain, and it cannot grant a teacher access to a cohort they do not teach.

PartyDoes
SchoolChooses the classes or groups in scope. Reviews it each term.
EduSpazeCreates a connection per pupil, dates it, and records who approved it.
VendorNothing.

S3 · Credentials and the handshake

PartyDoes
VendorIssues an API key and a launch signing secret (Integration & API). Sets its launch URL (https). Implements signature verification exactly as published, and decides whether to opt in to launch v2 (class information on the launch).
EduSpazeStores only a hash of the API key; shows each credential once; refuses an http launch URL.
SchoolNothing.

A vendor that cannot verify a signature must not be given a key. This is the one step where "we'll fix it later" ends with pupils in the wrong classes.

S4 · Provisioning — the vendor builds its accounts

PartyDoes
VendorGET /me → the tenants it may ask about. GET /roster?tenantId=… → every connected pupil with class and group codes, and the staff who teach those classes. Creates one account per (tenant, subject). Stores the class ids.
EduSpazeAnswers only for that vendor's own connections, in that tenant. An unknown tenant answers the same as an empty one.
SchoolNothing.

The vendor creates accounts before anyone arrives. A first launch should find an account waiting, not create one in a hurry with whatever the URL happened to contain.

S5 · First launch

A pupil opens the app from the EduSpaze launcher; a teacher opens it from School → Connected Apps. Both arrive through the same signed handshake.

PartyDoes
EduSpazeSigns subject, tenant, ts, nonce (v1) and, for a vendor on v2, role, classes and the pupil's class, className, year, groups. Records the launch on the school's audit log.
VendorVerifies the signature, rejects anything older than five minutes, remembers the nonce, then signs the person in to the account provisioned at S4.
SchoolNothing.

A launch is an authentication, not a provisioning event. A subject the vendor does not recognise means its roster is stale: re-sync, then admit them. It never means "create an account for whoever this is".

S6 · Steady state

PartyDoes
VendorRe-syncs the roster nightly with updatedSince. Posts telemetry (/heartbeat while active, /sessions from its own logs).
EduSpazeFolds telemetry into engagement, shows the school minutes per pupil per app, shows a parent the same for their own child where the school allows it.
SchoolKeeps classes and groups current as pupils move.

S7 · Change during the year

What happenedWhat the vendor seesWhat the vendor must do
Pupil moves from 5A to 5Bsame subject, different class idmove the account; never create a second one
Pupil leaves the schoolconnection revoked → absent from the rosterstop accepting the subject; delete per the DSA
New teacher takes a classa new staff subject in staff[]provision their view
A class is added to scopenew subjects appearprovision them
Academic rolloverclass ids change; academicYear moves onkeep the accounts, re-map the classes

The rule underneath all of it: the subject is the person; the class is where they are today. A vendor that keys accounts on the class, or on a name it inferred, breaks at the first move.

S8 · Exit

PartyDoes
SchoolRevokes in EduSpaze — never by emailing the vendor, because only the platform revocation reaches the API.
EduSpazeDrops the pupils from the roster, refuses their subjects on launch and telemetry, records the revocation.
VendorDeletes or anonymises what it holds for those subjects within the DSA's window, and confirms.

An erasure request for one pupil follows the same path: the school anonymises the pupil in EduSpaze, the subject stops resolving, and the vendor deletes on that signal. Historic engagement stays as time-only, attached to a token that no longer identifies anybody.

4. What each party must comply with

The school must

  1. Give every pupil a stable externalId from the SIS, and never reuse or renumber it. This is the single obligation that everything else rests on. A re-import that changes the codes makes every pupil a stranger to every vendor and orphans their history.
  2. Keep class and group assignments current — they are what the vendor places people by.
  3. Approve and review scope by class or group each term, and revoke through the platform.
  4. Never send a pupil's name, email or date of birth to a vendor out of band. If a vendor asks for one, tell EduSpaze: it is a breach of the DSA, not a favour.
  5. Keep a school administrator with a password, so a provider outage cannot lock the school out of its own settings.

The vendor must

  1. Key every account on (tenant, subject). Not on a name, not on an email, not on a class, not on an order of arrival.
  2. Verify the launch signature before trusting anything in the URL, reject a launch older than five minutes, and refuse a repeated nonce.
  3. Provision only from the roster. No self-signup, no invite links, no "just add this one pupil" inside a school's tenant.
  4. Treat a subject that has left the roster as revoked, and delete within the agreed window.
  5. Send no content. Telemetry carries when and how long, never what: no scores, no work, no free text. There is no field for it, and inventing one is a breach.
  6. Never attempt re-identification — not by cross-referencing, not by asking a pupil their name, not by inference from a class label.
  7. Tell EduSpaze before changing how it verifies launches, and opt in to v2 only after its verifier signs whatever it receives rather than a fixed list of parameters.

EduSpaze will

  1. Guarantee that one subject means one person, for as long as the school keeps its codes stable, across launch, roster and telemetry.
  2. Answer a vendor only about its own connected pupils, in the tenant it names.
  3. Refuse — identically and without explanation — any launch outside a verified domain, an inactive configuration, or a lapsed subscription, so no probe learns anything.
  4. Record every launch, every scope change, every revocation and every roster read on the school's own audit log, and show the school what each vendor was told.
  5. Never publish a change to the v1 launch contract. A vendor's working integration stays working; new information arrives only in a version they opt into.
  6. Show the school, in its own portal, exactly what a vendor is told about a pupil and a member of staff, in the same words as this document.

5. What is never shared

SharedNever shared
pupil subject, staff subjectname, email, username, date of birth
school id and namehome address, contact details, photographs
class and group ids, with teaching labelsyear-level attainment, SEN status, safeguarding flags
role (student, teacher, staff, school_admin)anything a guardian said, or any note about a pupil
session start and end timeswhat a pupil did in the app: scores, answers, content

If a vendor's product genuinely needs something on the right-hand column, that is a conversation with EduSpaze and the school before a line of code — not a field added to a payload.

6. When it goes wrong

SymptomCauseWho fixes it
Vendor rejects a pupil at launchroster is stale, or the connection was revokedvendor re-syncs; if still absent, the school's scope is the answer
A pupil has two accounts in the appvendor keyed on something other than the subjectvendor, by merging on (tenant, subject)
Everyone appears as new in Januaryschool renumbered its externalIdsschool, by restoring the codes — the vendor cannot repair this
Telemetry counted for nobodysubject not connected to that vendorschool's scope, or a stale vendor roster
A teacher sees a class they do not teachclass assignments in EduSpaze are wrongschool
Launch signature fails after an EduSpaze deployshould never happen; the v1 contract is regression-testedEduSpaze, immediately

Whoever notices, tells EduSpaze. The audit log settles what actually happened, because both the launch and the roster read are on it.

7. What EduSpaze still has to build for this

Stated plainly, because the protocol above describes the intended process and part of it is not yet self-service:

GapConsequence todayNeeded
No school-facing way to connect pupils to an app (S2)connections exist only in seeded demo data; a real pilot needs EduSpaze to create thema scope screen: pick classes or groups, see who it covers, approve, and revoke
No staff scopeany teacher with the permission can open any connected app, rather than only the apps covering classes they teachderive staff scope from pupil scope, as §S2 describes
No rollover handlingclass ids change at the academic year and nothing prompts the school or warns the vendora rollover step, and a academicYear change flagged in the roster
No "what this vendor was told" view for the schoolthe school is asked to trust §5 rather than see ita per-vendor disclosure page in the school portal
No SIS syncexternalId stability depends on a CSV export, which is exactly the obligation most likely to be brokenOneRoster or Google Classroom sync, replacing the upload

The first two are what make this document enforceable rather than aspirational.

Source: docs/roster-mapping.md in the repository. Last updated with the code it describes.

Roster mapping protocol — EduSpaze