CertLister Help
Go to app
On this page

Integrations

Connect Moodle to CertLister

Issue credentials automatically when learners complete Moodle courses — via the Event Trigger plugin and webhooks, the REST API, or CSV export.

CertLister Integrations page with a Moodle webhook endpoint

Connect Moodle to CertLister

Time to read: 10 minutes

Plan required: Pro (Paths A and B use API & Webhook Integrations) · Any plan for Path C

Moodle is one of the most widely used learning management systems — and one of the most common questions we hear is: "Can CertLister issue a credential automatically when a learner completes a Moodle course?"

Yes. Moodle doesn't send webhooks out of the box, but there are three reliable ways to connect it to CertLister. This guide walks through all three, from fully automatic to fully manual.


Which Path Should I Use?

Path A — Event Trigger pluginPath B — Polling scriptPath C — CSV export
Real-time issuance✅ Instant⚠️ Near real-time (polling interval)❌ Manual
Code required❌ None✅ A small script❌ None
Moodle plugin required✅ Yes (Event Trigger)❌ No❌ No
Works on MoodleCloud❌ No (plugins can't be installed)✅ Yes✅ Yes
Moodle admin access neededSite administratorSite administrator (web services setup)Teacher/manager
CertLister planProProAny

Our recommendation: if you run your own Moodle (or your hosting partner can install plugins), use Path A — it's real-time, requires no code, and takes about 20 minutes to set up. If you're on MoodleCloud or can't install plugins, use Path B if you have a developer available, or Path C if you don't.


In this setup, the free Event Trigger plugin (tool_trigger, maintained by Catalyst IT) watches for the Course completed event in Moodle and sends an HTTP POST to a CertLister incoming webhook. CertLister creates the credential — and can email it to the learner — the moment they complete the course.

Moodle course completed → Event Trigger workflow → HTTP POST → CertLister webhook → Credential issued → Email sent

Step 1 — Create the Incoming Webhook in CertLister

  1. In CertLister, go to IntegrationsWebhooks tab
  2. Click Create Webhook
  3. Fill in:
    • Name — e.g., "Moodle Course Completions"
    • ModeCreate (automatically creates a credential from incoming data)
    • Category — the category new credentials should go into
    • Design — optionally pick a saved design so credentials render with your template
    • Send Email — toggle on to email each learner their credential automatically
  4. Configure Field Mappings — map the JSON fields Moodle will send to credential fields:
Credential FieldJSON Path
recipient_namerecipient_name
recipient_emailrecipient_email
titletitle

(These match the JSON body you'll build in Moodle in Step 3. You can add more mappings for custom attributes — see Adding More Data below.)

  1. Click Create, then copy the webhook URL. It looks like:
https://app.certlister.com/api/v1/hooks/your-endpoint-token

Keep this URL secret. The endpoint token in the URL is what authorizes requests. Anyone with the URL can create credentials in your account — treat it like a password, and regenerate it from the endpoint card if it ever leaks.

Step 2 — Install the Event Trigger Plugin in Moodle

  1. In Moodle, go to Site administrationPluginsInstall plugins
  2. Search the plugin directory for Event Trigger (tool_trigger), or upload the ZIP from the plugin page
  3. Complete the installation and upgrade steps

Note: Installing plugins requires site administrator access and is not possible on MoodleCloud. If that's your situation, jump to Path B or Path C.

Step 3 — Create the Workflow in Moodle

  1. Go to Site administrationServerEvent Trigger
  2. Click Add workflow and fill in:
    • Name — e.g., "Send completions to CertLister"
    • Event — search for and select Course completed (\core\event\course_completed)
    • Active — leave off for now; you'll enable it after testing
  3. Add the workflow steps, in this order:

Step 3a — User lookup. The raw event only contains the learner's numeric userid. Add a User lookup step (reading the relateduserid field — for course completions, that's the learner) so the workflow gains fields like {user_fullname} and {user_email}.

Step 3b — Course lookup. Same idea for the course: add a Course lookup step reading the courseid field, which adds {course_fullname} and {course_shortname}.

Step 3c — HTTP POST action. Add an HTTP POST action step:

  • URL — your CertLister webhook URL from Step 1
  • HTTP headers:
Content-Type: application/json
  • HTTP params (body):
{
  "recipient_name": "{user_fullname}",
  "recipient_email": "{user_email}",
  "title": "{course_fullname}"
}

The {placeholders} are replaced with real values from the lookup steps when the workflow fires.

  1. Save the workflow.

Tip — Learning mode: if you're unsure which datafields are available, enable the workflow's Learning mode, complete a test course once, and Event Trigger will record the event and show you every field you can use as a placeholder.

Step 4 — Test End to End

  1. In Moodle, enrol a test user in a course that has course completion criteria configured (Course → Course completion settings — completion must be enabled, or the event never fires)
  2. Complete the course as the test user (or mark them complete manually as a teacher)
  3. Moodle fires the event within a minute or two (event workflows run on Moodle's cron — make sure cron is running)
  4. In CertLister, go to IntegrationsWebhooks and open the endpoint's Logs — you should see the request, its payload, and the result
  5. Check your Credentials page: the new credential should be there, and if Send Email is on, the learner has received it

Once the test passes, set the Moodle workflow to Active. Every course completion now issues a credential automatically.

Adding More Data from Moodle

Want the course short name, completion metadata, or other fields on the credential? Add them to the JSON body in Moodle:

{
  "recipient_name": "{user_fullname}",
  "recipient_email": "{user_email}",
  "title": "{course_fullname}",
  "course_code": "{course_shortname}"
}

Then map course_code to a custom attribute in the CertLister field mappings. Custom attributes can be printed on the credential design and shown on the verification page — see Custom Credential Attributes.

You can leave issue_date unmapped — CertLister stamps the credential with the date it's created, which for a real-time webhook is the completion date.

One Course, One Credential Type

A single webhook endpoint has one category and one design. If different Moodle courses should issue different credentials:

  1. Create one CertLister webhook endpoint per course (or per credential type), each with its own category and design
  2. In Moodle, create one workflow per course, adding a Filter step on courseid so each workflow only fires for its course, and point each at the matching webhook URL

For a handful of courses this is quick; for large catalogs, consider Path B where a script can route dynamically.


Path B — Polling Script via Moodle Web Services

If you can't install Moodle plugins (e.g., MoodleCloud), a small script can poll Moodle's built-in web services API for completions and create credentials through CertLister's REST API. It's near real-time — as fresh as your polling interval.

Cron/scheduler → Moodle web services (completions) → CertLister REST API → Credential issued

Step 1 — Enable Web Services in Moodle

In Moodle, as a site administrator:

  1. Site administrationGeneralAdvanced features → enable Web services
  2. Site administrationServerWeb servicesManage protocols → enable REST
  3. Web servicesExternal services → add a custom service and add these functions to it:
    • core_enrol_get_enrolled_users — list learners in a course
    • core_completion_get_course_completion_status — check a learner's completion
  4. Create a dedicated Moodle user for the integration, give it a role with only the capabilities those functions require, and authorize it for your service
  5. Web servicesManage tokens → create a token for that user

Moodle's own web services documentation covers each screen in detail.

Step 2 — Create a CertLister API Key

  1. In CertLister, go to IntegrationsAPI Keys tab
  2. Click Create API Key, name it (e.g., "Moodle Sync"), and give it the credentials:read and credentials:write scopes
  3. Copy the key (cl_live_...) — it's shown only once

Step 3 — Run a Polling Script

The script below checks one Moodle course for completions and issues a credential to anyone who completed it and doesn't have one yet. Run it on a schedule (cron, GitHub Actions, Cloud Scheduler — every 15 minutes works well).

// moodle-sync.js — Node.js 18+
const MOODLE_URL = "https://your-moodle-site.example.com";
const MOODLE_TOKEN = "your-moodle-ws-token";
const COURSE_ID = 42; // Moodle course id
const COURSE_NAME = "Workplace Safety Training";
const CERTLISTER_KEY = "cl_live_your_api_key_here";
const CERTLISTER_API = "https://app.certlister.com/api/v1/external";

async function moodle(wsfunction, params) {
  const query = new URLSearchParams({
    wstoken: MOODLE_TOKEN,
    wsfunction,
    moodlewsrestformat: "json",
    ...params,
  });
  const res = await fetch(`${MOODLE_URL}/webservice/rest/server.php?${query}`);
  return res.json();
}

async function certlister(path, options = {}) {
  const res = await fetch(`${CERTLISTER_API}${path}`, {
    ...options,
    headers: {
      Authorization: `Bearer ${CERTLISTER_KEY}`,
      "Content-Type": "application/json",
      ...options.headers,
    },
  });
  return res.json();
}

async function main() {
  const users = await moodle("core_enrol_get_enrolled_users", {
    courseid: COURSE_ID,
  });

  for (const user of users) {
    const status = await moodle("core_completion_get_course_completion_status", {
      courseid: COURSE_ID,
      userid: user.id,
    });
    if (!status.completionstatus?.completed) continue;

    // Skip learners who already have this credential
    const existing = await certlister(
      `/credentials?search=${encodeURIComponent(user.email)}`
    );
    const alreadyIssued = (existing.data || []).some(
      (c) => c.title === COURSE_NAME && c.recipient_email === user.email
    );
    if (alreadyIssued) continue;

    await certlister("/credentials", {
      method: "POST",
      body: JSON.stringify({
        recipient_name: user.fullname,
        recipient_email: user.email,
        title: COURSE_NAME,
        send_email: true,
      }),
    });
    console.log(`Issued credential to ${user.fullname}`);
  }
}

main();

Adapt as needed: loop over multiple courses, set category_id and design_id per course (list them via GET /external/categories and GET /external/designs), or add custom_attributes. The full endpoint reference is in API & Webhook Integrations.


Path C — CSV Export + Bulk Import

No plugins, no code, works everywhere — including MoodleCloud. Best for issuing credentials at the end of a course session rather than continuously.

  1. In Moodle, open the course and go to the Course completion report (ReportsCourse completion) — or the gradebook — and download the report as CSV
  2. In a spreadsheet, keep the columns you need: learner name, email, and completion date
  3. In CertLister, go to CredentialsImport and upload the CSV, mapping columns to credential fields
  4. Review and confirm — CertLister creates the credentials and can email them in bulk

Full walkthrough: Bulk Import Credentials.


Good to Know

Rate limits. Incoming webhooks accept 30 requests per minute per endpoint, and the REST API allows 100 requests per minute per key. Normal course completions never approach this — but if you bulk-mark a large cohort complete in Moodle at once, Event Trigger may fire hundreds of webhooks in a burst and some will be rejected with 429. For large batch operations, prefer Path B (the script paces itself) or Path C.

Duplicate protection. Webhook create mode creates a credential for every request it receives. Moodle only fires Course completed once per learner per course, so duplicates are rare in practice — but if you re-run completions (e.g., resetting and re-completing a course), check the webhook Logs and your Credentials list.

Completion must be configured. The Course completed event only fires for courses with completion criteria enabled in Moodle (Course → Course completion settings). If webhooks never arrive, this is the first thing to check — then confirm Moodle's cron is running, since Event Trigger workflows execute on cron.

Credential verification. Every credential issued from Moodle gets the same verification page, QR code, and recipient portal access as any other CertLister credential — see Recipient Portal.


Troubleshooting

SymptomLikely causeFix
Nothing arrives in CertListerCourse completion not enabled, or Moodle cron not runningEnable completion criteria; check Site administrationServerTasks
Webhook log shows 400 mapping errorJSON body fields don't match your field mappingsCompare the logged payload against the mappings; placeholder typos like {user_fullname } (stray space) are common
Webhook log shows 401Wrong or regenerated endpoint token in the URLCopy the current webhook URL from the endpoint card into the Moodle workflow
Credential created but no emailSend Email toggle off, or learner has no email in MoodleEnable the toggle; confirm {user_email} resolves (check Moodle user profile)
429 errors during bulk completionsRate limit (30/min per endpoint)Space out bulk completion marking, or use Path B/C for batches
Moodle web service returns invalidtokenToken expired or service not authorized for the userRecreate the token under Manage tokens; confirm the user is authorized for the service

Still stuck? We respond within 24 hours on business days. Contact support →