Developer documentation
Build with Reedap
The Reedap REST API lets institutions issue, look up, and verify on-chain credentials, sync students and exam results into their LMS/SIS, and receive signed webhooks when meaningful events happen.
Quickstart
Issue your first certificate in under two minutes.
- Open Admin → API integrations and click Create key with the scopes you need.
- Copy the key (shown only once) into your secret manager as
REEDAP_API_KEY. - Call the API:
curl -X POST "https://reedap.com/api/public/v1/certificates" \
-H "Authorization: Bearer $REEDAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"recipient_name": "Ada Lovelace",
"recipient_email": "ada@example.com",
"course": "Intro to Programming",
"grade": "A",
"issue_date": "2026-06-18"
}'The response includes a verify_url you can email to recipients or print on a certificate as a QR code.
Authentication
Tenant-scoped API keys, sent as Bearer tokens.
Every request (except public verify) must include an Authorization header:
Authorization: Bearer rdp_live_<prefix>_<secret>Keys belong to one institution (tenant) and carry a set of scopes. You can create read-only keys for analytics dashboards and full-write keys for production SIS sync.
| Scope | Grants |
|---|---|
| certificates:read | List and fetch certificates issued by your institution |
| certificates:write | Issue new certificates (on-chain mint runs async) |
| students:read | List students who hold your certificates |
| students:write | Invite students by email; auto-creates accounts |
| attempts:read | Read exam attempt results and scores |
Certificates
Issue, list, and look up credentials.
/certificatescertificates:writeIssues a new certificate. Returns the record plus a public verify_url. On-chain anchoring happens asynchronously — status starts as pending and transitions to minted once confirmed on BSC.
| Name | Type | Description |
|---|---|---|
| recipient_name | string | Required. Full name as it appears on the credential. |
| recipient_email | string? | Optional but recommended — enables student lookup. |
| course | string | Required. Program / course title. |
| grade | string? | Optional grade or classification. |
| issue_date | YYYY-MM-DD | Required. |
| expiry_date | YYYY-MM-DD? | Optional expiration. |
| metadata | object? | Free-form JSON, included in the on-chain hash. |
{
"id": "5f8c…",
"certificate_id": "REE-2026-ABCD1234",
"recipient_name": "Ada Lovelace",
"course": "Intro to Programming",
"status": "pending",
"data_hash": "0x9f…",
"verify_url": "https://reedap.com/api/public/v1/verify/REE-2026-ABCD1234",
"created_at": "2026-06-18T12:00:00Z"
}POST /api/public/v1/certificatesStored locally in this browser only. Create one in Admin → API integrations.
/certificatescertificates:readLists certificates issued by your institution, newest first. Cursor-paginated.
| Name | Type | Description |
|---|---|---|
| limit | 1-200, default 50 | Max records to return. |
| cursor | ISO timestamp | Pass next_cursor from the previous page. |
| recipient_email | Filter by student email. | |
| status | pending | minting | minted | revoked | failed | Filter by lifecycle state. |
curl -G "https://reedap.com/api/public/v1/certificates" \
-H "Authorization: Bearer $REEDAP_API_KEY" \
--data-urlencode "limit=20" \
--data-urlencode "status=minted"{
"data": [ { "id": "…", "certificate_id": "REE-…", "status": "minted", … } ],
"next_cursor": "2026-06-18T11:59:00Z"
}GET /api/public/v1/certificates?limit=20Stored locally in this browser only. Create one in Admin → API integrations.
limitstatusrecipient_emailcursor/certificates/{id}certificates:readFetch a single certificate by internal UUID or public certificate_id.
GET /api/public/v1/certificates/REE-2026-ABCD1234Stored locally in this browser only. Create one in Admin → API integrations.
idStudents
Enrol students and read your roster.
/studentsstudents:writeInvites a student by email. Creates an auth account if needed and adds them to your institution. Idempotent on email.
curl -X POST "https://reedap.com/api/public/v1/students" \
-H "Authorization: Bearer $REEDAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"new@student.com","full_name":"New Student"}'{ "user_id": "9f…", "email": "new@student.com", "invited": true }POST /api/public/v1/studentsStored locally in this browser only. Create one in Admin → API integrations.
/studentsstudents:readReturns the unique set of students who hold a certificate from your institution.
| Name | Type | Description |
|---|---|---|
| limit | 1-200, default 50 | Max records to return. |
| string | Filter by email. |
GET /api/public/v1/students?limit=50Stored locally in this browser only. Create one in Admin → API integrations.
limitemailExam attempts
Sync scores back into your LMS.
/attemptsattempts:readReturns exam attempts across all exams owned by your institution.
| Name | Type | Description |
|---|---|---|
| limit | 1-200, default 50 | |
| cursor | ISO timestamp | Pagination cursor. |
| exam_id | uuid | Filter to a single exam. |
| passed | true | false | Filter by outcome. |
{
"data": [
{
"id": "…",
"exam_id": "…",
"user_id": "…",
"score": 87.5,
"passed": true,
"submitted_at": "2026-06-18T10:30:00Z"
}
],
"next_cursor": null
}GET /api/public/v1/attempts?limit=20Stored locally in this browser only. Create one in Admin → API integrations.
limitexam_idpassedcursorPublic verification
No auth required — anyone can verify.
Use this on a public website or in QR codes printed on your certificates. The endpoint looks up the credential, reads the on-chain record, and confirms the hash matches.
/verify/{certificate_id}no authReturns the certificate, tenant branding, and on-chain match status.
curl "https://reedap.com/api/public/v1/verify/REE-2026-ABCD1234"{
"found": true,
"certificate": { "recipient_name": "Ada Lovelace", "status": "minted", … },
"tenant": { "name": "University of Lagos", "slug": "unilag" },
"onChain": { "configured": true, "exists": true, "matches": true,
"issuedAt": 1750000000, "revokedAt": 0 }
}GET /api/public/v1/verify/REE-2026-ABCD1234certificate_idWebhooks
Get notified the moment something happens.
Register your endpoint in Admin → API integrations. Reedap POSTs JSON with these headers:
X-Reedap-Event: certificate.issued
X-Reedap-Signature: sha256=<hex hmac of raw body>
X-Reedap-Delivery: <uuid>
Content-Type: application/json| Event | When it fires |
|---|---|
| certificate.issued | After a certificate is minted on-chain successfully. |
| certificate.revoked | After a certificate is revoked (and the chain record updated). |
| attempt.submitted | When a student submits an exam attempt. |
Verifying signatures
Always verify the signature with a timing-safe comparison before trusting the payload.
import express from "express";
import crypto from "crypto";
const app = express();
app.post("/webhooks/reedap",
express.raw({ type: "application/json" }), // need RAW body
(req, res) => {
const sig = req.header("X-Reedap-Signature") ?? "";
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.REEDAP_WEBHOOK_SECRET)
.update(req.body) // raw Buffer
.digest("hex");
if (sig.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(401).send("invalid signature");
}
const { event, data } = JSON.parse(req.body.toString("utf8"));
if (event === "certificate.issued") {
// sync to your LMS
}
res.status(200).send("ok");
});
Errors & rate limits
All errors return JSON:
{ "error": "Missing scope: certificates:write" }| Status | Meaning |
|---|---|
| 200 / 201 | Success. |
| 400 | Bad input — see error field for details. |
| 401 | Missing, malformed, or revoked API key. |
| 403 | Key is valid but missing the required scope. |
| 404 | Resource not found within your tenant. |
| 429 | Rate limited — back off and retry. |
| 5xx | Server error — safe to retry idempotent requests. |
Every authenticated request is logged with method, path, status, and timestamp, visible to tenant admins in the dashboard.
SDKs & samples
Official SDKs are on the roadmap. The API is plain REST + JSON, so any HTTP client works. Reach out at developers@reedap.com for integration support, sandbox credentials, or to request an SDK in your stack.