Connecting Red Stet to your LMS
Red Stet plugs into Canvas, Schoology, and Brightspace via LTI 1.3 — the same standard Grammarly, Turnitin, and Khan Academy use. One install per school; teachers attach assignments inside the LMS. Rosters sync. Grades pass back. Google Classroom and LTI Dynamic Registration are scaffolded for Phase 11.
Who this is for: the IT admin doing the one-time install, and any teacher whose class came in from an LMS. No LMS, or no third-party installs allowed? The join-code flow in Setting up your first class still works.
- What you get when LTI is connected
- Per-LMS support status
- Initial registration — the admin task
- Canvas — Developer Key walkthrough
- Schoology & Brightspace
- Deep Linking — picking an assignment from inside the LMS
- Roster sync (NRPS)
- Grade passback (AGS)
- Google Classroom
- Surviving course copy
- Troubleshooting
- The fallback — join codes still work
What you get when LTI is connected
Four mechanics, all driven by LTI 1.3 + LTI Advantage:
- Single sign-on. A student clicks a Red Stet assignment in the LMS and lands already signed in. No second password. The LMS identity binds to a Red Stet account on first launch via the
iss+subpair. - Deep Linking. A teacher adds a Red Stet assignment to a module via the tool-side picker. The LMS gets back a resource link bound to one assignment — no URL pasting.
- Roster sync (NRPS). Red Stet pulls the roster from the LMS. Adds and drops update without anyone touching a join code.
- Grade passback (AGS). Finalize a rubric score; the number lands in the LMS gradebook column within seconds.
Teachers do nothing beyond authoring and grading. The plumbing is invisible when it works.
| Capability | Status |
|---|---|
| Single sign-on (OIDC launch) | Live |
| Deep Linking — assignment picker | Live |
| Roster sync (NRPS) | Live |
| Grade passback (AGS) | Live |
| Post-hoc lineitem binding | Live |
| Course-copy resource-link rebinding | Live |
| LTI 1.3 Dynamic Registration | Scaffold |
| Google Classroom | Scaffold |
Per-LMS support status
LTI 1.3 is one standard; every LMS implements it with quirks. Where each platform stands today:
Canvas (Instructure)
Developer Key install, Deep Linking, NRPS, and AGS run against Canvas's published JWKS and OAuth2 endpoints.
Schoology & Brightspace
LTI 1.3 endpoints are platform-agnostic — the same code handles Canvas, Schoology, or Brightspace launches. Admin at one of these? Get in touch and we'll work the install with you.
Google Classroom scaffold
Google Classroom isn't LTI 1.3 — it speaks its own REST API + OAuth2 flow. Phase 11 ships the schema, bindings table, and Settings panel. A "Link Google Classroom" button in teacher Settings surfaces the current state. Live OAuth, roster sync, and grade pass-back unlock once a Google Workspace OAuth client + the three Classroom scopes are provisioned. Checklist in docs/PHASE_11_GO_LIVE.md.
Anything else
If your LMS speaks LTI 1.3 — Blackboard, Sakai, Moodle, Open edX, D2L variants — the plumbing should work. Email us your openid configuration URL.
| LMS | State |
|---|---|
| Canvas | Production |
| Schoology | Early adopter |
| Brightspace (D2L) | Early adopter |
| Blackboard | On request |
| Moodle / Sakai / others | On request |
Initial registration — the admin task
Every LMS needs to know about Red Stet before any teacher can install it. One-time, school-wide, done by your LMS administrator. After that, teachers add Red Stet to courses without admin involvement.
What we need from your IT admin:
- Platform issuer (
iss) — Canvas:https://canvas.instructure.com. Self-hosted Canvas, Schoology, Brightspace use their own domain. - Auth endpoints — OIDC login URL, OAuth2 token URL, JWKS URL. Canvas's developer-key UI lists all three.
- Scopes — at minimum NRPS read and AGS score. Too few breaks grade passback; too many is harmless.
What we give back:
- Target Link URI —
https://<our-convex-host>/lti/launch - OIDC Login URI —
https://<our-convex-host>/lti/login - JWKS URL —
https://<our-convex-host>/.well-known/jwks.json— Canvas fetches this to verify our signed JWTs. - Tool name & description — Red Stet, with a one-liner for the LMS app catalog.
The admin pastes our values into the LMS's developer-key UI; we paste yours into ltiRegistrations. The (iss, clientId) pair routes every launch.
One-paste Dynamic Registration scaffold
To skip the field-by-field paste: the admin Integrations panel surfaces a Dynamic Registration URL. Paste that one URL into your LMS's "Register a tool" UI; the LMS POSTs us its OpenID configuration; we return OIDC client metadata in one round trip. Per-LMS activation is gated on a partnership test before being marked live.
Canvas — Developer Key walkthrough
For Instructure-hosted Canvas, install at Admin → Developer Keys → + Developer Key → + LTI Key.
1. Method
Manual Entry. (Paste JSON works too; we provide the JSON on request.)
2. Key fields
- Title — Red Stet
- Target Link URI — paste from us
- OpenID Connect Initiation URL — paste from us
- JWK Method — Public JWK URL → paste our JWKS URL
3. LTI Advantage Services
Toggle on:
Can retrieve user data(NRPS) — roster syncCan create and view assignment data in the gradebook(AGS) — grade passbackCan edit assignment data in the gradebook— push the rubric max score
4. Placements
Enable Assignment Selection and Link Selection. Both fire Deep Linking — how a teacher adds a Red Stet assignment to a module.
5. Save, then install per-account
Set State: ON, copy the Client ID, send it back to us.
Then install at the account level: Admin → Settings → Apps → +App → By Client ID. That makes Red Stet available in every course.
Schoology & Brightspace
Both are full LTI 1.3 implementations; the install matches Canvas — admin generates a key pair, pastes our endpoints, enables NRPS + AGS, sends back issuer + client ID + deployment ID.
Schoology
Admin path: System Settings → Integration → Add LTI 1.3 App. Scope labels read longer than Canvas's but the underlying scopes are identical. Schoology's "Developer Key state" equivalent is the per-app enabled flag — flip it after saving.
Brightspace (D2L)
Admin path: Admin Tools → Manage Extensibility → LTI Advantage. Registration is split: Tool Registration + Tool Deployment. Register once, deploy per org-unit or org-wide. Brightspace's NRPS endpoint is spec-compliant, so the standard roster path works.
What's the same everywhere
Issuer, client ID, deployment ID, JWKS URL, OAuth2 token URL — five strings is all ltiRegistrations needs. After that, every teacher in the school can add Red Stet to a course.
ltiRegistrations row per LMS install. The (iss, clientId) pair is the lookup key on every incoming launch.
Google Classroom scaffold
The second-most-common K-12 LMS. Doesn't speak LTI 1.3 — Classroom integrations run through Google's REST API + a Workspace OAuth client. Phase 11 ships the structural pieces. Live OAuth and REST calls are gated on a Google Cloud project + OAuth client provisioning, which needs an operator with billing access.
What's shipped
googleClassroomBindingstable mapping each classroom to a Google course id.users.googleClassroomAuthper-teacher link record.assignments.googleCourseworkIdso a Red Stet assignment mirrors to a coursework row.- Stub mutations (
linkAccount,syncRoster,pushGrade) that throw an "in development" message until credentials wire up. - "Link Google Classroom" button in teacher Settings surfacing the current state.
What unlocks live sync
Checklist at docs/PHASE_11_GO_LIVE.md. Three scopes:
classroom.courses.readonly— list the teacher's coursesclassroom.rosters.readonly— read each course's student listclassroom.coursework.students— push grades to coursework submissions
Once those scopes are granted and the secret lives in our Convex env, the stub mutations swap to live calls with no schema migration.
convex/googleClassroom.ts mark the lines that flip from throw to fetch.
Deep Linking — picking an assignment from inside the LMS LtiDeepLinkingRequest
In Canvas: + Assignment → Submission Type → External Tool → Red Stet. Canvas fires a Deep Linking request; Red Stet opens in an iframe, signed in as you, with a picker:
- Existing assignments you've authored, filtered to ones not bound to a Canvas link. Pick one — done.
- Create a new assignment inside the picker. Full editor one click away; save binds it to the Canvas link in the same step.
On confirm, Red Stet signs a Deep Linking response JWT carrying the assignment id in a custom claim — red_stet_assignment_id. Canvas writes that into the resource link; every student launch resolves without an extra round trip.
Bind multiple assignments to one module
Multi-select. Attach a sequence of three drafts as three resource links in one Canvas action — useful for multi-stage essays with checkpoints.
red_stet_assignment_id. If a launch fails to resolve, mismatched or missing custom claims on the LMS side is the first thing to check.
Roster sync (NRPS) contextmembership.readonly
First teacher launch from a course does two things: mints a Red Stet classroom (named to match the course) and pulls the roster via NRPS. Every LMS-listed student becomes an active enrollment.
When sync runs
- On first launch — full sync; mints the classroom, enrolls everyone.
- On every subsequent teacher launch — delta sync; adds new enrollments, reactivates anyone removed.
- Nightly cron — every active
ltiContextBindingsrow re-syncs server-side. Catches mid-week add/drops. - On demand — Resync roster from LMS in the classroom's kebab menu. Use after a registrar bulk-enrolls.
What it pulls
Per student: LMS stable user id (sub claim), display name, email when exposed, and the LTI roles array. Roles decide student vs. co-teacher. If the LMS suppresses email (FERPA-restrictive), sync still works — students appear by name with a placeholder identity.
Removals
When a student drops, the next sync sets leftAt. Submissions and revisions stay on their account; they just leave your active roster. Re-adding reactivates the same row — history intact.
Grade passback (AGS) ags/scope/score
Finalize a rubric score; Red Stet POSTs an AGS Score 2.0 payload to the LMS's lineitem endpoint. Within seconds the score appears in the Canvas/Schoology/Brightspace gradebook column.
How the binding works
Two paths connect an assignment to an LMS gradebook column:
- Deep-link binding (common case). When a teacher picks via the external-tool flow, the LMS creates a column tied to that resource link. Our
ltiResourceLinksrow carries thelineitemURL; we POST scores there. - Post-hoc binding. Built the assignment first and want to send grades to an existing column? The assignment dashboard's binding picker lists LMS columns visible to you. Pick one; we store an
lmsBindingsrow and use that lineitem.
Multi-LMS broadcast
If the same assignment binds to columns in two LMSes, the score POST fans out to every binding the student matches. A Canvas student's score doesn't go to Schoology, but if there's a Schoology binding and the student has a Schoology identity, both gradebooks get the number.
What carries with the score
The score, the assignment's max points (default 100, override in rubric), and optionally a one-line comment. Teachers can include a provenance-review summary — confirmed/non-issue/pending flag counts — so integrity work travels with the grade.
lmsSyncFailures with a one-click retry. Grades aren't lost; they sometimes need a nudge.
Surviving course copy resource_link.id_history
Canvas (and any LTI 1.3 platform) lets a teacher copy a course into a fresh shell. Each module's resource link gets a fresh resource_link_id, but the LMS hands us the prior id chain in the launch claim's id_history.
What Red Stet does
On every resource-link launch, Red Stet walks id_history. If any prior id resolves to a known assignment binding, we mint a fresh ltiResourceLinks row for the new id pointing at the same assignment — lineage preserved in previousResourceLinkIds. The teacher gets a notification ("Assignment carried into a copied course") so the rebind is auditable.
Why a new row instead of patching the old one
The old course often runs in parallel with the new one while a teacher finishes a unit before archiving. Each link binds independently to the same assignment.
rl_4f2e9a3b
rl_8c1a47ba
(rebound via id_history)
Troubleshooting
Three failure modes cover ~90% of tickets:
"My class didn't appear in Red Stet"
First teacher launch mints the classroom. If you opened Red Stet via direct URL (not the LMS), no LMS context, no classroom. Fix: open the assignment from inside the LMS once. The classroom mints and stays.
"The roster's missing students"
Three culprits, in order of likelihood:
- The student hasn't launched yet. Red Stet has no binding until they launch once. Ask them to click the assignment, or trigger Resync roster from LMS — that enrolls every LMS-roster student, even ones who haven't clicked.
- The LMS suppresses email. Some districts disable email exposure for FERPA. Sync works (students appear by name); the binding is name-only.
- NRPS scope isn't granted. If everyone is missing, the install lacks the contextmembership scope. See the Canvas walkthrough.
"Grades aren't passing back"
Open the assignment's LMS sync panel — shows the most recent score post per student and any LMS error responses.
- "No AGS context" — the student joined via join code rather than LTI, or the assignment isn't bound to a column. Normal for hybrid classrooms; score is saved, not pushed.
- "token rejected (401)" — install's scopes were trimmed after registration. Have your admin re-check the AGS score scope.
- "lineitem not found (404)" — the LMS column was deleted. Re-bind via the post-hoc picker.
Every failure lands in lmsSyncFailures with a one-click retry. If retry still fails, the error message in that row is the most informative thing to send us.
Token endpoint rejected client_assertion: scope ags/scope/score not granted to this client
Insufficient permissions: contextmembership.readonly not granted
The fallback — join codes still work
Every classroom — including LMS-minted ones — carries a join code and an invite link. Not the primary path for an LMS-connected school, but a safety net.
When to reach for them:
- One student is having LMS trouble — provisioning, SSO, a late add. Hand them the join code; they're in within a minute, LMS binding catches up on next sync.
- Your LMS is down. Rare but real. Class still runs.
- One-off cross-class project — a writing partnership with another section. Invite specific students via link without involving the LMS.
Join code is in the classroom header strip; the kebab menu has Rotate invite link and Rotate join code if one leaks. See Setting up your first class for mechanics — identical whether the classroom came from LTI or was created directly.
Related
→ Setting up your first class — the standalone classroom flow plus join-code mechanics.
→ Co-teachers & TAs — adding a second teacher or TA to an LTI class.
→ Creating an assignment — what travels through Deep Linking.
Stuck on an install? Email us — for early-adopter installs we get on a call with your IT admin and walk it together.