CPDQC API
Connect your learning platform so learners get their CPDQC certificate the moment they complete an accredited course — no spreadsheets.
1. Get a key
Approved members create keys in Dashboard → API. A key is shown once; keep it on your server, never in a browser or app. Send it on every request:
Authorization: Bearer cpdqc_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXBase URL https://cpdqc.com/api/v1 · JSON in and out · 300 requests a minute per key · 2,000 certificates a day per provider.
2. Issue a certificate when a learner completes
course is your course’s CPDQC id or accreditation number. Pass your own external_id (for example your enrolment id) and retries are safe: the same id always returns the same certificate.
curl -X POST https://cpdqc.com/api/v1/certificates \
-H "Authorization: Bearer $CPDQC_KEY" -H "Content-Type: application/json" \
-d '{
"course": "CPDQC-C-00012",
"learner_name": "Maya Okafor",
"learner_email": "[email protected]",
"completed_on": "2026-10-20",
"external_id": "enrolment-48213"
}'201 Created (200 if this external_id was already issued)
{
"result": "issued", // or "exists" | "duplicate"
"certificate": {
"code": "CPDQC-CT-7KQ4-M9XD",
"url": "https://cpdqc.com/certificates/CPDQC-CT-7KQ4-M9XD",
"status": "valid", "learner_name": "Maya Okafor", "cpd_points": 4, ...
}
}The learner is emailed their certificate (add ?send_email=false to skip that), and it joins their CPDQC record when their email is confirmed. Show certificate.url or the code on your own certificate so anyone can verify it.
Endpoints
| GET | /provider | Your membership, provider number and package usage |
| GET | /courses | Your courses (all statuses), with listings. ?status=approved |
| GET | /courses/{id or CPDQC-C number} | One course |
| POST | /courses | Create a draft course (same fields as the dashboard) |
| PUT | /courses/{ref} | Update a course |
| POST | /courses/{ref}/listings | Add a link where the course is sold — { "url": "…" } |
| DELETE | /courses/{ref}/listings/{listing_id} | Remove a link |
| POST | /courses/{ref}/submit | Submit a draft for accreditation (needs a package slot) |
| POST | /certificates | Issue one certificate (safe to retry with external_id) |
| POST | /certificates/batch | Issue up to 500 certificates in one call |
| GET | /certificates | Your certificates. ?external_id= ?email= ?course= ?status= |
| GET | /certificates/{code} | One certificate |
| POST | /certificates/{code}/revoke | Revoke — { "reason": "…" } |
Errors
Errors return {"detail": "…"} with a plain-English message: 401 missing/revoked key · 403 membership not approved · 404 not yours / not found · 409 conflict (e.g. course not accredited, link already used) · 422 invalid data · 429 slow down / daily limit. A batch never fails as a whole — each row comes back as issued, exists, duplicate or invalid with a reason.
Links are checked daily
We check every listed course link once a day. A link that fails several days running is hidden from the catalogue and we email you; it reappears as soon as it loads again. Tracking parameters (utm_…, gclid, fbclid) are removed when a link is added, and the same link can’t be listed on two courses.
