Author: Klein Panic · @kleinpanic · MIT License
An OpenClaw skill that gives AI agents read-only access to Canvas LMS via the REST API. Fetch courses, assignments, grades, announcements, submissions, modules, and upcoming events.
- Read-only by design — write operations (POST/PUT/DELETE/PATCH) are blocked at the script level
- JSON output — all responses are clean JSON, ready for
jqpiping - Portable — works on Linux and macOS
- Zero config bloat — two env vars and you're done
- Content normalization — HTML entities and tags are stripped from text fields so AI agents receive clean, readable content
| Dependency | Purpose |
|---|---|
curl |
HTTP requests |
jq |
JSON filtering (used in announcements-all) |
python3 |
Response normalization |
- Log in to your Canvas instance
- Go to Account → Settings → New Access Token
- Give it a name, set an expiry (optional), click Generate Token
- Copy the token — you won't see it again
Add to your shell config (~/.zshrc, ~/.bashrc, etc.):
export CANVAS_TOKEN="your_token_here"
export CANVAS_BASE_URL="https://canvas.yourinstitution.edu"Or set them per-session:
export CANVAS_TOKEN="$(cat ~/.secret/canvas-token)"
export CANVAS_BASE_URL="https://canvas.example.edu"chmod +x ~/.openclaw/skills/canvas-lms/bin/canvas-apiThe skill auto-registers with OpenClaw. Reference the binary in agent configs:
CANVAS_API="$HOME/.openclaw/skills/canvas-lms/bin/canvas-api"Or add to your PATH:
export PATH="$HOME/.openclaw/skills/canvas-lms/bin:$PATH"canvas-api courses
canvas-api upcoming
canvas-api todo
canvas-api missing
canvas-api grades-all
canvas-api assignments <course_id>
canvas-api announcements <course_id>
canvas-api announcements-all
canvas-api grades [course_id]
canvas-api syllabus <course_id>
canvas-api files <course_id>
canvas-api submissions <course_id>
canvas-api modules <course_id>
canvas-api calendar
# Raw API path (anything not matching a shorthand)
canvas-api "/api/v1/courses?enrollment_state=active&per_page=100"export COURSE_ID=12345
canvas-api assignments # uses COURSE_ID automatically
canvas-api syllabus # same# Course list
canvas-api courses | jq '.[] | {id, name, course_code}'
# Assignments due this week with submission status
canvas-api assignments 12345 | jq '.[] | select(.due_at != null) | {name, due_at, submitted: .submission.submitted_at}'
# Missing submissions
canvas-api missing | jq '.[] | {name, course_id, due_at}'
# Current grade across all courses
canvas-api grades-all | jq '.[] | {course_id, score: .grades.current_score, grade: .grades.current_grade}'
# Recent announcements
canvas-api announcements-all | jq '.[] | {title, posted_at}'| Variable | Required | Description |
|---|---|---|
CANVAS_TOKEN |
✅ | Canvas API access token |
CANVAS_BASE_URL |
✅ | Canvas instance base URL (no trailing slash) |
COURSE_ID |
optional | Default course ID for shorthands that require one |
CANVAS_SANITIZE_DISABLE |
optional | Set to 1 to receive raw API output without normalization |
- Pagination: Canvas defaults to 10 results per page. Shorthands request up to 50-100. For very large courses, use raw paths with
?per_page=100. - Rate limits: Canvas enforces rate limits (usually ~3000 req/hr). The script doesn't retry automatically — add retry logic in your agent if needed.
- Token expiry: Tokens can expire. If you get
401, regenerate in Canvas Settings. - Cross-platform date:
announcements-allusesdate -d(Linux) withdate -v(macOS) fallback.
Issues and PRs welcome. Please keep contributions read-only in spirit — don't add write operations to this skill.
Klein Panic · kleinpanic.com · @kleinpanic
MIT — Copyright (c) 2026 Klein Panic — see LICENSE