How we survive when a vendor breaks something
Every connector uses the same reliability primitives, written once in
app/connectors/base.py. No connector is allowed to skip them.
⟳
Retry with backoff
3 attempts, exponential + jitter. Retries transient errors (408, 425, 429, 5xx) and all network failures.
⏱
Respect Retry-After
When the vendor tells us to slow down, we listen. No thundering herd.
⇄
Endpoint version fallbacks
Try v1 → v2 → v3 transparently. Different tenants run different versions of the same API — we probe.
⚠
Partial success is a real outcome
Users pulled but groups failed? That's still a win. Errors surface without erasing the good data.
🔐
Credentials encrypted at rest
Fernet (AES-128) with a key that never leaves the server. Never logged, never cached in memory across runs.
📋
Actionable error messages
"403 (plugin missing student.read permission?)" — not just "sync failed." Errors tell admins how to fix it.
Systems we integrate with
1Microsoft Entra ID (Azure AD)
HIGH CONFIDENCE
Microsoft's directory service — the source of truth for staff accounts at
most districts using Office 365. Microsoft Graph is one of the best-documented
APIs we integrate with.
Authentication
- OAuth2 client credentials
- App registration in Azure portal
- Scope:
https://graph.microsoft.com/.default
- Permissions:
User.Read.All, Group.Read.All
Endpoints
GET /v1.0/users
GET /v1.0/groups
- Delta via
$filter=lastModifiedDateTime ge ...
- Paged via
@odata.nextLink, $top 999
⚠ Known gotchas
ConsistencyLevel: eventual header is required for $count or $search.
- Deleted users don't appear in
/users — need the deletedItems endpoint for offboarding detection.
- Guest users have
userType=Guest and usually no jobTitle.
2Google Workspace (Admin SDK)
HIGH CONFIDENCE
Google's Admin SDK covers directory, org units, and groups for districts on
Google for Education. Auth is more involved than most — domain-wide delegation
with service account impersonation — but the API itself is rock-solid.
Authentication
- Service account with JSON key
- Domain-wide delegation authorized in Admin console
- Impersonates a real super-admin email
- OAuth2 JWT assertion → Bearer token
Endpoints
GET /admin/directory/v1/users
GET /admin/directory/v1/groups
GET /admin/directory/v1/customer/my_customer/orgunits
- Paged via
pageToken, 500 max
⚠ Known gotchas
- Domain-wide delegation must be explicitly authorized in the Google Admin console — a frequent cause of
401: unauthorized_client.
customer=my_customer only works when impersonating a user in that customer's domain.
- Rate limit: 2400 req/min per project. Our backoff respects 429 + Retry-After.
3Clever
HIGH CONFIDENCE
The K-12 rostering + SSO platform. Clever acts as a middle layer between
the SIS (PowerSchool/IC/Skyward/etc.) and everything downstream. If a
district has Clever, pulling from Clever is often simpler than going
directly to the SIS.
Authentication
- OAuth 2.0 client credentials
- HTTP Basic auth on
/oauth/tokens
- JWT Bearer token, 1h TTL
Endpoints (v3.0)
GET /v3.0/users?role=teacher|student
GET /v3.0/schools
GET /v3.0/sections
- HATEOAS pagination via
links[].rel=next, limit 100
⚠ Known gotchas
- Response shape is double-nested:
{"data": [{"data": {...}}]} — the inner .data is the actual record.
- Student emails aren't included by default — the district admin must enable sharing in Clever's privacy settings.
- v2.1 and v2.0 endpoints exist but are deprecated. We use v3.0 exclusively.
4Infinite Campus (OneRoster 1.1 / 1.2)
MEDIUM CONFIDENCE
One of the two dominant K-12 SIS vendors. Infinite Campus exposes
integrations via the IMS OneRoster standard — but because the standard is
large and each district enables a different subset, real-world behavior
varies.
Authentication
- OAuth 1.0a with HMAC-SHA256 (yes, really)
- Consumer key + secret issued per-district via Connection Manager
- Signed
Authorization: OAuth ... header
Endpoints
GET /ims/oneroster/v1p1/users?role=teacher|student
GET /ims/oneroster/v1p1/orgs
GET /ims/oneroster/v1p1/classes
- Pagination via
limit, offset (100/page)
⚠ Known gotchas
- OAuth 1.0a signatures must include query params in the base string — extremely easy to get wrong. Test against IC's sandbox before shipping.
- District must enable OneRoster at System Settings → Digital Learning → OneRoster.
- Some older districts still run OneRoster 1.0 (no
v1p1, flat array response). We fall back automatically.
5PowerSchool (Plugin API)
MEDIUM CONFIDENCE
The largest K-12 SIS by market share. PowerSchool's API is exposed via
district-installed "plugins" — a plugin.xml declares the
OAuth scopes. Mechanics are straightforward; the per-district install
step is the friction point.
Authentication
- OAuth 2.0 client credentials
- RFC-standard Basic auth; we fall back to body-creds if that fails
- Token at
/oauth/access_token
Endpoints
GET /ws/v1/district/teacher
GET /ws/v1/district/student
GET /ws/v1/district/school
- Paged via
?page=N&pagesize=500
⚠ Known gotchas
- 403 almost always means the plugin is installed but not enabled, or the scope requested doesn't match
plugin.xml.
district/teacher includes users with status=0 (inactive) — filter in application code.
- Non-teaching staff comes from
/ws/v1/staff, not /ws/v1/district/staff.
- Full OpenAPI is at
{base_url}/ws/swagger/index.html once the plugin is installed.
6Skyward (Qmlativ)
MEDIUM CONFIDENCE
Skyward's SIS and School Business Suite. Modern districts run Qmlativ,
legacy districts on SMS 2.0 (EOL 2025). API docs are gated behind a
district login, so a lot of our knowledge is from leaked PDFs and
customer deployments.
Authentication
- OAuth 2.0 client credentials
- Token endpoint: Qmlativ
/api/oauth/token or SMS 2.0 /ISCorp/API/oauth/token
- We try both via version fallback
Endpoints
GET /api/v1/Entity/Staff
GET /api/v1/Entity/Student
- Paged via
pageSize + pageNumber
- Envelope:
{"Entities": [...], "TotalCount": N}
⚠ Known gotchas
- Paths are case-sensitive (
Staff not staff).
- Entity names change across versions —
Staff vs Employee; discover via /api/v1/Metadata/Entity.
- Some districts IP-allow-list — our EC2 outbound IP must be pre-registered.
- SMS 2.0 is EOL in 2025 — don't build new features for it.
7Aeries SIS
HIGH CONFIDENCE
California-concentrated SIS, ~1700 districts. Their public docs are
actually good — rare in K-12. Rate limits are tight so we lean on
backoff more than for other vendors.
Authentication
- API key in
AERIES-CERT header
- Older tenants also accept
X-API-KEY — we send both
Endpoints
GET /api/v5/staff
GET /api/v5/students
GET /api/v5/schools
/api/v6/* — fallback for 2023+ tenants
⚠ Known gotchas
- Rate limit is ~60 req/min — aggressive. Respect 429 or expect to get blocked.
- Response is a flat array for most endpoints but
/schools uses {"data": [...]}.
/staff returns teaching + non-teaching; filter on the JobClass field.
8Tyler Munis
LOW CONFIDENCE
Tyler Munis is the HR/ERP system a lot of districts run payroll on. Their
API module is separately licensed (≈$10k/year) and docs are not public.
We ship a Munis connector but do not warranty it for production until
we have 2+ customer deployments.
Authentication
- OAuth 2.0 (2023+) OR custom API key (2020.x)
- Token endpoint varies by version
Endpoints
GET /api/hr/employees
GET /api/hr/positions
GET /api/hr/departments
- Response shapes vary significantly between versions
⚠ Known gotchas
- Tyler doesn't publish API docs publicly. District must request the "Munis API access package" from Tyler.
- Field names are inconsistent:
employeeId vs empNum vs emp_no.
- Some districts are on-prem — endpoints are internal and need VPN (out of current product scope).
💡 Recommended fallback
If the district's Munis has no API module licensed: fall back to their
nightly SFTP-delivered HR extract. Most districts already have this
pipeline for other vendors.
9Frontline Absence Management (Aesop)
LOW CONFIDENCE
The de-facto substitute/absence management system at most US districts.
Frontline's public API is realistically non-existent — they run on
constantly-rotating internal endpoints. What districts actually use is
a nightly SFTP drop.
Authentication
- Username + password → Bearer token
- Token endpoint rotates — we try 3 candidates
Endpoints
GET /api/v1/absences?startDate=...&endDate=...
GET /api/v1/employees
- Fallbacks:
/api/v2/*, /v1/*
⚠ Known gotchas
- Credentials are a real admin login — no service accounts exist.
- Date range required; absences > 180 days old return empty.
- "Aesop legacy" vs "Frontline unified" use entirely different endpoint shapes.
💡 Recommended fallback
For any district where the REST endpoints misbehave, we prefer the
nightly SFTP drop. CSV-based ingest → same normalization pipeline,
more reliable. On the roadmap as P2.