Certificate API
Reference documentation and a live tester for the internal certificate service.
Overview
This API serves certificate records for two views in Postgres and builds links to the external certificate viewer. There are two certificate types.
| Type | Source view | Meaning |
|---|---|---|
ujikom | assesment_certificates_view | Competency assessment certificates |
pkl | certificateview | Internship (Praktik Kerja Lapangan) certificates |
All routes below except /health and /auth/token require a Bearer token in the Authorization header.
Authentication
Exchange the shared client credentials for a token once. The token stays valid for 30 days by default, set by JWT_EXPIRES_IN in the API's environment. Store it and reuse it for every request until it expires.
- Send
clientIdandclientSecrettoPOST /auth/token. - Save the returned
tokenvalue. - Attach it as
Authorization: Bearer <token>on every other request. - If a request returns 401, request a new token and try again.
Checks that the server is running and can reach the database.
curl http://localhost:3000/health
{
"status": "ok",
"database": true
}
Exchanges client credentials for a Bearer token.
| Field | Type | Required | Description |
|---|---|---|---|
clientId | string | Required | Matches CLIENT_ID in the server env. |
clientSecret | string | Required | Matches CLIENT_SECRET in the server env. |
curl -X POST http://localhost:3000/auth/token \
-H "Content-Type: application/json" \
-d '{"clientId":"internal-frontend-team","clientSecret":"your-secret"}'
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"tokenType": "Bearer",
"expiresIn": "30d"
}
Lists certificates from the chosen view, with search and pagination.
| Param | Type | Required | Description |
|---|---|---|---|
type (path) | string | Required | ujikom or pkl. |
search | string | Optional | Matches against name or student_id. |
page | integer | Optional | Defaults to 1. |
limit | integer | Optional | Defaults to 20, max 100. |
curl "http://localhost:3000/api/certificates/pkl?search=Arfa&page=1&limit=20" \ -H "Authorization: Bearer <token>"
{
"data": [
{ "document_id": "...", "student_id": "...", "name": "Arfa Nurzahra" }
],
"meta": { "page": 1, "limit": 20, "total": 1, "totalPages": 1 }
}
Returns a single certificate record by document_id.
| Param | Type | Required | Description |
|---|---|---|---|
type (path) | string | Required | ujikom or pkl. |
documentId (path) | string | Required | The record's document_id. |
curl "http://localhost:3000/api/certificates/pkl/002/JL-DIR-EXAMPLE" \ -H "Authorization: Bearer <token>"
{ "data": { "document_id": "002/JL-DIR-EXAMPLE", "name": "Arfa Nurzahra" } }
Looks up the record and returns the built external certificate URL, without fetching it.
| Param | Type | Required | Description |
|---|---|---|---|
type (path) | string | Required | ujikom or pkl. |
documentId (path) | string | Required | The record's document_id. |
curl "http://localhost:3000/api/certificates/pkl/002/JL-DIR-EXAMPLE/certificate-url" \ -H "Authorization: Bearer <token>"
{
"documentId": "002/JL-DIR-EXAMPLE",
"name": "Arfa Nurzahra",
"type": "pkl",
"url": "https://sertifikat.lontarlab.com/certificate/item/Arfa+Nurzahra/pkl"
}
Fetches the certificate source server side and returns it as is. Note the source is an HTML page rendered client side, not a static image file, so this returns HTML, not a picture.
| Param | Type | Required | Description |
|---|---|---|---|
type (path) | string | Required | ujikom or pkl. |
documentId (path) | string | Required | The record's document_id. |
curl "http://localhost:3000/api/certificates/pkl/002/JL-DIR-EXAMPLE/certificate-image" \ -H "Authorization: Bearer <token>" \ --output certificate.html
Embedding certificates via iframe
How to show the certificate itself on your own frontend, without going through the API.
The certificate source at sertifikat.lontarlab.com is not a static image file. It is a page rendered by its own JavaScript, with front and back tabs and a download button built in. Because of that, the right way to show it on your client is to embed the real page in an iframe rather than to request a picture from it.
This means you do not need to call certificate-image for display purposes. You only need the record's name, which you already get from the list or detail endpoints, plus the certificate type.
1. Build the URL on the client
The same rule the API uses internally: spaces in the name become +, then the type is appended at the end.
function buildCertificateUrl(name, type) {
const encodedName = name
.trim()
.split(/\s+/)
.map((part) => encodeURIComponent(part))
.join("+");
return "https://sertifikat.lontarlab.com/certificate/item/" + encodedName + "/" + type;
}
// type is "pkl" or "ujikom"
const url = buildCertificateUrl("Arfa Nurzahra", "pkl");
// https://sertifikat.lontarlab.com/certificate/item/Arfa+Nurzahra/pkl
2. Embed it directly
A plain iframe pointed at that URL is enough for a full sized view. The site's own front, back, and download controls keep working inside the frame.
<div style="width: 100%; height: 600px; border: 1px solid #ddd; border-radius: 8px; overflow: hidden;">
<iframe
src="https://sertifikat.lontarlab.com/certificate/item/Arfa+Nurzahra/pkl"
style="width: 100%; height: 100%; border: 0;"
title="Certificate for Arfa Nurzahra"
></iframe>
</div>
3. Small thumbnail in a list or grid
To show many certificates at once without loading full sized pages, render the iframe larger than its visible box and scale it down with CSS, then clip the overflow. Add loading="lazy" so the browser skips loading cards that are off screen.
<style>
.cert-thumb {
position: relative;
width: 220px;
height: 150px;
overflow: hidden;
border-radius: 8px;
border: 1px solid #ddd;
}
.cert-thumb iframe {
position: absolute;
top: 0;
left: 0;
width: 400%;
height: 400%;
border: 0;
transform: scale(0.25);
transform-origin: top left;
pointer-events: none; /* clicks pass through to a wrapper, not the frame */
}
</style>
<div class="cert-thumb" onclick="openFullCertificate('https://sertifikat.lontarlab.com/certificate/item/Arfa+Nurzahra/pkl')">
<iframe
src="https://sertifikat.lontarlab.com/certificate/item/Arfa+Nurzahra/pkl"
loading="lazy"
title="Certificate preview"
></iframe>
</div>
Clicking the thumbnail can then open the same URL full sized in a modal, or in a new tab.
One thing to check first
Embedding only works if the certificate site allows itself to be framed. If it sends an X-Frame-Options or Content-Security-Policy: frame-ancestors header that blocks embedding, the iframe will stay blank instead of showing the page. Since this is your own team's site, that setting is usually something you can adjust on that end. If it cannot be changed, the fallback is a server side headless browser that screenshots the rendered certificate into a real image file, which is a heavier setup than the iframe approach above.