This skill guides you through creating custom external web service APIs for Moodle LMS, following Moodle's external API framework and coding standards.
Installs into .claude/skills of the current project.
Are you the author of Moodle External Api Development?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/huytran1120-moodle-external-api-development)
---
name: moodle-external-api-development
description: "This skill guides you through creating custom external web service APIs for Moodle LMS, following Moodle's external API framework and coding standards."
risk: critical
source: community
date_added: "2026-02-27"
---
# Moodle External API Development
This skill guides you through creating custom external web service APIs for Moodle LMS, following Moodle's external API framework and coding standards.
## Detailed Guide
Read [the detailed guide](references/detailed-guide.md) before executing this skill. It retains the complete procedure and reference material. Treat its safety, prerequisites, and validation requirements as mandatory. For focused work, load the relevant sections; for end-to-end work, read the guide completely.
## When to Use This Skill
- Creating custom web services for Moodle plugins
- Implementing REST/AJAX endpoints for course management
- Building APIs for quiz operations, user tracking, or reporting
- Exposing Moodle functionality to external applications
- Developing mobile app backends using Moodle
## Illustrative Implementation Fragments
### Simple Read API (Get Quiz Attempts)
```php
<?php
namespace local_userlog\external;
defined('MOODLE_INTERNAL') || die();
require_once("$CFG->libdir/externallib.php");
use external_api;
use external_function_parameters;
use external_single_structure;
use external_value;
class get_quiz_attempts extends external_api {
public static function execute_parameters() {
return new external_function_parameters([
'userid' => new external_value(PARAM_INT, 'User ID'),
'courseid' => new external_value(PARAM_INT, 'Course ID')
]);
}
public static function execute($userid, $courseid) {
global $DB;
$params = self::validate_parameters(self::execute_parameters(), [
'userid' => $userid,
'courseid' => $courseid
]);
$context = \context_course::instance($params['courseid']);
self::validate_context($context);
require_capability('mod/quiz:viewreports', $context);
// Instructor-report endpoint; add group/quiz-specific visibility restrictions as needed.
$sql = "SELECT COUNT(*) AS quiz_attempts
FROM {quiz_attempts} qa
JOIN {quiz} q ON qa.quiz = q.id
WHERE qa.userid = :userid AND q.course = :courseid";
$attempts = $DB->get_field_sql($sql, [
'userid' => $params['userid'],
'courseid' => $params['courseid']
]);
return ['quiz_attempts' => (int)$attempts];
}
public static function execute_returns() {
return new external_single_structure([
'quiz_attempts' => new external_value(PARAM_INT, 'Total number of quiz attempts')
]);
}
}
```
### Complex Write API (Create Quiz from Categories)
The referenced `create_quiz_from_categories.php` is not bundled. Do not claim it
was read, executed or transaction-tested. Use the installed Moodle release's
activity/question APIs with context/capability checks, group visibility, bounded
input and rollback tests; direct insertion fragments in the guide are historical
schema illustrations, not a complete quiz builder. Use least-privilege test roles
and synthetic learner data, never production admin tokens in shell arguments.
## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.