CertifyMe Open edX Plugin
Open edX plugin that automatically issues CertifyMe credentials when a learner passes a course. The plugin is a thin bridge — all certificate design, templating, and delivery logic lives in CertifyMe; this package only talks to its REST API and keeps a local record of what was requested.
Features
- Admin-editable configuration (regional server, API token, template ID, credential fields, auto-issue toggle) with full change history.
- Automatic issuance on course completion, via the Open edX Hooks Extension Framework — never blocks an LMS request; all API calls happen in a Celery task.
- Automatic retries with exponential backoff for transient CertifyMe failures; permanent failures are recorded with a reason and can be retried manually from Django admin or the instructor tools page.
- Learner-facing "My Certificates" page (with a link to the CertifyMe-hosted certificate) and a public certificate verification page.
- Course-staff "instructor tools" page: search/filter, manual issue, bulk issue for a whole cohort, retry.
Architecture notes
- Registration: uses edx-platform's native Django plugin architecture
(
lms.djangoapp/cms.djangoappentry points + aplugin_appconfig inapps.py), not a bespoke Tutor plugin. NoINSTALLED_APPSedits, no core changes. - Course-completion signal: subscribes to
openedx_events.learning.signals.COURSE_PASSING_STATUS_UPDATED, the Hooks Extension Framework event edx-platform's own grading app emits whenever a learner's passing status changes. Event payload field names have shifted across Open edX releases before; the handler logs the raw payload and fails closed (logs + returns) rather than crashing grade processing if a field is ever missing. - Student/instructor pages: the real student dashboard and legacy
instructor dashboard are edx-platform core templates with no supported
third-party injection point (that only exists for the newer React MFEs, a
separate frontend project). Rather than patch core templates, this
plugin serves its own small, self-contained pages under
/certifyme/.... - CertifyMe API contract: confirmed via a real, successful live call
(not a guess) — see
api.py's module docstring for the full detail. In particular:- The working endpoint is the regional one, e.g.
POST https://apac.platform.certifyme.dev/api/v2/credential— seeservers.py. CertifyMe's public API reference documents a generichttps://my.certifyme.online/api/v2/credentialwhich consistently returned500in testing against a real account; the regional URL (matching CertifyMe's own Moodle plugin,local_certifyme) worked. - Auth is the raw API token in the
Authorizationheader (noBearerprefix). template_IDmust be sent as a JSON number, not a string.- No organization-id concept — an account is scoped entirely by its API token + regional server.
- No dedicated course/completion-date field — course data can only reach
the credential via
custom_fields(see Configuration below). - The response does carry a stable id (
credential_UID) and a verification URL (credential_url) — captured ascertificate_id/verification_urlonCertifyMeCertificate. - No badge, revoke, or resend endpoint is confirmed to exist. CertifyMe's
public API reference does document
GET/PUT/DELETEby credential id (retrieve/edit/delete) — not yet implemented here, only issuance.
- The working endpoint is the regional one, e.g.
Installation (Tutor)
-
For local development, bind-mount the plugin so Tutor builds it as an editable install and keeps it live-editable in the container:
tutor mounts add /path/to/openedx-certifyme
For a published release instead, add it to
OPENEDX_EXTRA_PIP_REQUIREMENTS:tutor config save --append OPENEDX_EXTRA_PIP_REQUIREMENTS=openedx-certifyme==<version>
-
Rebuild and restart the images (
openedxfortutor local,openedx-devfortutor dev):tutor images build openedx-dev # or `openedx` for a local/production deploy tutor dev launch # or `tutor local launch`
-
Run migrations (Tutor does this automatically on
launch/init; to run by hand):tutor dev run lms ./manage.py lms migrate openedx_certifyme
-
Make sure a Celery worker for the LMS queue is running (Tutor runs one by default) — automatic issuance depends on it.
-
Verify the plugin loaded: the LMS log should contain
CertifyMe plugin loaded (app=openedx_certifyme, ...), and:curl http://local.edly.io/certifyme/health # {"plugin": "openedx-certifyme", "status": "ok", "version": "0.1.0", "enabled": false}
Configuration
Go to Django admin → CertifyMe Configuration → Add. Every save creates a new versioned row (full change history, nothing is overwritten):
| Field | Purpose |
|---|---|
| Enabled | Master on/off switch for the whole integration |
| Server | Which CertifyMe regional server this account is on (APAC/EU2/US1/Butterfly) |
| API Token | CertifyMe API token (masked in the admin list view) |
| Template ID | CertifyMe credential template ID |
| Text | Free-text label shown on the credential (e.g. job title, organization) |
| Verify mode | Identity-verification mode: None / SSN / Code / Passport Number |
| Verify code | Required by CertifyMe when Verify mode isn't "None" |
| License number | Optional license number to display on the credential |
| Custom fields | One FieldName=value per line, sent as Custom.FieldName. Supports {course_name}, {student_name}, {student_email}, {date} tokens — this is the only way to get the course name onto the credential, e.g. Course={course_name} |
| Auto issue certificates | Issue automatically when a learner passes |
| API timeout / max retries | Tuning for the HTTP client |
There is no "Test connection" action: CertifyMe has no confirmed non-mutating endpoint to check against. Use the instructor tools page's manual Issue Certificate to confirm a token/template pair actually works.
Usage
- Learners:
/certifyme/certificates/lists their certificates, each linking to the CertifyMe-hosted certificate and a local verify page. - Course staff:
/certifyme/instructor/<course_id>/for manual issue, bulk issue, retry, search, and filtering. Requires course staff/instructor role or platform staff. - Support/ops: Django admin → CertifyMe Certificates for a read-only audit log (status, retry count, failure reason, raw API response) across all courses, with a bulk Retry action.
Troubleshooting
- Certificates never get issued: check
EnabledandAuto issue certificatesare both on in the current configuration, and that a Celery worker is consuming the LMS default queue. - "CertifyMe is not configured yet" errors: an API token is required before any API call will be attempted.
- A specific certificate is stuck FAILED: check
failure_reasonon the row (Django admin or instructor tools). A401/403means the API token is wrong; a404means the template ID doesn't exist on the selected server (per CertifyMe's own troubleshooting docs). Permanent failures are not auto-retried — fix the underlying issue, then use the Retry action. - Health check flooding logs: it logs at
DEBUG, notINFO, precisely because liveness probes hit it constantly — raise your log level if you need to see it. - Certificates stuck retrying with a connection/timeout error: a real
successful issuance call was observed taking ~20 seconds (CertifyMe
appears to generate and upload the credential image before responding),
which is why
API timeout / max retriesdefaults to a 30s timeout — if you've lowered it, raise it back up rather than assuming the API is down.
Metadata
Release files for openedx-certifyme 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openedx_certifyme-0.1.0.tar.gz | 30.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openedx_certifyme-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 66.0 kB
Release files / openedx_certifyme-0.1.0.tar.gz
| Download URL | openedx_certifyme-0.1.0.tar.gz |
|---|---|
| Size | 30.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
116676218c074ae441062df75973d6c8622fd7ffb6160331cc6b9529e5d86762
|
|
BLAKE2b-256 checksum How to use checksums |
6a62332a001e14d764b81e2be71d173293f0db8176a51b3dbff6aaf937d073e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|
Release files / openedx_certifyme-0.1.0-py3-none-any.whl
| Download URL | openedx_certifyme-0.1.0-py3-none-any.whl |
|---|---|
| Size | 35.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
74672e163255afde4d00372a0fb33040a3d2f3023063b0b0d834cdbd9c158745
|
|
BLAKE2b-256 checksum How to use checksums |
b9b12b0120e03e45931435550c233043e7c83b7137f3b3fa8ba479e44138bcb7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.4
|