https://truecerta.com/api/v1Press⌘Kto searchAuthentication
All API requests (except public verification) require a Bearer token. Generate API keys in your Dashboard > Settings > API.
curl https://truecerta.com/api/v1/certificates \
-H "Authorization: Bearer trc_live_your_api_key_here"Security: API keys grant full access to your organization. Never expose them in client-side code. Store them as environment variables.
Error Handling
Errors return standard HTTP status codes with a JSON body:
{
"error": {
"message": "Course not found or does not belong to your organization",
"code": "not_found"
}
}Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
401 | unauthorized | Optional | Missing or invalid API key |
403 | plan_limit_exceeded | Optional | Certificate or course limit reached for your plan |
404 | not_found | Optional | Resource not found |
422 | unprocessable_entity | Optional | Missing or invalid parameters |
429 | rate_limited | Optional | Too many requests |
500 | internal_error | Optional | Server error |
Certificates
/api/v1/certificatesList all certificates
/api/v1/certificatesIssue a new certificate
/api/v1/certificates/:codeGet a single certificate by code
/api/v1/certificates/:code/revokeRevoke a certificate
List Certificates
Returns a paginated list of your certificates.
Query Parameters
Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
course_id | uuid | Optional | Filter by course |
status | string | Optional | "valid", "expired", or "all" (default) |
cursor | string | Optional | Pagination cursor (created_at of last item) |
limit | number | Optional | Results per page (max 50, default 50) |
curl "https://truecerta.com/api/v1/certificates?status=valid&limit=10" \
-H "Authorization: Bearer trc_live_your_key"{
"data": [
{
"id": "uuid",
"code": "TRC-ACA-26-F5N8K3",
"holder_name": "Jane Doe",
"holder_email": "jane@example.com",
"course_id": "uuid",
"course_name": "Web Development",
"issued_at": "2026-03-15T10:00:00Z",
"expires_at": "2027-03-15T10:00:00Z",
"claimed_at": "2026-03-16T14:00:00Z",
"is_claimed": true,
"created_at": "2026-03-15T10:00:00Z"
}
],
"pagination": {
"cursor": "2026-03-15T10:00:00Z",
"has_more": true
}
}Issue a Certificate
Request Body
Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
course_id | uuid | Required | ID of the course |
holder_name | string | Required | Full name of the certificate holder |
holder_email | string | Required | Email of the certificate holder |
issued_at | ISO 8601 | Optional | Custom issue date (defaults to now) |
curl -X POST https://truecerta.com/api/v1/certificates \
-H "Authorization: Bearer trc_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"course_id": "your-course-uuid",
"holder_name": "Jane Doe",
"holder_email": "jane@example.com"
}'Revoke a Certificate
Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
reason | string | Optional | Reason for revocation |
curl -X POST https://truecerta.com/api/v1/certificates/TRC-ACA-26-F5N8K3/revoke \
-H "Authorization: Bearer trc_live_your_key" \
-H "Content-Type: application/json" \
-d '{"reason": "Issued in error"}'Courses
/api/v1/coursesList all courses
/api/v1/coursesCreate a new course
Create a Course
Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Required | Course name |
description | string | Optional | Course description |
validity_period_months | number | Optional | Certificate validity in months (null = never expires) |
curl -X POST https://truecerta.com/api/v1/courses \
-H "Authorization: Bearer trc_live_your_key" \
-H "Content-Type: application/json" \
-d '{"name": "Web Development", "validity_period_months": 12}'Verification (Public)
The verification endpoint is public and does not require authentication. Use it to check the validity of any TrueCerta certificate. CORS headers are included — you can call this endpoint directly from browser JavaScript.
/api/v1/verify/:codeVerify a certificate by code
No authentication requiredcurl https://truecerta.com/api/v1/verify/TRC-ACA-26-F5N8K3{
"valid": true,
"status": "valid",
"certificate": {
"code": "TRC-ACA-26-F5N8K3",
"holder_name": "Jane Doe",
"is_claimed": true,
"issued_at": "2026-03-15T10:00:00Z",
"expires_at": "2027-03-15T10:00:00Z"
},
"course": {
"name": "Web Development",
"description": "Full-stack web development course",
"eqf_level": 5,
"nlqf_level": "5",
"qualification_label": "MBO 4"
},
"academy": {
"name": "Academy Name",
"logo_url": "https://..."
},
"links": {
"verify_page": "https://truecerta.com/v/TRC-ACA-26-F5N8K3",
"open_badge": "https://truecerta.com/api/obi/assertion/TRC-ACA-26-F5N8K3"
}
}Possible status values
Scroll
| Parameter | Type | Required | Description |
|---|---|---|---|
valid | string | Optional | Certificate is active and within its validity period |
never_expires | string | Optional | Certificate was issued without an expiry date |
expired | string | Optional | Certificate has passed its expiry date |
renewed | string | Optional | Certificate has been superseded by a renewal |
Open Badges 2.0
TrueCerta supports the Open Badges 2.0 specification. Each claimed certificate has three JSON-LD endpoints that comply with the IMS Global Open Badges standard.
/api/obi/assertion/:codeOB 2.0 Assertion (the credential)
No authentication required/api/obi/badge-class/:courseIdOB 2.0 BadgeClass (the credential type)
No authentication required/api/obi/issuer/:orgIdOB 2.0 Issuer Profile (the academy)
No authentication requiredAssertion Example
curl https://truecerta.com/api/obi/assertion/TRC-ACA-26-F5N8K3{
"@context": "https://w3id.org/openbadges/v2",
"type": "Assertion",
"id": "https://truecerta.com/api/obi/assertion/TRC-ACA-26-F5N8K3",
"recipient": {
"type": "email",
"hashed": true,
"salt": "TRC-ACA-26-F5N8K3",
"identity": "sha256$abc123..."
},
"badge": "https://truecerta.com/api/obi/badge-class/course-uuid",
"verification": {
"type": "HostedBadge"
},
"issuedOn": "2026-03-15T10:00:00Z",
"expires": "2027-03-15T10:00:00Z"
}Validate badges at openbadgesvalidator.imsglobal.org
Webhooks
Configure webhooks in Dashboard > Settings > Webhooks to receive real-time notifications when certificate events occur.
Events
| Event | Description |
|---|---|
certificate.issued | A new certificate was issued |
certificate.claimed | A certificate was claimed by a professional |
certificate.revoked | A certificate was revoked |
certificate.expiring | A certificate will expire within 30 days |
certificate.expired | A certificate has expired |
Payload Format
{
"event": "certificate.issued",
"data": {
"certificate": {
"id": "uuid",
"code": "TRC-ACA-26-F5N8K3",
"holder_name": "Jane Doe",
"holder_email": "jane@example.com",
"course_id": "uuid",
"course_name": "Web Development",
"issued_at": "2026-03-15T10:00:00Z",
"expires_at": "2027-03-15T10:00:00Z"
}
},
"timestamp": "2026-03-15T10:00:05Z"
}Signature Verification
Every webhook delivery includes an X-TrueCerta-Signature header containing an HMAC-SHA256 signature of the request body, using your webhook secret as the key.
// Node.js verification example
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post('/webhook', (req, res) => {
const signature = req.headers['x-truecerta-signature'];
const body = JSON.stringify(req.body);
if (!verifyWebhook(body, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const { event, data } = req.body;
console.log('Received:', event, data);
res.sendStatus(200);
});Test Webhook
Send a test event to all your registered webhook endpoints:
curl -X POST https://truecerta.com/api/v1/webhooks/test \
-H "Authorization: Bearer trc_live_your_key"Rate Limits
| Endpoint type | Limit | Scope |
|---|---|---|
| Authenticated API | 120 requests / minute | Per API key |
| Public verify / OBI | 60 requests / minute | Per IP |
| Webhook test | 5 requests / minute | Per organization |
Rate-limited responses return 429 Too Many Requests.
Need Help?
Questions about the API? Contact us at support@truecerta.com or visit our contact page.