Skip to main content

Table of Contents

  • Overview
  • Authentication
  • Base URL
  • Endpoints
    • 1. Get Certified Trainings by Student
    • 2. Get Certified Exams by Student
    • 3. Get Certified Students by Training
    • 4. Get Certified Students by Exam
    • 5. Get Certifications by User
    • 6. Create Certification
    • 7. Get Certification Details
    • 8. Get Global Certification Config
    • 9. Set Global Certification Config
    • 10. Generate Certification Handler URL
    • 11. Verify Certification
    • 12. View Certificate Handler (Web Page)
    • 12b. Certificate Preview Image (PNG)
    • 13. View Certification Verification Page
    • 14. Render Certificate from JSON
  • Postman Setup
  • Certificate Payload Reference
  • Error Handling

Overview

The Certifications API allows you to create, view, verify, and configure training and exam completion certificates. The current flow is based on a public certificate handler URL:
  • A certification is created through POST /api/certification
  • The API generates a unique public certification_id
  • A public handler URL is returned, such as /certificate/ID/CERT-TR00012-20260413-00004
  • The certificate page uses the resources/views/certificate.blade.php Blade file
Important changes in the current version:
  • Quiz-based certifications are now exam-only
  • Each student can have only one certification per training and one certification per exam
  • POST /api/certification is idempotent for the same student_id + training_id or student_id + quiz_id
  • If the certification already exists, the API returns the existing certification instead of creating a new one, with already_exists: true and a message saying that nothing new was generated
  • A certification is issued once and is never regenerated, even when lessons or quizzes are added to the training afterwards
  • Listing endpoints return duplicate-safe results
  • Listing endpoints load each certification’s training with its badge_logo file and each quiz with its image, so a client can draw the badge of a certificate without a second request
  • GET /api/certification/{id} now returns certification data instead of downloading a PDF
  • Archive, old preview URL, and old preview query-page flows are no longer part of the active certification flow
  • The certificate payload now includes:
    • name
    • course
    • date
    • duration
    • duration_label
    • subject_label
    • signatory
    • company_logo
    • badge_logo
    • certification_id

Authentication

Authenticated API endpoints require a Bearer Token via Laravel Sanctum:
The token is obtained from POST /api/auth/login. Public endpoints in the current certification flow:
  • POST /api/certification/handler-url
  • POST /api/certification/verify
  • GET /certificate/ID/{certification_id}
  • GET /certificate/verify
  • GET /certificate

Base URL

The public web routes do not use the /api prefix:

Endpoints

1. Get Certified Trainings by Student

Returns all training-based certifications for a given student. The response is duplicate-safe: only one certification per student per training is returned. Each training comes with its badge_logo file (null when the training has no badge of its own; clients then fall back to the company’s default_badge_logo, endpoint 8). Example Request
Success Response — 200 OK

2. Get Certified Exams by Student

Returns all exam-based certifications for a given student. Current supported URLs:
  • Preferred: /api/certified_exams_by_student/{student_id}
  • Backward-compatible alias: /api/certified_quizzes_by_student/{student_id}
Only exam certifications are returned here. Regular quizzes attached to trainings or chapters are excluded. The response is duplicate-safe: only one certification per student per exam is returned. Example Request
Success Response — 200 OK

3. Get Certified Students by Training

Returns all certifications issued for a specific training. The response is duplicate-safe: only one certification per student for that training is returned. Example Request
Success Response — 200 OK

4. Get Certified Students by Exam

Returns all certifications issued for a specific exam. Current supported URLs:
  • Preferred: /api/certified_students_by_exam/{quiz_id}
  • Backward-compatible alias: /api/certified_students_by_quiz/{quiz_id}
Only exam certifications are returned here. The response is duplicate-safe: only one certification per student for that exam is returned. Example Request
Success Response — 200 OK

5. Get Certifications by User

Returns all certifications belonging to a specific user, without separating training certifications from exam certifications. You may search by:
  • user_id
  • user_email
Send exactly one of those identifiers. The response is unified and duplicate-safe: a user can only have one certification per training and one certification per exam. Each certification embeds its source:
  • training (training certifications): the training row with its badge_logo file. badge_logo is null when the training has no badge of its own; the client then falls back to the company’s default_badge_logo (endpoint 8). This is the badge the Flutter Certificates tab draws on each earned certificate.
  • quiz (exam certifications): the exam row with its image file.
  • certification_source: training or exam.
  • certification_handler_url: the public certificate page.
Example Request by ID
Example Request by Email
Success Response — 200 OK
The training and quiz objects are the full model rows (the samples above are shortened). A training whose badge_logo_id is set but whose badge_logo is null has an archived or deleted badge file.

6. Create Certification

Creates a new certification for a student and sends an in-app notification when the certification is newly created. This endpoint is idempotent for the certification target:
  • Repeating the same student_id + training_id request returns the existing training certification
  • Repeating the same student_id + quiz_id request returns the existing exam certification
  • No second certification is created for the same student and target
A certification is issued once per student and training (or exam) and is never regenerated. It stays valid when the training later gains lessons or quizzes: this endpoint does not re-check completion, so a repeat request simply gets the existing certification back. Two unique database indexes (student_id + training_id and student_id + quiz_id) guarantee it even under concurrent requests. Both outcomes answer 200 OK. Use the already_exists flag to tell them apart instead of parsing message: The status is deliberately not 409: the goal of the request (the student holds a certification) is met, and the client needs the body to display it. Message when the certification already exists This message is translated from the Accept-Language header (en, fr, ar; any other value falls back to en). The “created” message is always in English. You must send exactly one of:
  • training_id
  • quiz_id
If quiz_id is used, it must point to an exam only. Example Request
Request Body (Training-based)
Request Body (Exam-based)
Validation Rules Success Response — 200 OK
Already Exists Response — 200 OK Returned for every repeat request, including after the training content has changed.
Validation Error — 422 Unprocessable Entity

7. Get Certification Details

Returns certification data, the public certificate handler URL, and the certificate payload used by the Blade handler. This endpoint no longer downloads a PDF. Example Request
Success Response — 200 OK

8. Get Global Certification Config

Retrieves the company-level certification display configuration used by the certificate payload builder. Example Request
Success Response — 200 OK
Default Response (no config saved) — 200 OK
default_badge_logo returns the public URL of the custom badge image uploaded via endpoint 9. When no value is saved for the company, it falls back to the platform default badge. The same resolution is used as the fallback badge_logo in the certificate payload when a training/exam has no badge of its own.
certificate_verify_url is where the app sends “Verify your certificate”. Like every certification_handler_url, it is built on the backend’s CERTIFICATE_PUBLIC_URL when that is set — the app’s own domain, whose web server proxies /certificate/* to the backend, so shared links never show the backend host (see Certification_Proxy_README.md in the backend and frontend repositories) — and on APP_URL otherwise.

9. Set Global Certification Config

Updates the company-level certification configuration. Only the fields below are currently used by the active handler flow. Form Data Parameters
default_badge_logo is an image upload (same as the other logos). Upload an image file and its public URL is stored and returned. Sending it empty clears the stored value back to the platform default; omitting it entirely leaves the current value unchanged. This value becomes the fallback badge in the certificate payload when a training/exam has no badge of its own.
Postman Setup
  1. Set method to POST
  2. Set body type to form-data
  3. Add company_logo as File when uploading a logo
  4. Add signatory as Text when changing the signer label
  5. Add default_badge_logo as File to upload a custom default badge
Success Response — 200 OK

10. Generate Certification Handler URL

Generates the public certificate handler URL from an existing certification. You may send either:
  • id
  • certification_id
This endpoint is public and does not require authentication. Request Body
Alternative Request Body
Success Response — 200 OK

11. Verify Certification

Verifies a certification by public certification_id. This endpoint is public and does not require authentication. Request Body
Success Response — 200 OK
Failure Response — 404 Not Found

12. View Certificate Handler (Web Page)

Renders the public certificate page using resources/views/certificate.blade.php. Example Request
Success Response — 200 OK The page renders a certificate with:
  • student full name
  • training label or exam label
  • duration
  • certification date
  • signatory
  • company logo
  • training badge logo or fallback badge
  • generated certification ID
  • copy certification ID button
Printing (since 07/10/2026). The page’s print stylesheet (@media print) sets @page { size: A4 landscape; margin: 0 }, hides the .no-print chrome (the action buttons and the details card) and makes the canvas container full-bleed, so the 1123×794 canvas (A4 ratio) prints edge-to-edge on a single sheet with object-fit: contain — no browser margins, no blank second page. On screens narrower than 1024px the certificate is shown above the details card. Link preview tags The page’s <head> carries Open Graph and Twitter Card tags, so a shared link previews as the certificate on LinkedIn, Facebook, X, WhatsApp or Slack. Link crawlers do not run the page’s canvas script, so the preview image is a server-drawn PNG (endpoint 12b). Every URL is built on CERTIFICATE_PUBLIC_URL when it is set, else on APP_URL.
When the learner’s name, the course or the signatory is written in a right-to-left script, no image is generated: og:image is the training badge and twitter:card is summary. Error Response — 404 Not Found If the certification identifier does not exist, Laravel returns the standard not found response/page.

12b. Certificate Preview Image (PNG)

The certificate drawn server-side as a 1200×630 PNG: the og:image / twitter:image of endpoint 12. Same layout as the page’s canvas (logo, badge, name, course, duration, date, signatory, certification ID). Example Request
  • Drawn on the first request and cached on the server. The ?v= that endpoint 12 appends is a fingerprint of the drawn content: it changes when the training, the learner’s name, the badge or the signatory change, so social networks fetch the new image. The image itself ignores the query string.
  • Under /certificate/, so the app-domain proxy described in Certification_Proxy_README.md serves it too.
Error Response — 404 Not Found The certification identifier does not exist, or its texts use a right-to-left script the image cannot render.

13. View Certification Verification Page

Renders the public certification verification page using resources/views/certificate_verify.blade.php. Example Request
Success Response — 200 OK The page lets users:
  • enter a public certification_id
  • click Verify Certif
  • see a success or failure message
  • render the certificate inline when verification succeeds
  • view the duration and certification ID in the rendered certificate

14. Render Certificate from JSON

Renders the certificate view using the local development JSON file at storage/app/certificate_data.json. This route is intended for local testing of the Blade view and requires no authentication. Example Request
Success Response — 200 OK The page renders the same Blade used by the public certification handler. Error Response — 404 Not Found

Postman Setup

Step 1 — Environment Variables

Create a Postman environment with:

Step 2 — Login & Save Token

Create a POST {{base_url}}/auth/login request:
In the Tests tab, add this script to auto-save the token:

Step 3 — Set Auth Header

For authenticated certification endpoints:
  • Go to Authorization
  • Type: Bearer Token
  • Token: {{token}}
Do not use auth for:
  • POST {{base_url}}/certification/handler-url
  • POST {{base_url}}/certification/verify
  • GET {{web_url}}/certificate/ID/{certification_id}
  • GET {{web_url}}/certificate/verify
  • GET {{web_url}}/certificate

Step 4 — Postman Collection Structure

Tips for Testing

  • Create Certification: Copy certification_handler_url from the API response and open it in a browser
  • Duplicate Protection: Repeating the same create request for the same student and training/exam returns the existing certification instead of creating a new one
  • Unified User Lookup: Use /certifications/by-user when you want all certifications for a learner, regardless of source
  • Verify Certification: Use /certification/verify from the public verification page or from Postman without auth
  • Handler URL Endpoint: Useful when you already know the certification database id or public certification_id
  • Config Uploads: Use form-data and set company_logo as File
  • Exam Certifications: Always use an exam quiz_id, not a training/chapter quiz

Certificate Payload Reference

The public certificate handler and the local render route both use the following payload structure:
For local testing, GET /certificate reads from:
Image URLs are converted server-side to base64 data URLs when rendering the Blade, to avoid browser-side CORS issues.

Error Handling

All endpoints follow a consistent error format: Standard error shape
The errors object is only present for validation failures.