Requirements
- Target platform
- OpenClaw
- Install method
- Manual import
- Extraction
- Extract archive
- Prerequisites
- OpenClaw
- Primary doc
- SKILL.md
Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to cr...
Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to cr...
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete.
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run.
Access the Google Classroom API with managed OAuth authentication. Manage courses, coursework, students, teachers, announcements, and submissions.
# List all courses python3 <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/google-classroom/v1/courses') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
https://gateway.maton.ai/google-classroom/{api-path} The Google Classroom API uses the path pattern: https://gateway.maton.ai/google-classroom/v1/{resource}
All requests require the Maton API key in the Authorization header: Authorization: Bearer $MATON_API_KEY Environment Variable: Set your API key as MATON_API_KEY: export MATON_API_KEY="YOUR_API_KEY"
Sign in or create an account at maton.ai Go to maton.ai/settings Copy your API key
Manage your Google Classroom OAuth connections at https://ctrl.maton.ai.
python3 <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections?app=google-classroom&status=ACTIVE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
python3 <<'EOF' import urllib.request, os, json data = json.dumps({'app': 'google-classroom'}).encode() req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Content-Type', 'application/json') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
python3 <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF Response: { "connection": { "connection_id": "8efa1361-0e86-40b1-a63b-53a5051f8ac6", "status": "ACTIVE", "creation_time": "2026-02-14T00:00:00.000000Z", "last_updated_time": "2026-02-14T00:00:00.000000Z", "url": "https://connect.maton.ai/?session_token=...", "app": "google-classroom", "metadata": {} } } Open the returned url in a browser to complete OAuth authorization.
python3 <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF
If you have multiple Google Classroom connections, specify which one to use with the Maton-Connection header: python3 <<'EOF' import urllib.request, os, json req = urllib.request.Request('https://gateway.maton.ai/google-classroom/v1/courses') req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}') req.add_header('Maton-Connection', '8efa1361-0e86-40b1-a63b-53a5051f8ac6') print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2)) EOF If omitted, the gateway uses the default (oldest) active connection.
List Courses GET /v1/courses GET /v1/courses?courseStates=ACTIVE GET /v1/courses?teacherId=me GET /v1/courses?studentId=me GET /v1/courses?pageSize=10 Query Parameters: courseStates - Filter by state: ACTIVE, ARCHIVED, PROVISIONED, DECLINED, SUSPENDED teacherId - Filter by teacher ID (use me for current user) studentId - Filter by student ID (use me for current user) pageSize - Number of results per page (max 100) pageToken - Token for next page Response: { "courses": [ { "id": "825635865485", "name": "Introduction to Programming", "section": "Section A", "descriptionHeading": "CS 101", "description": "Learn the basics of programming", "ownerId": "102753038276005039640", "creationTime": "2026-02-14T01:53:58.991Z", "updateTime": "2026-02-14T01:53:58.991Z", "enrollmentCode": "3qsua37m", "courseState": "ACTIVE", "alternateLink": "https://classroom.google.com/c/ODI1NjM1ODY1NDg1", "guardiansEnabled": false } ], "nextPageToken": "..." } Get Course GET /v1/courses/{courseId} Create Course POST /v1/courses Content-Type: application/json { "name": "Course Name", "section": "Section A", "descriptionHeading": "Course Title", "description": "Course description", "ownerId": "me" } Response: { "id": "825637533405", "name": "Course Name", "section": "Section A", "ownerId": "102753038276005039640", "courseState": "PROVISIONED", "enrollmentCode": "abc123" } Update Course PATCH /v1/courses/{courseId}?updateMask=name,description Content-Type: application/json { "name": "Updated Course Name", "description": "Updated description" } Note: Use updateMask query parameter to specify which fields to update. Delete Course DELETE /v1/courses/{courseId} Note: Courses must be archived before deletion. To archive, update the course with courseState: "ARCHIVED".
List Course Work GET /v1/courses/{courseId}/courseWork GET /v1/courses/{courseId}/courseWork?courseWorkStates=PUBLISHED GET /v1/courses/{courseId}/courseWork?orderBy=dueDate Query Parameters: courseWorkStates - Filter by state: PUBLISHED, DRAFT, DELETED orderBy - Sort by: dueDate, updateTime pageSize - Number of results per page pageToken - Token for next page Get Course Work GET /v1/courses/{courseId}/courseWork/{courseWorkId} Create Course Work POST /v1/courses/{courseId}/courseWork Content-Type: application/json { "title": "Assignment Title", "description": "Assignment description", "workType": "ASSIGNMENT", "state": "PUBLISHED", "maxPoints": 100, "dueDate": { "year": 2026, "month": 3, "day": 15 }, "dueTime": { "hours": 23, "minutes": 59 } } Work Types: ASSIGNMENT - Regular assignment SHORT_ANSWER_QUESTION - Short answer question MULTIPLE_CHOICE_QUESTION - Multiple choice question States: DRAFT - Not visible to students PUBLISHED - Visible to students Update Course Work PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}?updateMask=title,description Content-Type: application/json { "title": "Updated Title", "description": "Updated description" } Delete Course Work DELETE /v1/courses/{courseId}/courseWork/{courseWorkId}
List Student Submissions GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions?states=TURNED_IN Query Parameters: states - Filter by state: NEW, CREATED, TURNED_IN, RETURNED, RECLAIMED_BY_STUDENT userId - Filter by student ID pageSize - Number of results per page pageToken - Token for next page Note: Course work must be in PUBLISHED state to list submissions. Response: { "studentSubmissions": [ { "courseId": "825635865485", "courseWorkId": "825637404958", "id": "Cg4I8ufNwwYQ7tSZgYIB", "userId": "102753038276005039640", "creationTime": "2026-02-14T02:30:00.000Z", "state": "NEW", "alternateLink": "https://classroom.google.com/..." } ] } Get Student Submission GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId} Grade Submission PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}?updateMask=assignedGrade,draftGrade Content-Type: application/json { "assignedGrade": 95, "draftGrade": 95 } Return Submission POST /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}:return Content-Type: application/json {}
List Teachers GET /v1/courses/{courseId}/teachers Response: { "teachers": [ { "courseId": "825635865485", "userId": "102753038276005039640", "profile": { "id": "102753038276005039640", "name": { "givenName": "John", "familyName": "Doe", "fullName": "John Doe" }, "emailAddress": "john.doe@example.com" } } ] } Get Teacher GET /v1/courses/{courseId}/teachers/{userId} Add Teacher POST /v1/courses/{courseId}/teachers Content-Type: application/json { "userId": "teacher@example.com" } Remove Teacher DELETE /v1/courses/{courseId}/teachers/{userId}
List Students GET /v1/courses/{courseId}/students Get Student GET /v1/courses/{courseId}/students/{userId} Add Student POST /v1/courses/{courseId}/students Content-Type: application/json { "userId": "student@example.com" } Remove Student DELETE /v1/courses/{courseId}/students/{userId}
List Announcements GET /v1/courses/{courseId}/announcements GET /v1/courses/{courseId}/announcements?announcementStates=PUBLISHED Get Announcement GET /v1/courses/{courseId}/announcements/{announcementId} Create Announcement POST /v1/courses/{courseId}/announcements Content-Type: application/json { "text": "Announcement text content", "state": "PUBLISHED" } States: DRAFT - Not visible to students PUBLISHED - Visible to students Update Announcement PATCH /v1/courses/{courseId}/announcements/{announcementId}?updateMask=text Content-Type: application/json { "text": "Updated announcement text" } Delete Announcement DELETE /v1/courses/{courseId}/announcements/{announcementId}
List Topics GET /v1/courses/{courseId}/topics Get Topic GET /v1/courses/{courseId}/topics/{topicId} Create Topic POST /v1/courses/{courseId}/topics Content-Type: application/json { "name": "Topic Name" } Update Topic PATCH /v1/courses/{courseId}/topics/{topicId}?updateMask=name Content-Type: application/json { "name": "Updated Topic Name" } Delete Topic DELETE /v1/courses/{courseId}/topics/{topicId}
List Course Work Materials GET /v1/courses/{courseId}/courseWorkMaterials Get Course Work Material GET /v1/courses/{courseId}/courseWorkMaterials/{courseWorkMaterialId}
List Invitations GET /v1/invitations?courseId={courseId} GET /v1/invitations?userId=me Note: Either courseId or userId is required. Create Invitation POST /v1/invitations Content-Type: application/json { "courseId": "825635865485", "userId": "user@example.com", "role": "STUDENT" } Roles: STUDENT TEACHER OWNER Accept Invitation POST /v1/invitations/{invitationId}:accept Delete Invitation DELETE /v1/invitations/{invitationId}
Get Current User GET /v1/userProfiles/me Response: { "id": "102753038276005039640", "name": { "givenName": "John", "familyName": "Doe", "fullName": "John Doe" }, "emailAddress": "john.doe@example.com", "permissions": [ { "permission": "CREATE_COURSE" } ], "verifiedTeacher": false } Get User Profile GET /v1/userProfiles/{userId}
List Course Aliases GET /v1/courses/{courseId}/aliases
The Google Classroom API uses token-based pagination. Responses include a nextPageToken when more results are available. GET /v1/courses?pageSize=10 Response: { "courses": [...], "nextPageToken": "Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI..." } To get the next page: GET /v1/courses?pageSize=10&pageToken=Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI...
// List all courses const response = await fetch( 'https://gateway.maton.ai/google-classroom/v1/courses', { headers: { 'Authorization': `Bearer ${process.env.MATON_API_KEY}` } } ); const data = await response.json(); console.log(data.courses);
import os import requests # List all courses response = requests.get( 'https://gateway.maton.ai/google-classroom/v1/courses', headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'} ) data = response.json() print(data['courses'])
import os import requests course_id = "825635865485" # Create an assignment assignment = { "title": "Week 1 Homework", "description": "Complete exercises 1-10", "workType": "ASSIGNMENT", "state": "PUBLISHED", "maxPoints": 100, "dueDate": {"year": 2026, "month": 3, "day": 15}, "dueTime": {"hours": 23, "minutes": 59} } response = requests.post( f'https://gateway.maton.ai/google-classroom/v1/courses/{course_id}/courseWork', headers={ 'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}', 'Content-Type': 'application/json' }, json=assignment ) print(response.json())
updateMask Required: PATCH requests require the updateMask query parameter specifying which fields to update Course Deletion: Courses must be archived (courseState: "ARCHIVED") before they can be deleted Student Submissions: Course work must be in PUBLISHED state to access student submissions User IDs: Use me to refer to the current authenticated user Timestamps: Dates use {year, month, day} format; times use {hours, minutes} format IMPORTANT: When piping curl output to jq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments
StatusMeaning400Bad request, invalid argument, or precondition failed401Invalid API key or expired token403Permission denied404Resource not found409Conflict (e.g., user already enrolled)429Rate limited4xx/5xxPassthrough error from Google Classroom API
Precondition check failed (400) When deleting a course: Course must be archived first When listing submissions: Course work must be published Permission denied (403) User doesn't have required role (teacher/owner) for the operation Attempting to access guardian information without proper scopes
Google Classroom API Documentation Course Resource Reference CourseWork Resource Reference StudentSubmissions Reference Maton Community Maton Support
Code helpers, APIs, CLIs, browser automation, testing, and developer operations.
Largest current source with strong distribution and engagement signals.