Google Classroom
by @byungkyu
Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to cr...
clawhub install google-classroomπ About This Skill
name: google-classroom description: | Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to create courses, manage coursework, track student submissions, or post announcements. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key. metadata: author: maton version: "1.0" clawdbot: emoji: π§ homepage: "https://maton.ai" requires: env: - MATON_API_KEY
Google Classroom
Access the Google Classroom API with managed OAuth authentication. Manage courses, coursework, students, teachers, announcements, and submissions.
Quick Start
# List all courses
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
Base URL
https://api.maton.ai/google-classroom/{api-path}
The Google Classroom API uses the path pattern:
https://api.maton.ai/google-classroom/v1/{resource}
Authentication
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"
Getting Your API Key
1. Sign in or create an account at maton.ai 2. Go to maton.ai/settings 3. Copy your API key
Connection Management
Manage your Google Classroom OAuth connections at https://api.maton.ai.
List Connections
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
Create Connection
python3 <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'google-classroom'}).encode()
req = urllib.request.Request('https://api.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
Get Connection
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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": "{connection_id}",
"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.
Delete Connection
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
Specifying Connection
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://api.maton.ai/google-classroom/v1/courses')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If you have multiple connections, always include this header to ensure requests go to the intended account.
Security & Permissions
API Reference
Courses
#### 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, SUSPENDEDteacherId - 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 pageResponse:
{
"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".
Course Work (Assignments)
#### 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, DELETEDorderBy - Sort by: dueDate, updateTimepageSize - Number of results per pagepageToken - 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 assignmentSHORT_ANSWER_QUESTION - Short answer questionMULTIPLE_CHOICE_QUESTION - Multiple choice questionStates:
DRAFT - Not visible to studentsPUBLISHED - 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}
Student Submissions
#### 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_STUDENTuserId - Filter by student IDpageSize - Number of results per pagepageToken - Token for next pageNote: 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{}
Teachers
#### 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}
Students
#### 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}
Announcements
#### 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 studentsPUBLISHED - 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}
Topics
#### 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}
Course Work Materials
#### List Course Work Materials
GET /v1/courses/{courseId}/courseWorkMaterials
#### Get Course Work Material
GET /v1/courses/{courseId}/courseWorkMaterials/{courseWorkMaterialId}
Invitations
#### 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:
STUDENTTEACHEROWNER#### Accept Invitation
POST /v1/invitations/{invitationId}:accept
#### Delete Invitation
DELETE /v1/invitations/{invitationId}
User Profiles
#### 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}
Course Aliases
#### List Course Aliases
GET /v1/courses/{courseId}/aliases
Pagination
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...
Code Examples
JavaScript
// List all courses
const response = await fetch(
'https://api.maton.ai/google-classroom/v1/courses',
{
headers: {
'Authorization': Bearer ${process.env.MATON_API_KEY}
}
}
);
const data = await response.json();
console.log(data.courses);
Python
import os
import requestsList all courses
response = requests.get(
'https://api.maton.ai/google-classroom/v1/courses',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
print(data['courses'])
Create Assignment Example
import os
import requestscourse_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://api.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())
Notes
updateMask query parameter specifying which fields to updatecourseState: "ARCHIVED") before they can be deletedPUBLISHED state to access student submissionsme to refer to the current authenticated user{year, month, day} format; times use {hours, minutes} formatjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environmentsError Handling
| Status | Meaning | |--------|---------| | 400 | Bad request, invalid argument, or precondition failed | | 401 | Invalid API key or expired token | | 403 | Permission denied | | 404 | Resource not found | | 409 | Conflict (e.g., user already enrolled) | | 429 | Rate limited | | 4xx/5xx | Passthrough error from Google Classroom API |
Common Errors
Precondition check failed (400)
Permission denied (403)
Resources
π‘ Examples
# List all courses
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
π Tips & Best Practices
updateMask query parameter specifying which fields to updatecourseState: "ARCHIVED") before they can be deletedPUBLISHED state to access student submissionsme to refer to the current authenticated user{year, month, day} format; times use {hours, minutes} formatjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments