# Moodle

> Roster sync and gradebook write-back from Moodle.

Canonical: https://grout.app/developer/documentation/guides/moodle/

This guide assumes a small local plugin or an external service with access to Moodle's web services. It uses two Grout keys, one per job, as recommended in [API Keys & Scopes](/developer/documentation/setting-up/api-keys/).

## 1. Nightly roster sync

Scopes: `students:write`, `groups:write`, `faculty:read`.

:::steps
### Pull enrolments
Use `core_enrol_get_enrolled_users` per course to get users and roles.

### Provision missing students
For each learner without a stored Grout id call `POST /students` with `student_id` = Moodle user id and `personal_email` = their Moodle email. Store the returned `id` and `login_email` in a custom profile field or your plugin table.

### Mirror course groups
`POST /groups` once per Moodle course (name = course full name, `faculty_id` = the teacher's Grout login email), then `PATCH /groups/{id}` with the full `student_ids` list on every run.

### Suspend leavers
`POST /students/{id}/suspend` for users whose enrolment ended.
:::

## 2. Gradebook write-back

Scopes: `submissions:read`, `webhooks:write`.

Register an endpoint subscribed to `exam.graded`. On delivery:

1. Verify the signature ([Verify Signatures](/developer/documentation/webhooks/verify/)).
2. `GET /submissions/{submission_id}` for `score`, `max_score`, `feedback`.
3. Map `exam_id` → Moodle grade item, then call `core_grades_update_grades` (or your grade item's API) for the student.
4. If `resubmission_required` is `true`, set the grade to "needs revision" instead.

## Cron sizing

A 5 000-student institution syncs comfortably within the default 600 requests/minute using `POST /students/bulk` and `PATCH /groups/{id}` — roughly 30 bulk calls plus one call per course.
