# canvas-dev-suggestions Short answer: yes—you should run a backend for an LTI 1.3 tool, and have **your server** (not the browser) exchange the LTI launch for a **short-lived, scoped token** and then call only the Canvas REST endpoints you need (e.g., Classic Quizzes + Modules). Below is a crisp, end-to-end recipe you can follow. # 0) What you’ll build (secure architecture) * **Frontend**: a minimal page that renders after an LTI launch (course context in the JWT), with UI to “Generate quiz,” “Create module,” etc. * **Backend**: handles OIDC login + LTI launch validation, exchanges for a **Canvas OAuth2 token** via the **LTI Advantage OAuth2 service**, and then calls Canvas APIs using **scoped developer keys**. Tokens are **short-lived**; refresh when needed. ([developerdocs.instructure.com][1]) --- # 1) Admin prep in Canvas (one-time) 1. **Create an LTI 1.3 Developer Key** Admin → **Developer Keys** → **+ LTI Key**. Record the **Client ID**. Enable **scope enforcement** for this key (least-privilege). ([Canvas Community][2]) 2. **Choose minimal scopes** (tweak as you implement) * Read course context and content you’ll analyze (e.g., pages, files, assignments, discussions). * Write only what you create (Classic Quizzes, quiz questions, modules/items). Canvas exposes a machine-readable **list of applicable scopes** and the exact `url:GET|...` patterns; use that to verify you’ve enabled only what your code calls. ([developerdocs.instructure.com][3]) 3. **Add placements** for your tool Typical: **Course Navigation** (launch your app in a course) and/or **Module item** selection. (Assignments placement if you’ll write grades via AGS later.) ([developerdocs.instructure.com][4]) --- # 2) Your LTI 1.3 server: launch & tokens ### A. Implement OpenID Connect login + LTI launch * Support **OIDC login initiation** (Canvas → your tool), then **LTI launch** POST with an ID token (JWT). * Validate: issuer, audience (your Client ID), nonce, message type, deployment ID, context claims. * Store the **course\_id** (and optionally user/roles) from the launch claims. Canvas supports **dynamic registration** too, but static registration is fine to start. ([developerdocs.instructure.com][5]) ### B. Exchange for an access token (server-to-server) From your backend, call Canvas’s **OAuth2 token endpoint** with your LTI client credentials to obtain a **short-lived access token** to: * Use **LTI Advantage services** (e.g., NRPS roster, AGS grading), and/or * Call **Canvas REST API** endpoints that are permitted by your **scoped developer key**. Tokens expire (≈1 hour); use refresh tokens to renew. ([developerdocs.instructure.com][1]) ### C. Optional LTI services you may want * **NRPS (Names & Roles)** to pull the current roster *when needed* (e.g., for targeted announcements). Requires the NRPS scope in your tool config. ([developerdocs.instructure.com][6]) * **AGS (Assignment & Grade Services)** if you later want your quizzes to post grades to the gradebook. ([developerdocs.instructure.com][7], [IMS Global][8]) --- # 3) Reading course content for your AI pipeline With a valid, scoped token, your backend can fetch only what you need for content analysis: Common endpoints you’ll call (examples): * **Pages**: `GET /api/v1/courses/:course_id/pages` (+ `/:url`) * **Files**: `GET /api/v1/courses/:course_id/files` (then download file) * **Assignments**: `GET /api/v1/courses/:course_id/assignments` * **Discussions**: `GET /api/v1/courses/:course_id/discussion_topics` * **Modules** (read, and later write): `GET /api/v1/courses/:course_id/modules` Confirm the exact scope strings via the **Scopes API** list for your account before enabling. (E.g., `url:GET|/api/v1/courses/:course_id/pages`, etc.) ([developerdocs.instructure.com][3]) --- # 4) Writing Classic Quizzes & Modules (least privilege) ### Classic Quizzes (REST) Typical write flow: 1. **Create a quiz** `POST /api/v1/courses/:course_id/quizzes` 2. **Add questions** `POST /api/v1/courses/:course_id/quizzes/:quiz_id/questions` (You can also create **question groups** if you’re assembling randomized pools.) 3. (Optional) **Publish** the quiz; set availability, time limit, etc. 4. (Optional) **Create a Module** and add the quiz as a module item. For Classic Quizzes submissions/testing APIs, see “Quiz Submissions” docs. Ensure your scopes include the specific `url:POST|...quizzes` and `.../questions` patterns. ([documentation.instructure.com][9]) ### Modules * Create a module: `POST /api/v1/courses/:course_id/modules` * Add items: `POST /api/v1/courses/:course_id/modules/:module_id/items` Again, enable only the `url:POST|...modules` scopes you call. ([developerdocs.instructure.com][3]) --- # 5) End-to-end request flow (what happens at runtime) 1. **Instructor** clicks your tool in **Course Navigation** → Canvas performs **OIDC → LTI launch** to your backend with a signed ID token (contains `course_id`, roles). ([developerdocs.instructure.com][4]) 2. **Backend** validates the launch, stores context, then **exchanges for an OAuth2 access token** using your LTI client credentials. ([developerdocs.instructure.com][1]) 3. **Backend** reads selected course content (pages/files/etc.), runs **AI processing** to propose quiz questions, module outlines, announcements. 4. **Backend** writes **Classic Quiz** + questions and/or **Module** + items via REST. Tokens are **short-lived and scoped**, reducing blast radius. ([developerdocs.instructure.com][10]) 5. **Frontend** shows links to the created Canvas objects (quiz, module) in that same course. --- # 6) Minimal security checklist (practical) * **Use scoped developer keys**; verify every REST call maps to an enabled scope. (Canvas even provides an endpoint that lists scopes available in your account.) ([developerdocs.instructure.com][3]) * **Never** issue or store Personal Access Tokens for this tool. Keep all token exchange **server-side**. ([Canvas Community][2]) * Validate **JWTs** (issuer, audience, nonce), store per-launch **state**, and rotate your client secret. * Log every write (who launched, which course, which endpoint). * Prefer **per-course launches** (course nav or module placement) so your app naturally operates within one course context per session. ([developerdocs.instructure.com][4]) --- # 7) Exact scopes to start with (tune down or up) Use the scopes list to find the precise strings for your instance; the pattern typically looks like: *Read (content for AI analysis)* * `url:GET|/api/v1/courses/:course_id` * `url:GET|/api/v1/courses/:course_id/pages` (+ show) * `url:GET|/api/v1/courses/:course_id/files` (+ file download) * `url:GET|/api/v1/courses/:course_id/assignments` * `url:GET|/api/v1/courses/:course_id/discussion_topics` *Write (Classic quizzes & modules only)* * `url:POST|/api/v1/courses/:course_id/quizzes` * `url:POST|/api/v1/courses/:course_id/quizzes/:quiz_id/questions` * `url:PUT|/api/v1/courses/:course_id/quizzes/:quiz_id` (to publish/tune settings) * `url:POST|/api/v1/courses/:course_id/modules` * `url:POST|/api/v1/courses/:course_id/modules/:module_id/items` Then add/remove as your logs prove you don’t need certain calls. ([developerdocs.instructure.com][3]) --- # 8) Classic Quizzes vs. “New Quizzes” You’re targeting **Classic Quizzes**, which are fully covered by the REST API documented in Canvas. If/when you move to **New Quizzes**, the APIs and scoping are different; check the evolving scopes/posts before you switch. ([Canvas Community][11]) --- # 9) Do you really need NRPS/AGS? * For your described tool (generate quizzes/modules from existing content), **you can skip NRPS/AGS at first**—you only need course context + REST. * Add **NRPS** if you want to personalize comms or target by role/section. Add **AGS** only if you want your tool to create gradebook columns and post grades. ([developerdocs.instructure.com][6]) --- If you want, I can sketch boilerplate for the **LTI 1.3 launch + token exchange** (Node or Python), plus a minimal “Create Classic Quiz + add questions + add to Module” sequence with the **exact REST calls** and recommended error handling. [1]: https://developerdocs.instructure.com/services/canvas/oauth2/file.oauth?utm_source=chatgpt.com "OAuth2 Overview" [2]: https://community.canvaslms.com/t5/Admin-Guide/How-do-I-add-a-developer-API-key-for-an-account/ta-p/259?utm_source=chatgpt.com "How do I add a developer API key for an account?" [3]: https://developerdocs.instructure.com/services/canvas/resources/api_token_scopes?utm_source=chatgpt.com "API Token Scopes" [4]: https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.tools_intro?utm_source=chatgpt.com "Introduction" [5]: https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.registration?utm_source=chatgpt.com "Registration" [6]: https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.provisioning?utm_source=chatgpt.com "Provisioning" [7]: https://developerdocs.instructure.com/services/canvas/external-tools/lti/file.assignment_tools?utm_source=chatgpt.com "Grading" [8]: https://www.imsglobal.org/spec/lti-ags/v2p0?utm_source=chatgpt.com "Learning Tools Interoperability Assignment and Grade ..." [9]: https://documentation.instructure.com/doc/api/quiz_submissions.html?utm_source=chatgpt.com "Quiz Submissions - Canvas LMS REST API Documentation" [10]: https://developerdocs.instructure.com/services/canvas/oauth2/file.developer_keys?utm_source=chatgpt.com "Developer Keys" [11]: https://community.canvaslms.com/t5/Canvas-Developers-Group/Using-API-endpoints-for-Quizzes/m-p/523333?utm_source=chatgpt.com "Solved: Using API endpoints for Quizzes - Canvas Community"