API Documentation
Bring the certificates you issue on Proudify into your own website or app: import the full list, look up any certificate, and display it with a link back to official verification.
How it works
You issue certificates in Proudify as usual. The API lets your own website or app pull those certificates in and show them to your visitors, for example a "Our graduates" wall, a student portal, or a certificate page in your mobile app.
1. Import
Your server calls GET /certificates with your token and saves the list (or caches it).
2. Show
Your pages render that data. Images, PDFs and QR codes load from public links, no token in the browser.
3. Verify
Visitors click "Verify" and land on the official Proudify page, which confirms the certificate is genuine.
Always call the API from your server (PHP, Node, Python, a serverless function). Never from JavaScript running in a visitor's browser: anyone could copy your token from the page.
Quick start (5 minutes)
- Create a token. In Proudify go to Profile → API Tokens, give it a name (e.g. "Website"), click Create Token and copy it. It is shown only once.
- Make your first call from a terminal:
curl https://proudify.in/api/v1/certificates?per_page=5 \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Accept: application/json"
- Check the response. You get a
dataarray of certificates pluslinksandmetafor paging. Copy anyimage_urlinto your browser: it opens the certificate image with no token. That's the link your website will use.
Integration guide
A complete, working pattern: import every certificate, keep it up to date, and render an index and a detail page. Pick your language:
Step 1: Import all certificates
Request pages of 100 until links.next is null. Store each certificate by its uuid so re-running the import updates rows instead of duplicating them.
// sync-certificates.php (run from cron or your framework's scheduler) $token = getenv('PROUDIFY_TOKEN'); $url = 'https://proudify.in/api/v1/certificates?per_page=100&sort=updated_at&direction=asc'; // For later runs, only fetch what changed since the last sync: // $url .= '&updated_since=' . urlencode($lastSyncTime); while ($url) { $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer $token", 'Accept: application/json'], ]); $page = json_decode(curl_exec($ch), true); curl_close($ch); foreach ($page['data'] as $cert) { saveCertificate($cert); // INSERT ... ON DUPLICATE KEY UPDATE on uuid } $url = $page['links']['next']; // null on the last page }
// sync-certificates.js (Node 18+, run on a schedule) const token = process.env.PROUDIFY_TOKEN; let url = 'https://proudify.in/api/v1/certificates?per_page=100&sort=updated_at&direction=asc'; // For later runs: url += `&updated_since=${encodeURIComponent(lastSyncTime)}`; while (url) { const res = await fetch(url, { headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, }); if (res.status === 429) { // rate limited: wait and retry await new Promise(r => setTimeout(r, (+res.headers.get('Retry-After') || 60) * 1000)); continue; } const page = await res.json(); for (const cert of page.data) { await saveCertificate(cert); // upsert by cert.uuid } url = page.links.next; // null on the last page }
Step 2: Choose what to store
| Field | Use it for |
|---|---|
uuid | Your primary key for upserts. |
verification_code | Friendly ID for your own URLs, e.g. /certificates/K7Q2XM4P. |
recipient_name, title, date_of_issue | Card text, search and sorting on your index page. |
status | Hide or badge revoked and expired certificates. |
image_url, pdf_url, verify_url, embed_url | Public links your pages use directly. |
recipient_email | Matching certificates to logged-in users. Don't show it publicly. |
Step 3: Index page (list of certificates)
<?php $certs = db()->query("SELECT * FROM certificates WHERE status = 'active' ORDER BY date_of_issue DESC"); ?> <div class="cert-grid"> <?php foreach ($certs as $c): ?> <a href="/certificates/<?= htmlspecialchars($c['verification_code']) ?>"> <img src="<?= htmlspecialchars($c['image_url']) ?>" alt="<?= htmlspecialchars($c['title']) ?>" loading="lazy"> <h3><?= htmlspecialchars($c['recipient_name']) ?></h3> <p><?= htmlspecialchars($c['title']) ?></p> </a> <?php endforeach; ?> </div>
// Express route app.get('/certificates', async (req, res) => { const certs = await db.certificates.findMany({ where: { status: 'active' }, orderBy: { date_of_issue: 'desc' } }); res.render('certificates/index', { certs }); }); // certificates/index.ejs <% certs.forEach(c => { %> <a href="/certificates/<%= c.verification_code %>"> <img src="<%= c.image_url %>" alt="<%= c.title %>" loading="lazy"> <h3><%= c.recipient_name %></h3> </a> <% }) %>
Step 4: Show page (one certificate)
Read it from your database, or fetch it live with GET /certificates/{verification_code} if you don't store data. Either build the page yourself, or drop in the ready-made embed:
<!-- Option A: your own layout --> <img src="{image_url}" alt="{title} awarded to {recipient_name}"> <a href="{pdf_url}">Download PDF</a> <a href="{verify_url}" target="_blank" rel="noopener">Verify authenticity</a> <!-- Option B: one line, styled for you --> <iframe src="{embed_url}" style="width:100%;max-width:960px;aspect-ratio:1.25;border:0" loading="lazy"></iframe>
Step 5: Keep it in sync
- Run the Step 1 script on a schedule (hourly or nightly) with
updated_sinceset to the time of the previous successful run. Revoked certificates come through as changes. - When a certificate's
statusbecomesrevoked, hide it or show a "Revoked" badge on your site. - If
assets_availableisfalse, your annual membership has lapsed and image/PDF links are paused until you renew. Show a placeholder. - Stay under 60 requests per minute. With 100 per page that's 6,000 certificates per minute, so a full import is quick.
FAQ
Can I call the API from JavaScript in the browser?
No. Your token would be visible to every visitor. Call it from your server and pass the results to your pages. The image, PDF, QR and embed links are public, so those are fine in the browser.
Which certificates does my token see?
Only the certificates issued by your own account. Each token belongs to the user who created it.
Do the image links ever change?
No. They are tied to the certificate itself, so you can store them. They stop working only if the certificate is deleted, or are paused while your annual membership is inactive.
How do I revoke a token?
Profile → API Tokens → Revoke. Any app using it gets 401 Unauthenticated straight away. If your account is suspended, all its tokens stop working too (403).
Can I issue certificates through the API?
Not yet. The API is read-only for now; issue certificates from the Proudify dashboard or bulk upload.
Introduction
All API requests are made over HTTPS. The API returns JSON for all responses. Requests and responses are UTF-8 encoded. Timestamps are in ISO 8601 format.
The API currently provides read access to certificates you have issued. Write endpoints (issuing, updating, deleting) are on the roadmap.
Authentication
The API uses Bearer token authentication (Laravel Sanctum personal access tokens).
Every request must include an Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Generating a token
- Sign in to your Proudify account.
- Go to Profile → API Tokens.
- Enter a name for the token and click Create Token.
- Copy the token immediately — it will not be shown again.
Treat API tokens like passwords. Do not embed them in client-side code or commit them to version control. Use environment variables.
Base URL
All endpoints are prefixed with:
https://proudify.in/api/v1
Rate Limiting
The API is rate-limited to 60 requests per minute per token.
When you exceed the limit you receive a 429 Too Many Requests response.
The following headers are included in every response:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window. |
X-RateLimit-Remaining | Requests remaining in the current window. |
Retry-After | Seconds to wait before retrying (only on 429). |
Errors
All error responses follow the same shape:
{
"message": "Unauthenticated."
}
| Status | Meaning |
|---|---|
400 | Bad Request — invalid parameters. |
401 | Unauthorized — missing or invalid token. |
403 | Forbidden — your account is not active (e.g. suspended). |
404 | Not Found — resource does not exist. |
422 | Validation Error — see errors key for details. |
429 | Too Many Requests — rate limit exceeded. |
500 | Server Error — something went wrong on our end. |
/certificates
Returns a paginated list of all certificates issued by the authenticated user, sorted newest first.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
per_page |
integer | optional | Results per page. Default: 15. Max: 100. |
page |
integer | optional | Page number to retrieve. Default: 1. |
status |
string | optional | Only active, expired or revoked certificates. |
template_id |
integer | optional | Only certificates issued from this template. |
search |
string | optional | Matches recipient name, email or title, or an exact Credential ID. |
issued_from |
date | optional | Issued on or after this date (YYYY-MM-DD). |
issued_to |
date | optional | Issued on or before this date. |
updated_since |
datetime | optional | Only certificates created or changed (e.g. revoked) since this time. Use for incremental syncs. |
sort |
string | optional | created_at (default), updated_at or date_of_issue. |
direction |
string | optional | desc (default) or asc. |
Example Request
# List the first 25 certificates curl https://proudify.in/api/v1/certificates?per_page=25 \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Accept: application/json" # Nightly sync: everything changed since the last run curl "https://proudify.in/api/v1/certificates?updated_since=2025-06-01T00:00:00Z&sort=updated_at&direction=asc&per_page=100" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Accept: application/json"
Response
{
"data": [
{
"uuid": "01940e3d-bfa2-7b2e-aef0-c3db35e12abc",
"title": "Data Science – Batch 2025",
"recipient_name": "Priya Mehta",
"recipient_email": "priya@example.com",
"description": null,
"date_of_issue": "2025-06-01",
"date_of_expiry": null,
"status": "active",
"verification_code": "K7Q2XM4P",
"verify_url": "https://proudify.in/certificates/verify/...",
"template": { "id": 3, "name": "Classic Navy" },
"assets_available": true,
"image_url": "https://proudify.in/certificates/verify/.../image",
"pdf_url": "https://proudify.in/certificates/verify/.../download",
"qr_url": "https://proudify.in/certificates/verify/.../qr",
"embed_url": "https://proudify.in/certificates/embed/...",
"created_at": "2025-06-01T10:30:00+05:30"
}
],
"links": {
"first": "https://proudify.in/api/v1/certificates?page=1",
"last": "https://proudify.in/api/v1/certificates?page=4",
"prev": null,
"next": "https://proudify.in/api/v1/certificates?page=2"
},
"meta": {
"current_page": 1,
"per_page": 25,
"total": 87,
"last_page": 4
}
}
/certificates/{uuid}
Returns a single certificate. The {uuid} segment accepts either
the certificate's UUID or the human-readable Credential ID
(e.g. K7Q2XM4P) printed on the certificate.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
uuid |
string | required | The certificate UUID or Credential ID. |
Example Request
curl https://proudify.in/api/v1/certificates/01940e3d-bfa2-7b2e-aef0-c3db35e12abc \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Accept: application/json" # Also works with the Credential ID printed on the certificate curl https://proudify.in/api/v1/certificates/K7Q2XM4P \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Accept: application/json"
Response
{
"data": {
"uuid": "01940e3d-bfa2-7b2e-aef0-c3db35e12abc",
"title": "Data Science – Batch 2025",
"recipient_name": "Priya Mehta",
"recipient_email": "priya@example.com",
"description": null,
"date_of_issue": "2025-06-01",
"date_of_expiry": null,
"status": "active",
"verification_code": "K7Q2XM4P",
"verify_url": "https://proudify.in/certificates/verify/...",
"created_at": "2025-06-01T10:30:00+05:30"
}
}
/certificates/{uuid}/image
Streams the rendered certificate as a PNG image.
Returns 404 if the image has not been generated yet.
Example Request
curl https://proudify.in/api/v1/certificates/01940e3d-bfa2-7b2e-aef0-c3db35e12abc/image \ -H "Authorization: Bearer YOUR_API_TOKEN" \ --output certificate.png
/certificates/{uuid}/download
Downloads the certificate as a PDF file with a
Content-Disposition: attachment header.
Returns 404 with "The PDF is still being generated." if the file is not ready yet.
Example Request
curl https://proudify.in/api/v1/certificates/01940e3d-bfa2-7b2e-aef0-c3db35e12abc/download \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -L --output "certificate.pdf"
/certificates/{uuid}/qr
Streams the QR code for the certificate as a PNG image. Scanning the QR code opens the public verification page for the credential.
Example Request
curl https://proudify.in/api/v1/certificates/01940e3d-bfa2-7b2e-aef0-c3db35e12abc/qr \ -H "Authorization: Bearer YOUR_API_TOKEN" \ --output qr.png
Certificate Object
Fields returned inside data for every certificate endpoint.
| Field | Type | Description |
|---|---|---|
uuid | string | Internal UUID of the certificate. |
title | string | Name of the certificate / program. |
recipient_name | string | Full name of the certificate recipient. |
recipient_email | string | Email address of the recipient. |
description | string|null | Optional description or achievement text. |
date_of_issue | string (date) | Issue date in YYYY-MM-DD format. |
date_of_expiry | string|null | Expiry date if set, otherwise null. |
status | string | Current status: active, expired, or revoked. |
verification_code | string | Human-readable Credential ID (e.g. K7Q2XM4P). |
verify_url | string (url) | Public verification URL for this certificate. |
template | object | id and name of the template it was issued from. |
assets_available | boolean | false while your annual membership is inactive; the asset URLs below are then null. |
image_url | string|null | Public PNG/JPG of the certificate. Safe to use directly in an <img src>: no token needed. null while still generating. |
pdf_url | string|null | Public PDF download. null for revoked or expired certificates. |
qr_url | string|null | Public QR code image linking to the verify page. |
embed_url | string (url) | Ready-made card (image, name, status, verify link) to place in an <iframe>. |
created_at | string (ISO 8601) | When the certificate record was created. |
updated_at | string (ISO 8601) | Last change, including revocation. Use with updated_since. |
Showing certificates on your website
Call the API from your server (your token stays secret), save or cache the results, then render them in your pages.
The image_url, pdf_url, qr_url and embed_url fields are public links, so your visitors' browsers can load them directly without your token.
Option 1: build your own list and detail pages
<!-- Index: one card per certificate from GET /certificates --> <a href="/certificates/K7Q2XM4P"> <img src="{image_url}" alt="{title} – {recipient_name}" loading="lazy"> <p>{recipient_name} · {title}</p> </a> <!-- Show: data from GET /certificates/{verification_code} --> <img src="{image_url}" alt="{title}"> <a href="{pdf_url}">Download PDF</a> <a href="{verify_url}" target="_blank">Verify</a>
Option 2: drop in the ready-made embed
No styling needed. Add ?image=0 to show only the name, status and verify link.
<iframe src="{embed_url}" style="width:100%;max-width:960px;aspect-ratio:1.25;border:0" loading="lazy" title="Certificate"></iframe>
updated_since (for example nightly) to keep your pages current.
Ready to build?
Generate your API token from the Profile page and start making requests.