Integrations · Setup guide
Connect your systems to Certrust
Part 1 is for the person who manages your organisation in Certrust: creating a key and keeping it safe. Part 2 is for whoever connects your system, such as your IT team or software provider. No IT team? The Google Sheets connector needs no development at all.
Part 1: For organisation admins
No technical knowledge needed. You will create a key, choose what it may do, and pass it on safely.
1. Check your plan
API keys are part of the Pro and Enterprise plans, including while you are on a trial of one. Sign in and open Manage → API keys. If you see "API keys are part of the paid plans", go to Billing to upgrade first.
2. Create a key
- Go to Manage → API keys.
- Give the key a name that says where it will be used, such as "Student records system" or "Moodle". You will see this name in the list and in the audit log.
- Choose the permissions. Give a key only what its system needs:
| Permission | Lets the system | Typical for |
|---|---|---|
| Read | Checking what has been issued, listing achievements and events. | Reporting or dashboards |
| Issue | Issuing credentials one by one or in groups, scheduling and renewing them. | Student records, learning platforms, event tools |
| Revoke | Revoking credentials, for example when a certification lapses. | HR and compliance systems |
| Manage | Creating and editing achievements and events. | Only if your system creates courses or events itself |
- Optionally set an expiry date. Leave it empty for a key that works until you revoke it.
- Select Create key.
4. Look after your keys
- Check activity. The API keys page shows when each key was last used. A key nobody uses any more should be revoked.
- Revoke straight away if a key may have been exposed, or a system or supplier is retired. Anything using it stops working immediately.
- Replace keys regularly. Create the new key, have your IT team switch over, then revoke the old one.
- Paused keys. If your plan no longer includes API access, keys show as paused and stop working until you upgrade again.
- Audit trail. Every credential issued or revoked with a key is recorded against that key's name.
Part 2: For developers
A JSON API over HTTPS. Base URL: https://api.certrust.app
Authentication
Send the key as a Bearer token on every request. Check it works, and see its organisation, permissions and the endpoints it may call:
curl https://api.certrust.app/api/api-keys/me \
-H "Authorization: Bearer $CERTRUST_API_KEY"The response includes issuerProfileId, which you need in the next step. Keep the key on your server; never put it in a website or mobile app.
Find the achievement ID
Every credential is issued for an achievement (the course, programme or event someone completed). Create the achievement in Certrust first. To find its numeric ID, either:
- open it on the Issue page in Certrust: the number after
?achievement=in the address is the ID, or - list the achievements the key's owner created:
curl https://api.certrust.app/api/achievements/creator/{issuerProfileId} \
-H "Authorization: Bearer $CERTRUST_API_KEY"Issue a credential
The recipient gets an email with a link to their credential. expirationDate and customFields are optional. customFields are only needed if your organisation has set up custom attributes in the Design Studio. Send them by key; required ones must be included, and keys that don't exist are ignored.
curl -X POST https://api.certrust.app/api/credentials/issue \
-H "Authorization: Bearer $CERTRUST_API_KEY" \
-H "Idempotency-Key: student-1042-course-7" \
-H "Content-Type: application/json" \
-d '{
"data": {
"achievementId": 12,
"recipient": { "name": "Ada Lovelace", "email": "ada@example.edu" },
"expirationDate": "2028-06-30",
"customFields": { "training_date": "2026-10-01" }
}
}'The same request in JavaScript (Node.js 18+):
const res = await fetch('https://api.certrust.app/api/credentials/issue', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.CERTRUST_API_KEY}`,
'Idempotency-Key': `student-${student.id}-course-${course.id}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: {
achievementId: 12,
recipient: { name: student.name, email: student.email },
},
}),
})
if (!res.ok) throw new Error((await res.json()).error?.message)
const { credential } = await res.json()
console.log(credential.credentialId) // urn:uuid:…And in Python:
import os, requests
res = requests.post(
"https://api.certrust.app/api/credentials/issue",
headers={
"Authorization": f"Bearer {os.environ['CERTRUST_API_KEY']}",
"Idempotency-Key": f"student-{student_id}-course-{course_id}",
},
json={"data": {
"achievementId": 12,
"recipient": {"name": name, "email": email},
}},
timeout=30,
)
res.raise_for_status()
print(res.json()["credential"]["credentialId"])Response. Keep credential.id if you may need to revoke later:
{
"credential": {
"id": 201,
"documentId": "mosfeiitve177d1bpztvbfej",
"credentialId": "urn:uuid:1713ee84-87c0-4564-84b1-d615b20a5e59",
"name": "Data Skills Workshop",
"issuanceDate": "2026-10-03T04:12:09.000Z",
"revoked": false
},
"openBadge": { "…": "the signed Open Badges 3.0 credential" },
"notification": { "…": "email delivery details" }
}Issue to a whole group
Send a whole class or cohort in one request. Each recipient gets their own result, so one bad email doesn't stop the rest. With "skipExisting": true, people who already hold this credential are skipped. That makes it safe to send the full list again, for example from a nightly sync.
curl -X POST https://api.certrust.app/api/credentials/batch-issue \
-H "Authorization: Bearer $CERTRUST_API_KEY" \
-H "Idempotency-Key: graduation-2026-batch-3" \
-H "Content-Type: application/json" \
-d '{
"data": {
"achievementId": 12,
"skipExisting": true,
"recipients": [
{ "name": "Ada Lovelace", "email": "ada@example.edu" },
{ "name": "Alan Turing", "email": "alan@example.edu" }
]
}
}'{
"results": [
{ "success": true, "recipient": "ada@example.edu", "data": { "…": "the credential" } },
{ "success": true, "skipped": true, "recipient": "alan@example.edu", "note": "Already has this credential" }
]
}A request can carry up to 200 recipients, and we recommend 50: long requests are more likely to be cut off part-way. If one is, resend the same batch with the same Idempotency-Key. For anything larger, use a job.
Very large groups: jobs
For a whole graduating class or a first import, create an issuance job with up to 2,000 recipients. The request returns straight away (status 202) and Certrust works through the list in the background, so nothing depends on one long connection.
curl -X POST https://api.certrust.app/api/issuance-jobs \
-H "Authorization: Bearer $CERTRUST_API_KEY" \
-H "Idempotency-Key: graduation-2026" \
-H "Content-Type: application/json" \
-d '{
"data": {
"achievementId": 12,
"skipExisting": true,
"recipients": [
{ "name": "Ada Lovelace", "email": "ada@example.edu" },
{ "name": "Alan Turing", "email": "alan@example.edu" }
]
}
}'The response contains the job's documentId. Ask for its progress every few seconds until status is completed, failed or cancelled:
curl https://api.certrust.app/api/issuance-jobs/{documentId} \
-H "Authorization: Bearer $CERTRUST_API_KEY"{
"data": {
"documentId": "rg69xy94mbvztzpkf84wpwll",
"status": "running",
"total": 1200,
"processed": 340,
"succeeded": 336,
"skipped": 3,
"failed": 1,
"results": [
{ "recipient": "ada@example.edu", "success": true, "id": 201, "credentialId": "urn:uuid:…" },
{ "recipient": "alan@example.edu", "success": true, "skipped": true, "note": "Already has this credential", "id": 187, "credentialId": "urn:uuid:…" },
{ "recipient": "bad-address", "success": false, "error": "…" }
]
}
}- Every recipient gets its own result, in the order you sent them, with the credential's
idandcredentialId. - A job that is interrupted, for example by a Certrust update, carries on where it stopped and never issues to the same person twice.
POST /api/issuance-jobs/{documentId}/cancelstops a job. Credentials already issued stay issued.- An organisation can have 3 jobs in progress at once. Results are kept for 30 days.
Revoke a credential
Needs the Revoke permission. Use the numeric credential.id from the issue response. Verification shows the credential as revoked straight away.
curl -X POST https://api.certrust.app/api/credentials/201/revoke \
-H "Authorization: Bearer $CERTRUST_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "Certification lapsed" }'Safe retries
Networks fail. If a request times out you can't tell whether the credential was issued, and sending it again might issue it twice. Add an Idempotency-Key header to every write request to prevent that:
- Use a value that names the operation, such as
student-1042-course-7, up to 255 characters. - Sending the same key with the same request again returns the first response instead of running it again, with the header
Idempotent-Replayed: true. Responses are kept for 24 hours. - Sending the same key with a different request is refused (422), so one key can't be reused by mistake.
- A retry that arrives while the first request is still running gets 409. Wait a moment and retry.
- Server errors (5xx) are not kept, so retrying after one runs the request again.
Rate limits
Each key can make 120 requests a minute. Every response shows where you stand in the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers. Beyond the limit you get 429 with a Retry-After header giving the seconds to wait. A batch or a job counts as one request however many recipients it has, so use those for volume instead of many single requests.
Errors
Errors come back as JSON with a readable message in error.message.
| Status | Meaning |
|---|---|
| 400 | Something in the request is missing or invalid, such as a required custom field. The message says what. |
| 401 | The key is wrong, revoked or expired, its creator has left the organisation, or your plan no longer includes API access. |
| 403 | The key doesn't have the permission for this action, or keys can't use this endpoint at all. |
| 409 | A request with the same Idempotency-Key is still running. Wait a moment and retry. |
| 422 | This Idempotency-Key was already used for a different request. Use a new key for a new operation. |
| 429 | Too many requests with this key in the last minute, or too many jobs in progress. Wait for the number of seconds in the Retry-After header. |
Endpoints by permission
A key can only call the endpoints its permissions allow. Everything else, including billing, account settings and key management, returns 403.
| Permission | Endpoints |
|---|---|
| Any key | GET /api/api-keys/me |
| Read | GET /api/credentialsGET /api/achievements/creator/{issuerProfileId}GET /api/achievements/{id}/credentialsGET /api/eventsGET /api/events/{id}GET /api/scheduled-issuancesGET /api/profiles/meGET /api/profiles/{id}/issued-credentialsGET /api/custom-attributesGET /api/issuance-jobsGET /api/issuance-jobs/{id} |
| Issue | POST /api/credentials/issuePOST /api/credentials/batch-issuePOST /api/credentials/{id}/renewPOST /api/scheduled-issuancesPOST /api/scheduled-issuances/{id}/cancelPOST /api/issuance-jobsGET /api/issuance-jobsGET /api/issuance-jobs/{id}POST /api/issuance-jobs/{id}/cancel |
| Revoke | POST /api/credentials/{id}/revoke |
| Manage | POST /api/achievements/createPUT /api/achievements/{id}POST /api/eventsPUT /api/events/{id} |
SDK and AI assistants
A JavaScript/TypeScript SDK (with apiKey and idempotencyKey options) and an MCP server for AI assistants such as Claude are in the open-source repository, in the sdk/ and mcp/ folders. For the MCP server, set CERTRUST_API_KEY to your key.
Questions? Contact us from the details at the bottom of this page.