Certificates

SecureCodingHub auto-issues a certificate when a user finishes every learn scenario and every practice challenge in an entire category (Web, API, Mobile, Client). These endpoints expose the issued certificates and a public-style verification lookup. Requires the certificates:read scope.

Endpoints

MethodPathScopePurpose
GET/certificates/users/{userId}certificates:readCertificate status for every category, for one user.
GET/certificates/verify/{certNumber}certificates:readVerify a certificate by its public number.

All paths are relative to https://api.limeplate.com/api/public/v1.

How certificates are issued

Issuance is fully automatic — there is no manual "grant" endpoint. The rules:

  • One certificate per category per user. The categories are web, api, mobile, and client.
  • A category certificate is issued the moment a user has completed all learn scenarios and all practice challenges in that category.
  • Each certificate is assigned a permanent, public-shareable certificateNumber.
  • Issuance fires a certificate.issued webhook event. See Webhooks for the payload.

Re-completing content after issuance does not generate a new certificate; the original number is permanent.

List certificate status for a user

Returns one entry per category. Entries where certificateId is null represent categories the user has started but not yet finished — handy for rendering a "you're 18 of 22 challenges away from your Web certificate" UI.

GET /api/public/v1/certificates/users/{userId}
Authorization: Bearer scs_live_…

Response

[
  {
    "categoryId": "web",
    "categoryTitle": "Web Application Security",
    "certificateId": "7e4c9d09-86ca-4125-bbd8-7fae54c8f654",
    "certificateNumber": "SCH-2026-WEB-000123",
    "issuedAt": "2026-05-12T08:41:22Z",
    "practiceTotal": 75,
    "practiceCompleted": 75,
    "learnTotal": 22,
    "learnCompleted": 22,
    "isComplete": true
  },
  {
    "categoryId": "api",
    "categoryTitle": "API Security",
    "certificateId": null,
    "certificateNumber": null,
    "issuedAt": null,
    "practiceTotal": 60,
    "practiceCompleted": 42,
    "learnTotal": 18,
    "learnCompleted": 14,
    "isComplete": false
  },
  {
    "categoryId": "mobile",
    "categoryTitle": "Mobile Application Security",
    "certificateId": null,
    "certificateNumber": null,
    "issuedAt": null,
    "practiceTotal": 50,
    "practiceCompleted": 0,
    "learnTotal": 15,
    "learnCompleted": 0,
    "isComplete": false
  },
  {
    "categoryId": "client",
    "categoryTitle": "Client-Side Security",
    "certificateId": null,
    "certificateNumber": null,
    "issuedAt": null,
    "practiceTotal": 40,
    "practiceCompleted": 6,
    "learnTotal": 12,
    "learnCompleted": 2,
    "isComplete": false
  }
]
  • isComplete mirrors "practiceCompleted == practiceTotal && learnCompleted == learnTotal".
  • practiceTotal and learnTotal are live counts — adding new content to a category does not invalidate already-issued certificates, but it will move an in-progress user's denominators upward.

Example

curl -sS \
  -H "Authorization: Bearer $SCH_API_KEY" \
  https://api.limeplate.com/api/public/v1/certificates/users/1a2b3c4d-5e6f-7081-9203-4d5e6f708192

Verify a certificate

Looks up a certificate by its public number and returns the holder, organization, and category. Use this to back a "Verify your certificate" page on your career site, or to validate a candidate's claim during hiring.

GET /api/public/v1/certificates/verify/{certNumber}
Authorization: Bearer scs_live_…

Although the endpoint behaves like a public verification lookup, the API key and certificates:read scope are still required when calling it. If you want truly public verification with no auth, link to the SecureCodingHub-hosted verification URL instead: https://www.securecodinghub.com/verify/{certNumber}.

Response

{
  "certificateNumber": "SCH-2026-WEB-000123",
  "userName": "Jane Smith",
  "organizationName": "Acme Corp",
  "categoryId": "web",
  "categoryTitle": "Web Application Security",
  "issuedAt": "2026-05-12T08:41:22Z"
}

Returns 404 certificate_not_found if no certificate with that number exists. Revoked or test-fixture certificate numbers also return 404.

Example

curl -sS \
  -H "Authorization: Bearer $SCH_API_KEY" \
  https://api.limeplate.com/api/public/v1/certificates/verify/SCH-2026-WEB-000123

Receiving certificate events

If you want to push certificates into an HR/talent system the moment they are issued — rather than polling the user endpoint — subscribe to the certificate.issued webhook. The webhook payload includes userId, certificateNumber, categoryId, and issuedAt, mirroring the verification response. See Webhooks.