Connect Retool to Moodle by creating a REST API Resource pointing to your Moodle site's Web Services endpoint, authenticated with a user token and using Moodle's function-based API (e.g., core_user_get_users, mod_assign_get_assignments). Build LMS administration dashboards that surface user enrollment data, course completion rates, grade reports, and assignment submissions — giving institutional administrators a consolidated operations view beyond Moodle's native reports.
| Fact | Value |
|---|---|
| Tool | Moodle |
| Category | Other |
| Method | REST API Resource |
| Difficulty | Intermediate |
| Time required | 25 minutes |
| Last updated | April 2026 |
Why Connect Retool to Moodle?
Moodle administrators at universities, schools, and corporate training departments manage large LMS deployments with hundreds of courses and thousands of users. Moodle's built-in reports are functional but fixed — they don't easily support custom aggregations, cross-course comparisons, or combining LMS data with external systems like student information systems (SIS) or HR platforms. Connecting Moodle to Retool enables building exactly the institutional dashboards that administrators need.
Common use cases include course completion dashboards that show completion rates across all courses in a department, identifying which courses have high incompletion rates that may indicate content or engagement problems. User management panels let administrators search, view, and bulk-update users faster than Moodle's native UI allows, especially for large user bases with thousands of enrolled students. Grade report panels pull Moodle gradebook data into sortable, filterable tables that academic coordinators can use for reporting without needing full Moodle admin access.
Moodle's Web Services API has a unique architecture: rather than RESTful resource-based endpoints, it uses a single endpoint URL with a 'wsfunction' parameter that specifies which built-in function to call. Functions cover all major Moodle data types including users, courses, enrollments, grades, assignments, forums, and quizzes. Enabling and configuring Web Services requires Moodle administrator access, and the token is scoped to a service that defines which functions are available.
Integration method
Retool connects to Moodle through a REST API Resource targeting Moodle's Web Services REST endpoint (your-moodle-site.com/webservice/rest/server.php). Authentication uses a Moodle Web Services token tied to a specific user account and service. Unlike REST APIs with separate endpoints per resource, Moodle uses a function-based approach: all requests go to the same endpoint URL with a 'wsfunction' parameter specifying which operation to call. All requests are proxied server-side through Retool's backend, keeping Moodle tokens off the browser.
Prerequisites
- A Moodle site with administrator access to enable and configure Web Services — this is in Site Administration → Server → Web services
- Moodle Web Services enabled with a REST protocol enabled — navigate to Site Administration → Server → Web services → Manage protocols and enable REST
- A Moodle Web Services external service created with the specific functions your dashboards require — this defines which wsfunction calls the token can make
- A Moodle Web Services token generated for a user account with appropriate role and permission (Site Administration → Server → Web services → Manage tokens → Add)
- Your Moodle site's base URL (e.g., https://moodle.youruniversity.edu) — the Web Services endpoint will be at {base_url}/webservice/rest/server.php
Step-by-step guide
Enable Moodle Web Services and configure an external service
Moodle Web Services must be explicitly enabled and configured before generating tokens. Log into your Moodle site as an administrator and navigate to Site Administration → Server → Web services → Overview — this page shows all the setup steps in sequence. Step 1 — Enable Web Services: Go to Site Administration → Advanced features and tick 'Enable web services'. Save changes. Step 2 — Enable REST protocol: Go to Site Administration → Server → Web services → Manage protocols. Enable the REST protocol by clicking the eye icon next to REST. The REST protocol is what allows HTTP-based API calls from Retool. Step 3 — Create an external service: Go to Site Administration → Server → Web services → External services → Add. Name the service 'Retool Integration', tick 'Enabled' and 'Authorised users only', and save. You will be redirected to the service's Functions page — add the specific functions your dashboards need. For a comprehensive admin dashboard, add: core_user_get_users, core_course_get_courses, core_enrol_get_enrolled_users, gradereport_user_get_grade_items, mod_assign_get_assignments, mod_assign_get_submissions. Step 4 — Create a token: Go to Site Administration → Server → Web services → Manage tokens → Add a token. Select the user (create a dedicated service account user rather than using your admin account), select the 'Retool Integration' service, and optionally set an IP restriction. Click Save — the generated token is displayed once. Copy and store it securely. Note the exact functions you enabled in the service — only these functions will work with your token. If you encounter 'Access control exception' errors on a wsfunction, add that function to the service.
Pro tip: Create a dedicated Moodle user account with the minimum necessary role (e.g., Moodle Manager role without student data editing capability) for the Web Services token rather than using an admin account. This follows the principle of least privilege and makes it easy to revoke API access without affecting admin operations.
Expected result: Web Services are enabled in Moodle, the REST protocol is active, an external service named 'Retool Integration' exists with the required functions, and a token has been generated for the service account user. The token string is copied and stored securely.
Create the Moodle Web Services REST API Resource in Retool
Moodle's Web Services API has a unique structure that differs from typical REST APIs. All requests go to a single endpoint URL, and the specific operation is determined by the 'wsfunction' URL parameter. The token is also passed as a URL parameter (wstoken) or in the request body. Navigate to the Resources tab in Retool and click Add Resource. Select REST API from the resource type list. Configure the resource: - Name: 'Moodle Web Services' - Base URL: https://your-moodle-site.com/webservice/rest/server.php For authentication, select No Authentication from the Auth dropdown — Moodle's token authentication uses a URL parameter rather than standard HTTP auth headers. Add default URL parameters that will be included in every request: - Key: wstoken, Value: YOUR_MOODLE_TOKEN (or better: {{ retoolContext.configVars.MOODLE_TOKEN }}) - Key: moodlewsrestformat, Value: json The moodlewsrestformat=json parameter tells Moodle to return JSON instead of XML — always include this. Click Save Changes. Store the Moodle token in a Retool Configuration Variable (Settings → Configuration Variables) named MOODLE_TOKEN, marked as secret. Update the wstoken default parameter to reference it: {{ retoolContext.configVars.MOODLE_TOKEN }}. Verify by creating a test query: - Method: GET - Path: (leave empty — the base URL is the full endpoint) - URL Parameters: wsfunction: core_webservice_get_site_info Run the query. A successful response returns your Moodle site's name, version, and information about the authenticated user, confirming the token and service configuration are correct.
Pro tip: Moodle Web Services functions that require POST bodies (like creating users or enrolling students) expect parameters in application/x-www-form-urlencoded format, not JSON. For read-only GET queries using wsfunction parameters, GET requests with URL parameters work correctly. Check the Moodle Web Services documentation for each specific function's expected request method and parameter format.
Expected result: The Moodle Web Services REST API Resource is configured with the wstoken and moodlewsrestformat=json default parameters. A test query to core_webservice_get_site_info returns Moodle site information, confirming the resource and token are correctly configured.
Query users and build a user management table
The core_user_get_users Web Services function searches Moodle users by various criteria (name, email, username, etc.) and returns user profile data. This is the primary function for building user management panels. Create a new query using the Moodle Web Services resource: - Method: GET - Path: (empty) - URL Parameters: - wsfunction: core_user_get_users - criteria[0][key]: email (or 'fullname', 'username', 'id') - criteria[0][value]: {{ userSearchInput.value || '%' }} (% as wildcard for all users) Moodle's function parameter convention is unusual — array parameters are passed as criteria[0][key], criteria[0][value], criteria[1][key], criteria[1][value] for multiple criteria. In Retool's URL Parameters, each line is a separate key-value pair, so add them as individual lines. The response returns a users array with id, username, fullname, email, firstaccess, lastaccess, lastlogin, enrolled_courses, and other profile fields. Write a JavaScript transformer that formats the response, converts Unix timestamps to readable dates (Moodle uses Unix timestamps throughout its API), and adds a 'days since last login' computed field. Bind to a Table component with columns for full name, username, email, last login, and course count.
1// JavaScript transformer for Moodle core_user_get_users response2const users = data?.users || [];34function formatMoodleDate(unixTimestamp) {5 if (!unixTimestamp) return 'Never';6 return new Date(unixTimestamp * 1000).toLocaleDateString('en-US', {7 year: 'numeric', month: 'short', day: 'numeric'8 });9}1011return users.map(user => {12 const lastLoginTs = user.lastlogin || user.lastaccess || 0;13 const daysSinceLogin = lastLoginTs > 014 ? Math.floor((Date.now() - lastLoginTs * 1000) / (1000 * 60 * 60 * 24))15 : null;1617 return {18 id: user.id,19 username: user.username,20 fullname: user.fullname || `${user.firstname || ''} ${user.lastname || ''}`.trim(),21 email: user.email || '',22 first_access: formatMoodleDate(user.firstaccess),23 last_login: formatMoodleDate(lastLoginTs),24 days_since_login: daysSinceLogin,25 department: user.department || '',26 institution: user.institution || '',27 suspended: user.suspended ? 'Suspended' : 'Active'28 };29});Pro tip: Moodle uses Unix timestamps (seconds since epoch) for all date fields — not milliseconds like JavaScript's Date.now(). When converting Moodle timestamps, multiply by 1000 before passing to new Date(): new Date(moodleTimestamp * 1000). Forgetting this multiplier results in dates from 1970.
Expected result: A searchable Table displays Moodle users with formatted dates, computed days-since-login, and account status. Entering text in the search input triggers the query with the user's input as the search criteria. Clicking a user row shows more details in a side panel.
Query course enrollment data and build a completion dashboard
Build the course overview component of your LMS dashboard using core_course_get_courses to list courses and core_enrol_get_enrolled_users to get enrollment counts per course. Create a query for course data: - Method: GET - URL Parameters: - wsfunction: core_course_get_courses - options[ids][]: (leave empty to get all courses, or specify IDs) The response returns a courses array with id, fullname, shortname, categoryid, categoryname, startdate, enddate, timecreated, numsections, and a visible field. Write a transformer that formats the dates and filters out hidden courses. For enrollment counts, create a second query for a selected course: - wsfunction: core_enrol_get_enrolled_users - courseid: {{ coursesTable.selectedRow?.id }} - options[userfields][]: id,fullname,email This function returns all enrolled users for the selected course. Use a JavaScript transformer to count total enrollments and create a per-course enrollment summary. For completion data, use the completion_get_course_completion_status function (if installed and configured in Moodle) or the core_completion_get_activities_completion_status function to get per-user completion status for a course. Combine the enrollment count and completion count into a percentage. Display a courses Table with enrollment count and completion percentage, plus a Stat component row showing aggregate metrics: total active courses, total enrolled users across all courses, and average completion rate.
1// JavaScript transformer for Moodle course list response2const courses = data || [];34function formatMoodleDate(unixTs) {5 if (!unixTs || unixTs === 0) return 'Not set';6 return new Date(unixTs * 1000).toLocaleDateString('en-US', {7 year: 'numeric', month: 'short', day: 'numeric'8 });9}1011return courses12 .filter(course => course.visible === 1 && course.id !== 1) // exclude site home (id=1)13 .map(course => ({14 id: course.id,15 fullname: course.fullname || 'Untitled Course',16 shortname: course.shortname || '',17 category: course.categoryname || 'Uncategorized',18 start_date: formatMoodleDate(course.startdate),19 end_date: formatMoodleDate(course.enddate),20 sections: course.numsections || 0,21 is_visible: course.visible === 1 ? 'Visible' : 'Hidden',22 time_created: formatMoodleDate(course.timecreated)23 }));Pro tip: The core_course_get_courses function without an options[ids][] filter returns ALL courses including system courses. Filter out the 'Site home' course (id=1) and hidden courses (visible=0) in your transformer to show only meaningful course data in the admin dashboard.
Expected result: A course Table displays all active Moodle courses with formatted start dates, category names, and section counts. Selecting a course row triggers the enrollment query and shows enrolled user count. Stat components show aggregate totals across all courses.
Query grade reports and build a grades overview panel
Grade data in Moodle is accessed through the gradereport_user_get_grade_items function, which returns grade items and scores for a specific user in a course. For an overview panel, you query grades per course and display aggregated statistics. Create a grade query: - Method: GET - URL Parameters: - wsfunction: gradereport_user_get_grade_items - courseid: {{ courseSelect.value }} - userid: {{ userSelect.value }} (optional — omit for course overview) The response returns a usergrades array, where each entry contains a userid, userfullname, and a gradeitems array with individual grade item names, grades, maximum grades, and percentage scores. For a course-level grade overview (not per-user), use mod_assign_get_submissions combined with mod_assign_get_grades to get assignment-level grade distributions. These require the assignment module to be enabled and configured. Write a transformer that calculates per-grade-item statistics: average grade, percentage of users who completed (received a grade), and minimum/maximum grades. Display a Table with grade item name, max possible, average achieved, and completion rate. For a combined view, add a search/select component for course and user, and display that specific user's grade report alongside the class average for context. Course administrators use this to quickly check individual student performance against class averages during advising sessions.
1// JavaScript transformer for Moodle gradereport response2// Returns per-grade-item statistics for a course3const userGrades = data?.usergrades || [];45if (userGrades.length === 0) return [];67// Collect all grade items from all users8const gradeItemMap = {};910userGrades.forEach(userEntry => {11 const items = userEntry.gradeitems || [];12 items.forEach(item => {13 if (!gradeItemMap[item.id]) {14 gradeItemMap[item.id] = {15 id: item.id,16 item_name: item.itemname || 'Course Total',17 item_type: item.itemtype || 'category',18 max_grade: parseFloat(item.grademax || 100),19 grades: []20 };21 }22 if (item.graderaw !== null && item.graderaw !== undefined) {23 gradeItemMap[item.id].grades.push(parseFloat(item.graderaw));24 }25 });26});2728return Object.values(gradeItemMap)29 .filter(item => item.item_type === 'mod' || item.item_type === 'category')30 .map(item => {31 const grades = item.grades.filter(g => !isNaN(g));32 const avg = grades.length > 033 ? grades.reduce((s, g) => s + g, 0) / grades.length34 : null;35 return {36 item_name: item.item_name,37 max_grade: item.max_grade,38 graded_count: grades.length,39 average_grade: avg !== null ? avg.toFixed(1) : 'N/A',40 average_percent: avg !== null && item.max_grade > 041 ? ((avg / item.max_grade) * 100).toFixed(0) + '%' : 'N/A',42 completion_rate: userGrades.length > 043 ? ((grades.length / userGrades.length) * 100).toFixed(0) + '%' : '0%'44 };45 });Pro tip: Moodle grade values are returned as raw numeric grades (graderaw) alongside formatted display grades (gradeformatted). Use graderaw for calculations and gradeformatted for display in the Table — the formatted version respects the grade display type configured in the Moodle gradebook (percentage, letter, etc.).
Expected result: A grade overview Table shows all grade items in the selected course with average grades, completion rates, and score distributions. Selecting a specific user from a dropdown shows that user's grades alongside the class average for comparison.
Common use cases
Build a course enrollment and completion dashboard
Create a Retool panel that queries Moodle for all courses in a specific category, along with their enrollment counts and completion percentages. Administrators use this to identify courses with low completion rates, track overall LMS engagement, and generate reports for institutional leadership. The dashboard includes course name, category, enrolled count, completion percentage, and last modified date.
Build a course analytics dashboard that queries Moodle's core_course_get_courses function for all courses in a specified category ID. For each course, fetch enrollment counts using core_enrol_get_enrolled_users and compute completion rates. Display in a Table with course name, category, enrolled count, and completion percentage. Add a Chart showing completion rate distribution across all courses.
Copy this prompt to try it in Retool
Create a user management and enrollment panel
Build a Retool user management panel that lets administrators search Moodle users by name, email, or department, view their enrolled courses and completion status, and manually enroll or unenroll users from courses. This panel provides a faster interface for bulk user management operations than Moodle's native admin UI, particularly useful for institutional changes affecting large groups of students.
Create a user management panel with a search input for searching by name or email using core_user_get_users. Display results in a Table with username, full name, email, last login date, and number of enrolled courses. When a user row is selected, show a detail panel with their enrolled courses and completion status. Add an enroll button that triggers enrol_manual_enrol_users with the selected user and a target course dropdown.
Copy this prompt to try it in Retool
Build an assignment submission monitoring panel
Create a Retool panel for course administrators to monitor assignment submission status across a course. The panel shows all assignments in a selected course, the number of submissions received vs expected (enrollment count), the number of graded vs ungraded submissions, and average grades where available. Course coordinators use this to track grading workload and identify assignments with low submission rates.
Build an assignment tracker that accepts a course ID input. Query Moodle's mod_assign_get_assignments to list all assignments in the course. For each assignment, call mod_assign_get_submissions to count total submissions, graded submissions, and ungraded submissions. Display a Table with assignment name, due date, total submitted, graded count, and average grade. Highlight assignments with ungraded submissions past the due date.
Copy this prompt to try it in Retool
Troubleshooting
All Moodle API requests return 'Access control exception. Sorry, you are not allowed to make this call.' error
Cause: The wsfunction being called is not included in the external service that the token is associated with. Moodle strictly enforces function-level authorization through its external service configuration.
Solution: Go to Moodle Site Administration → Server → Web services → External services → Edit the 'Retool Integration' service → Functions tab. Add the specific wsfunction name that is returning the error. Only functions explicitly added to the service are callable with that service's tokens.
Moodle API returns date values that appear as very large numbers (Unix timestamps) and the transformer shows dates from 1970 or the far future
Cause: Moodle returns all timestamps as Unix timestamps in seconds (not milliseconds). JavaScript's Date constructor expects milliseconds, so passing the raw Moodle timestamp gives a date from 1970, and multiplying by 1000 then provides the correct date.
Solution: Multiply all Moodle timestamp values by 1000 before passing them to JavaScript's Date constructor: new Date(moodleTimestamp * 1000). Add a check for zero or null timestamps (Moodle uses 0 to represent 'not set'): if (!timestamp || timestamp === 0) return 'Not set'; else return new Date(timestamp * 1000).toLocaleDateString().
1// Correct Moodle timestamp conversion2function formatMoodleDate(unixTs) {3 if (!unixTs || unixTs === 0) return 'Not set';4 return new Date(unixTs * 1000).toLocaleDateString();5}Array-based Moodle function parameters (like criteria[0][key]) are not working correctly in Retool's URL Parameters panel
Cause: Moodle's function parameters use a PHP array notation (criteria[0][key]) that Retool's URL parameter encoding may handle differently than Moodle expects.
Solution: In Retool's query editor, add each array parameter as a separate URL parameter entry with the full bracket notation as the key: criteria[0][key] = email and criteria[0][value] = user@example.com as separate rows in the URL Parameters table. If this doesn't work, switch to a POST request and use form-encoded body with the same parameter structure.
1// For Retool URL Parameters table, add separate rows:2// Row 1: Key = criteria[0][key] Value = email3// Row 2: Key = criteria[0][value] Value = {{ searchInput.value }}core_course_get_courses returns hundreds of results including hidden and system courses
Cause: Without filtering, core_course_get_courses returns all courses in the Moodle instance including the site home course (id=1) and any courses with visible=0.
Solution: Filter the results in a JavaScript transformer: exclude courses where id === 1 (site home) and where visible !== 1 (hidden courses). Use options[ids][] parameters to request specific course IDs if you know which courses you want. For department-specific dashboards, query by category ID using core_course_get_categories and then filter courses by categoryid.
1// Filter system and hidden courses in transformer2const visibleCourses = (data || [])3 .filter(course => course.id !== 1 && course.visible === 1);Best practices
- Store the Moodle Web Services token in a Retool Configuration Variable marked as secret — never hardcode it in the resource's default parameters or query configurations.
- Create a dedicated Moodle user account with the minimum required role for the Web Services token rather than using an administrator account — this limits the scope of API access and makes token revocation straightforward.
- Always add the moodlewsrestformat=json parameter to your Moodle Web Services resource — without it, Moodle returns XML by default, which Retool cannot parse natively.
- Use Moodle's external service function list as your access control list — only add the functions your dashboards actually need, rather than adding all available functions, to minimize the API surface area.
- Convert all Moodle Unix timestamps to human-readable dates in your transformers — Moodle returns timestamps as seconds-since-epoch throughout its API, and displaying raw numbers in Tables is confusing for administrators.
- Cache Moodle course and user list queries for 5-15 minutes using Retool's query caching — large Moodle instances with thousands of users can be slow to query, and institutional data doesn't change minute-to-minute.
- For write operations (enrolling users, creating courses), always test against a Moodle staging or development instance before running against production — Moodle write operations are immediate and some are difficult to reverse.
Alternatives
Choose Canvas LMS if your institution uses Canvas instead of Moodle, as Canvas provides a modern REST API with standard endpoints and OAuth 2.0 authentication that is significantly simpler to integrate than Moodle's function-based Web Services.
Choose Schoology if your institution is K-12 focused and uses Schoology as the LMS, which provides OAuth 1.0a authentication and standard REST endpoints for course, grade, and user management.
Choose Blackboard if your institution uses Blackboard Learn as the LMS, which provides a modern REST API with OAuth 2.0 authentication through the Anthology developer portal for enterprise LMS integration.
Frequently asked questions
Why does Moodle's API use wsfunction parameters instead of separate endpoints like other REST APIs?
Moodle's Web Services architecture predates modern REST API conventions and reflects Moodle's plugin-based architecture. All Web Service functions — whether core Moodle functions or plugin-specific functions — are exposed through a single server.php endpoint, with the function name passed as a parameter. This design allows plugin developers to add new API functions without changing the server infrastructure. While unusual, it works reliably: every wsfunction call goes to the same URL with different parameters, making the Retool REST API Resource configuration straightforward.
Can I use the Moodle Mobile App Service token instead of creating a custom external service?
Moodle includes a built-in 'Moodle mobile web service' that is used by the Moodle mobile app. Its token can technically be used for Retool queries, but this is not recommended — the mobile service's function list is designed for the mobile app experience, not administrative dashboards. Create a custom external service with only the functions your Retool dashboards need, and generate a dedicated token for it. This gives you precise control over what the Retool integration can access.
What is the difference between core_enrol_get_enrolled_users and core_user_get_users?
core_user_get_users searches across all Moodle users globally (site-wide user search) and requires broad user-reading permissions. core_enrol_get_enrolled_users returns users enrolled in a specific course, scoped to that course's context. For building course-level dashboards, use core_enrol_get_enrolled_users with a courseid parameter — it returns richer enrollment-specific data and is appropriately scoped. Use core_user_get_users for global user management panels that need to search across all users regardless of enrollment.
Can Retool perform write operations in Moodle, such as enrolling users or creating courses?
Yes — Moodle's Web Services includes write functions like enrol_manual_enrol_users (enroll a user in a course), core_user_create_users (create new user accounts), and core_course_create_courses (create courses). These use POST requests with parameters in the request body. Add write functions to your external service and ensure the service account user has the appropriate Moodle roles to perform those operations. Always test write operations in a Moodle staging environment before enabling them in production dashboards.
How do I handle Moodle instances behind a VPN or firewall from Retool Cloud?
Retool Cloud connects to external APIs from its published egress IP ranges. For on-premises Moodle instances behind a firewall, you need to whitelist Retool's IP ranges for HTTPS access to the webservice/rest/server.php path. Check Retool's documentation for the current egress IP CIDR blocks for your Retool region. Alternatively, use self-hosted Retool deployed inside the same network as your Moodle instance, which eliminates the need for firewall exceptions.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation