---
name: leoyeai/google-classroom
source: https://app.decimal.ai/s/leoyeai-google-classroom@1/SKILL.md
source_sha256: a36b65482f3b
---

# 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://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
```

## Base URL

```
https://gateway.maton.ai/google-classroom/{api-path}
```

The Google Classroom API uses the path pattern:
```
https://gateway.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://ctrl.maton.ai`.

### List Connections

```bash
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
```

### Create Connection

```bash
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
```

### Get Connection

```bash
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:**
```json
{
  "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.

### Delete Connection

```bash
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
```

### 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://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.

## 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://gateway.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://gateway.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://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())
```

## 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)