Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to cr...
---
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
```bash
# 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`:
```bash
export MATON_API_KEY="YOUR_API_KEY"
```
### Getting Your API Key
1. Sign in or create an account at [maton.ai](https://maton.ai)
2. Go to [maton.ai/settings](https://maton.ai/settings)
3. Copy your API key
## Connection Management
Manage your Google Classroom OAuth connections at `https://api.maton.ai`.
### List Connections
```bash
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
```bash
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
```bash
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:**
```json
{
"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
```bash
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:
```bash
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
- Access is scoped to courses, assignments, students, teachers, and announcements within the connected Google Classroom account.
- **All write operations require explicit user approval.** Before executing any create, update, or delete call, confirm the target resource and intended effect with the user.
## API Reference
### Courses
#### List Courses
```bash
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:**
```json
{
"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
```bash
GET /v1/courses/{courseId}
```
#### Create Course
```bash
POST /v1/courses
Content-Type: application/json
{
"name": "Course Name",
"section": "Section A",
"descriptionHeading": "Course Title",
"description": "Course description",
"ownerId": "me"
}
```
**Response:**
```json
{
"id": "825637533405",
"name": "Course Name",
"section": "Section A",
"ownerId": "102753038276005039640",
"courseState": "PROVISIONED",
"enrollmentCode": "abc123"
}
```
#### Update Course
```bash
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
```bash
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
```bash
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
```bash
GET /v1/courses/{courseId}/courseWork/{courseWorkId}
```
#### Create Course Work
```bash
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
```bash
PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}?updateMask=title,description
Content-Type: application/json
{
"title": "Updated Title",
"description": "Updated description"
}
```
#### Delete Course Work
```bash
DELETE /v1/courses/{courseId}/courseWork/{courseWorkId}
```
### Student Submissions
#### List Student Submissions
```bash
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:**
```json
{
"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
```bash
GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}
```
#### Grade Submission
```bash
PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}?updateMask=assignedGrade,draftGrade
Content-Type: application/json
{
"assignedGrade": 95,
"draftGrade": 95
}
```
#### Return Submission
```bash
POST /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}:return
Content-Type: application/json
{}
```
### Teachers
#### List Teachers
```bash
GET /v1/courses/{courseId}/teachers
```
**Response:**
```json
{
"teachers": [
{
"courseId": "825635865485",
"userId": "102753038276005039640",
"profile": {
"id": "102753038276005039640",
"name": {
"givenName": "John",
"familyName": "Doe",
"fullName": "John Doe"
},
"emailAddress": "john.doe@example.com"
}
}
]
}
```
#### Get Teacher
```bash
GET /v1/courses/{courseId}/teachers/{userId}
```
#### Add Teacher
```bash
POST /v1/courses/{courseId}/teachers
Content-Type: application/json
{
"userId": "teacher@example.com"
}
```
#### Remove Teacher
```bash
DELETE /v1/courses/{courseId}/teachers/{userId}
```
### Students
#### List Students
```bash
GET /v1/courses/{courseId}/students
```
#### Get Student
```bash
GET /v1/courses/{courseId}/students/{userId}
```
#### Add Student
```bash
POST /v1/courses/{courseId}/students
Content-Type: application/json
{
"userId": "student@example.com"
}
```
#### Remove Student
```bash
DELETE /v1/courses/{courseId}/students/{userId}
```
### Announcements
#### List Announcements
```bash
GET /v1/courses/{courseId}/announcements
GET /v1/courses/{courseId}/announcements?announcementStates=PUBLISHED
```
#### Get Announcement
```bash
GET /v1/courses/{courseId}/announcements/{announcementId}
```
#### Create Announcement
```bash
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
```bash
PATCH /v1/courses/{courseId}/announcements/{announcementId}?updateMask=text
Content-Type: application/json
{
"text": "Updated announcement text"
}
```
#### Delete Announcement
```bash
DELETE /v1/courses/{courseId}/announcements/{announcementId}
```
### Topics
#### List Topics
```bash
GET /v1/courses/{courseId}/topics
```
#### Get Topic
```bash
GET /v1/courses/{courseId}/topics/{topicId}
```
#### Create Topic
```bash
POST /v1/courses/{courseId}/topics
Content-Type: application/json
{
"name": "Topic Name"
}
```
#### Update Topic
```bash
PATCH /v1/courses/{courseId}/topics/{topicId}?updateMask=name
Content-Type: application/json
{
"name": "Updated Topic Name"
}
```
#### Delete Topic
```bash
DELETE /v1/courses/{courseId}/topics/{topicId}
```
### Course Work Materials
#### List Course Work Materials
```bash
GET /v1/courses/{courseId}/courseWorkMaterials
```
#### Get Course Work Material
```bash
GET /v1/courses/{courseId}/courseWorkMaterials/{courseWorkMaterialId}
```
### Invitations
#### List Invitations
```bash
GET /v1/invitations?courseId={courseId}
GET /v1/invitations?userId=me
```
**Note:** Either `courseId` or `userId` is required.
#### Create Invitation
```bash
POST /v1/invitations
Content-Type: application/json
{
"courseId": "825635865485",
"userId": "user@example.com",
"role": "STUDENT"
}
```
**Roles:**
- `STUDENT`
- `TEACHER`
- `OWNER`
#### Accept Invitation
```bash
POST /v1/invitations/{invitationId}:accept
```
#### Delete Invitation
```bash
DELETE /v1/invitations/{invitationId}
```
### User Profiles
#### Get Current User
```bash
GET /v1/userProfiles/me
```
**Response:**
```json
{
"id": "102753038276005039640",
"name": {
"givenName": "John",
"familyName": "Doe",
"fullName": "John Doe"
},
"emailAddress": "john.doe@example.com",
"permissions": [
{
"permission": "CREATE_COURSE"
}
],
"verifiedTeacher": false
}
```
#### Get User Profile
```bash
GET /v1/userProfiles/{userId}
```
### Course Aliases
#### List Course Aliases
```bash
GET /v1/courses/{courseId}/aliases
```
## Pagination
The Google Classroom API uses token-based pagination. Responses include a `nextPageToken` when more results are available.
```bash
GET /v1/courses?pageSize=10
```
**Response:**
```json
{
"courses": [...],
"nextPageToken": "Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI..."
}
```
To get the next page:
```bash
GET /v1/courses?pageSize=10&pageToken=Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI...
```
## Code Examples
### JavaScript
```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
```python
import os
import requests
# List 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
```python
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://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 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
## Error 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)**
- 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
## Resources
- [Google Classroom API Documentation](https://developers.google.com/workspace/classroom/reference/rest)
- [Course Resource Reference](https://developers.google.com/workspace/classroom/reference/rest/v1/courses)
- [CourseWork Resource Reference](https://developers.google.com/workspace/classroom/reference/rest/v1/courses.courseWork)
- [StudentSubmissions Reference](https://developers.google.com/workspace/classroom/reference/rest/v1/courses.courseWork.studentSubmissions)
- [Maton Community](https://discord.com/invite/dBfFAcefs2)
- [Maton Support](mailto:support@maton.ai)
don't have the plugin yet? install it then click "run inline in claude" again.