Database reference

Every table the schema creates, what it holds, and the rules that matter when you query it directly.

The authoritative schema is database/schema.sql in the package. php bin/migrate.php applies it, and re-applying it is safe: existing tables, columns and indexes are left alone.

Tables

The schema creates 41 tables. It uses utf8mb4 throughout, so names in any script are stored and printed correctly.

TableWhat it holds
api_certificate_issuancesThe external reference and owning API client for each certificate created through the API.
api_clientsExternal systems allowed to call the versioned certificate API, with their status and rate limit.
api_idempotency_keysCanonical request hashes and completed write responses used to make concurrent API retries safe.
api_tokensScoped integration credentials stored as hashes, with expiry, revocation and last-used timestamps.
audit_logsAppend-only record of who did what, with sanitised metadata, address and user agent.
certificate_attachmentsSupporting files attached to a certificate.
certificate_document_versionsPrevious render snapshots, kept when a name correction replaces a PDF.
certificate_documentsOne row per issued PDF, with its file hash, approved business values and last-render presentation. Appearance follows the current template and verification follows the current certificate.
certificate_integration_mappingsClient-owned external course and run keys mapped to a local occurrence, template and optional verifier.
certificate_status_historyEvery status transition, with who made it and when.
certificate_versionsPre-edit snapshots of certificate and participant details, written before any edit.
certificatesThe certificate record: its numbers, dates, status, verifier assignment and public token.
course_certificate_templatesNamed certificate layouts for a course, each with a background image and field positions.
course_runsDated occurrences of a course, with their own description.
coursesThe training programmes certificates are issued for.
email_queueOutgoing mail waiting to be sent, with attempt counters.
email_verification_tokensOne-time activation links, stored as hashes rather than as the link itself.
export_jobsRequested exports and the files produced for them.
import_batchesOne row per uploaded import file, with its staged, confirmed and completed state.
import_rowsOne row per data row in an import, with its validation state and resulting certificate.
login_attemptsRate-limit counters for sign-in, two-factor, activation, reset and public verification.
notificationsIn-application notices for internal users.
organizationsVerifier organisations, so a verifier account can be shown with the body it belongs to.
participantsThe people certificates are issued to. There is deliberately no national identity-number column.
password_reset_tokensOne-time password-reset links, stored as hashes.
permissionsThe individual permissions a role can hold.
role_permissionsWhich permissions a role holds. This is the only source of authority; per-user grants are removed by the seed.
rolesThe four roles: System Admin, Manager, Verifier and Viewer.
saved_filtersSaved list filters, per user.
security_eventsSecurity-relevant events such as lockouts and refused hosts.
system_settingsRuntime settings owned by administrators: branding, sender address, SMTP connection and feature switches.
two_factor_recovery_codesSingle-use recovery codes, stored as password hashes.
two_factor_secretsAuthenticator secrets, encrypted with the application key.
user_permissionsKept for older installations and deliberately emptied by the seed, so no account can exceed its role.
user_rolesWhich role an account holds. One account holds exactly one role.
user_sessionsActive sessions, so disabling an account or changing its role can revoke them immediately.
usersEvery account, internal and verifier, with status, role requirement for two-factor authentication and locale preference.
verification_assignmentsWhich verifier account is responsible for which certificate.
verification_decisionsVerify, return and reject decisions, with the note the verifier left.
verification_requestsRequests sent to a verifier organisation for approval.
webhook_deliveriesImmutable signed event payloads and at-least-once delivery attempts for API clients.

How the main records relate

courses
  |- course_runs                  dated occurrences of the course
  |- course_certificate_templates named layouts, one background each
       |
certificates                      one per issued credential
  |- participants                 the person it was issued to
  |- verification_assignments      which verifier is responsible
  |- verification_decisions        verify, return, reject, with the note
  |- certificate_status_history    every transition
  |- certificate_versions          pre-edit snapshots
  |- certificate_documents         one issued PDF, with its render snapshot
       |- certificate_document_versions   previous renders after a name correction

Rules to respect if you query directly

Character set and time

Every table is utf8mb4 with a Unicode collation, so names in Cyrillic, Greek or any other script are stored and printed correctly. Keep DB_CHARSET=utf8mb4: a narrower connection charset corrupts names on the way in.

Timestamps are written using the offset of APP_TIMEZONE. Changing that setting later does not rewrite existing rows, so pick it during installation and leave it.

Applying the schema and the seed

php bin/migrate.php        # create or update the tables
php bin/seed.php           # roles, permissions and default settings
php bin/create-admin.php   # an administrator, if you have no browser installer

bin/seed.php is safe to re-run and never touches accounts or certificates. It does, however, empty user_permissions and rebuild the role grants, which is how the product guarantees that authority comes from the role alone.