Red Stet
← Back to Help
Help · For teachers & admins · Tier 1 — Getting started

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

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 + sub pair.
  • 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.

An assignment can bind to more than one LMS. If your school runs both Canvas and Schoology, a single assignment installs in both; grades broadcast to whichever LMS each student came in through.
CapabilityStatus
Single sign-on (OIDC launch)Live
Deep Linking — assignment pickerLive
Roster sync (NRPS)Live
Grade passback (AGS)Live
Post-hoc lineitem bindingLive
Course-copy resource-link rebindingLive
LTI 1.3 Dynamic RegistrationScaffold
Google ClassroomScaffold

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.

LMSState
CanvasProduction
SchoologyEarly adopter
Brightspace (D2L)Early adopter
BlackboardOn request
Moodle / Sakai / othersOn request
"Early adopter" means the code is there and the spec says it should work; we'll do the install with you so we catch the platform-specific quirks together.

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 URIhttps://<our-convex-host>/lti/launch
  • OIDC Login URIhttps://<our-convex-host>/lti/login
  • JWKS URLhttps://<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.

Tool name Red Stet
Target Link URI https://red-stet.convex.cloud/lti/launch
OIDC Login URI https://red-stet.convex.cloud/lti/login
JWKS URL https://red-stet.convex.cloud/.well-known/jwks.json
Scopes contextmembership.readonly · ags/scope/score · ags/scope/lineitem
These are the values your IT admin pastes into Canvas's Developer Key UI (or the equivalent in Schoology / Brightspace).

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 sync
  • Can create and view assignment data in the gradebook (AGS) — grade passback
  • Can 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.

Developer Key — Red Stet
State ON
Client ID 10000000000123
Placements Assignment Selection · Link Selection
1
Create Developer Key
Manual entry, paste our URIs
2
Toggle on LTI Advantage services
NRPS + AGS scopes
3
Set State = ON, copy Client ID
Send it back to us
4
Install at account level
Apps → By Client ID

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.

First school of your kind? We'll do the install with you. The spec is solid; the LMS UIs are not. Email us.
Registration row stored by Red Stet
iss https://schoology.com
clientId 5482937401
deploymentId 2:8a4f9c…
authTokenUrl …/oauth2/token
jwksUrl …/lti/jwks
institutionName Lakeside Middle School
One 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

  • googleClassroomBindings table mapping each classroom to a Google course id.
  • users.googleClassroomAuth per-teacher link record.
  • assignments.googleCourseworkId so 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 courses
  • classroom.rosters.readonly — read each course's student list
  • classroom.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.

Why ship the scaffold now. Schema-then-credential lets a Phase 11 school start a binding in dev mode and light it up when credentials land. PENDING markers in convex/googleClassroom.ts mark the lines that flip from throw to fetch.
Google Classroom link — current state
OAuth client PENDING — see go-live doc
Scopes (required) courses.readonly · rosters.readonly · coursework.students
Teacher link UI Shipped — surfaces "in development" notice on click
Schema Shipped — bindings + per-teacher auth + coursework id
Existing Red Stet classrooms can be bound to Google courses the moment OAuth is wired — no schema changes needed.

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:

  1. Existing assignments you've authored, filtered to ones not bound to a Canvas link. Pick one — done.
  2. 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.

The custom claim is 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.
Pick a Red Stet assignment to attach to Canvas: Essay 1 — argument draft
The Crucible — argument draft
MLA · 750 words · revision-friendly
Modified 2 days ago
Hamlet — close reading
MLA · 500 words
Last term
+ Create a new assignment
Opens the full editor
Attach 1 assignment

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 ltiContextBindings row re-syncs server-side. Catches mid-week add/drops.
  • On demandResync 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.

OK
Roster synced from Canvas
24 students · 1 added, 0 removed
2 min ago
···
Auto-resync
Nightly · next run 3:00 AM local
Scheduled
Resync roster from LMS
The classroom kebab menu carries a manual resync. Use it when a registrar just changed the roster and you can't wait for the nightly cron.

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 ltiResourceLinks row carries the lineitem URL; 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 lmsBindings row 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.

The score is saved locally first. Passback is a downstream side-effect, not the source of truth. Failures (network, expired token, LMS outage) go into lmsSyncFailures with a one-click retry. Grades aren't lost; they sometimes need a nudge.
OK
Score posted to Canvas
Ava Chen — 87/100 · Essay 1
Just now
OK
Score posted to Canvas
Ben Kowalski — 91/100 · Essay 1
Just now
!
Score saved · Canvas sync failed
Diego Reyes — 79/100 · token rejected (401)
Retry
Failed syncs queue with retry. The local score is never lost.

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.

If a launch hits a copied link with no history. Some LMSes strip the id_history claim (older Canvas builds, some Brightspace configs). The new link looks fresh — you'll see "this resource link isn't bound yet." Open it once via Deep Linking and it rebinds.
Old course (Spring '26)
→ Essay 1 — argument draft
Copied course (Fall '26)
→ Essay 1 — argument draft
(rebound via id_history)
OK
Assignment carried into a copied course
Notification fires once · old + new links both work
Auto

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.

Grade sync failed · Canvas returned 401
Token endpoint rejected client_assertion: scope ags/scope/score not granted to this client
Retry Copy error Email IT admin
Roster sync failed · Canvas returned 403
Insufficient permissions: contextmembership.readonly not granted
Retry Copy error
Failures are durable — they don't disappear after one retry. The cron retries them on a back-off, and the assignment dashboard always shows the current state.

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.

Join code — hand this to a student in person
RS7K2P
Invite link
red-stet.com/j/4f2e9a3b…
These live on every classroom — including LMS-synced ones — exactly because the LMS path isn't always available.

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.

Back to the help library.

Stuck on an install? Email us — for early-adopter installs we get on a call with your IT admin and walk it together.