Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Stivenz3/Nexu/llms.txt

Use this file to discover all available pages before exploring further.

Nexu certificates carry a scannable QR code that links to a public verification page. Anyone — employers, inspectors, or health authorities — can open that URL and confirm the certificate is authentic without creating an account or logging in. The verification page reads exclusively from the certificatePublic Firestore collection, which is intentionally separate from the private certificates data.

Route and Access

The verification route is /verificar/:code, rendered by VerificationPage. This page has no authentication requirement — it is accessible in an incognito window, on a mobile device that has never visited Nexu, or by scanning the QR on a printed certificate.
https://{your-domain}/verificar/NX-AB12CD34EF
VerificationPage reads the :code URL parameter and calls getPublicVerification(code). If no matching document is found, a “Certificado no encontrado” error state is shown. No backend function, no auth token, no session.

Service Function: getPublicVerification

// src/services/certificateService.ts
export async function getPublicVerification(
  verifyCode: string
): Promise<PublicVerificationView | null>
The lookup is case-insensitive: it first tries verifyCode.toUpperCase() (the canonical format), and if that document does not exist, falls back to the code as supplied. This ensures codes typed in lowercase or mixed-case still resolve correctly. Internally, it reads the single document at certificatePublic/{verifyCode} from Firestore and maps it to a PublicVerificationView via the private mapPublic helper, which also recomputes the live status using resolveStatus(expiresAt, isValid).

PublicVerificationView Fields

FieldTypeDescription
verifyCodestringThe verification code (e.g. NX-AB12CD34EF)
userNamestringFull name of the certificate holder
userDocumentMaskedstringMasked ID — e.g. CC. ***6789
lessonTitlestringDisplay title of the course or legacy lesson
courseNamestring"Buenas Prácticas de Manufactura"
finalScorenumberAverage exam score across all completed lessons (0–100)
completedLessonsCompletedLessonSnapshot[]Array of passed lesson snapshots with per-lesson scores
issuedAtDateDate the certificate was issued
expiresAtDateDate the certificate expires (issuedAt + 1 year)
status'valid' | 'expired' | 'revoked'Live status at time of verification

Status Resolution

The status field is not read from Firestore — it is re-evaluated on every verification request using:
function resolveStatus(expiresAt: Date, isValid: boolean): CertificateStatus {
  if (!isValid) return 'revoked'
  if (expiresAt.getTime() < Date.now()) return 'expired'
  return 'valid'
}
This means a certificate that was valid at creation will automatically transition to expired after one year, with no background job or Firestore update required.

Firestore Rule for Public Collection

The certificatePublic collection has allow read: if true — unauthenticated reads are explicitly permitted. Writes are restricted to the authenticated owner, and updates and deletes are completely denied:
// firestore.rules
match /certificatePublic/{verifyCode} {
  allow read: if true;
  allow create: if request.auth != null
    && request.resource.data.userId == request.auth.uid;
  allow update, delete: if false;
}
This means the public verification document is immutable after creation — there is no way to alter the displayed information through the client.

Privacy Model

All personal data stored in certificatePublic is limited to what is necessary for verification. The document number is masked before it is ever written to the public collection:
function maskDocument(documentType: string, documentNumber: string): string {
  const tail = documentNumber.slice(-4)
  return `${documentType.toUpperCase()}. ***${tail}`
  // e.g. "CC. ***6789"
}
The full, unmasked userDocument field lives only in certificates/{certificateId}, which is readable solely by the authenticated owner.

Testing Verification

To manually verify a certificate end-to-end:
1

Complete the course

Log in, complete all available lessons, and pass each final exam with a score of 70% or higher.
2

Open the official certificate page

Navigate to /certificado/documento. The certificate with the QR code and verification URL is displayed in the ActionsCard.
3

Copy the verification link

Click “Compartir verificación” to copy the qrVerifyUrl to the clipboard, or scan the QR code directly.
4

Open in incognito mode

Paste the URL into a private/incognito browser window where you are not logged in to Nexu. The VerificationPage should load and display the certificate holder’s name, masked document, course name, score, and validity dates.
5

Confirm status badge

A green “Certificado válido” header and a “Verificado” badge confirm the certificate is active. An “expired” or “revoked” status renders a red state instead.

Maintenance Notes

The public verification page reads only from certificatePublic. It does not call any Cloud Function, does not query the private certificates collection, and does not require any authentication context. When new lessons are added to the course, no changes are needed in VerificationPage or getPublicVerification — the completedLessons snapshot stored at certificate creation time already captures whatever lessons the user passed.
Never store the full userDocument field (e.g. CC. 123456789) in the certificatePublic collection. Always use maskDocument(documentType, documentNumber) before writing to certificatePublic. The Firestore security rule does not enforce this at the field level — it is a code-level responsibility enforced exclusively inside issueCourseCertificate in certificateService.ts.

Build docs developers (and LLMs) love