Bulk import
Issue many certificates from one CSV or XLSX file, with a review stage that creates nothing until you confirm it.
Import is a five-stage workflow, not an upload button. Validation happens before anything is created, so a file with mistakes in it costs you a correction rather than a hundred wrong certificates.
The five stages
- Upload.
.csvor.xlsx, up to the size and row limits below. The header row is checked first: an unexpected or missing column stops the file before any row is read. - Validation. Every row is checked against the rules in the table below and stored in a staging area with its own result. Nothing is created.
- Correction. The review screen shows each row, valid or not, with the reason for every rejection. Fix them in your file and upload again.
- Confirmation. Only an explicit confirmation creates certificates. This is also where you choose whether verification is required, which verifier receives them, and whether notification emails are queued.
- Result. A summary of what was created and what failed. Confirming twice does not create a second copy: the batch resumes from row state and finishes once.
The columns
The header row must match exactly. Download the template from the import screen, or take
it from Sample-Files/certificate-import-template.csv in the package, which is
written from the same list the parser uses.
| # | Column | Required | Format | Notes |
|---|---|---|---|---|
| 1 | course_id | Yes | Whole number, 1 or higher | The course this certificate belongs to. Read it from the course list. |
| 2 | course_run_id | Yes | Whole number, 1 or higher | The dated occurrence of that course. It must belong to course_id, or the row is rejected. |
| 3 | certificate_template_id | Yes | Whole number, 1 or higher | The certificate layout to render. It must belong to course_id. |
| 4 | participant_first_name | Yes | Text, up to 120 characters | Printed on the certificate exactly as written here. |
| 5 | participant_last_name | Yes | Text, up to 120 characters | Printed on the certificate exactly as written here. |
| 6 | participant_email | Yes | Email address, up to 255 characters | Every certificate is issued to an address, so a notification can always be delivered. A row without one is rejected. |
| 7 | participant_phone | No | Text, up to 80 characters | Never printed on the certificate and never shown publicly. |
| 8 | participant_organization | No | Text, up to 255 characters | The participant employer, not your own organisation. |
| 9 | participant_position | No | Text, up to 180 characters | Job title, if your certificates show one. |
| 10 | certificate_number | Yes | Text, up to 120 characters | Your own numbering. It must be unique across the whole system, and it is one of the two values the public can verify with. |
| 11 | unique_id | No | Text, up to 120 characters | Leave it empty and the system generates one. It must be unique if you supply it. |
| 12 | issued_at | Yes | Date as YYYY-MM-DD | Cannot be in the future. |
| 13 | expires_at | No | Date as YYYY-MM-DD | Must not be earlier than issued_at. Leave it empty for a credential that does not expire. |
The header row must contain exactly these 13 names. A missing column, an extra column or a different spelling is refused before any row is read, and the screen names the column it could not match.
A valid file
course_id,course_run_id,certificate_template_id,participant_first_name,participant_last_name,participant_email,participant_phone,participant_organization,participant_position,certificate_number,unique_id,issued_at,expires_at
1,1,1,Anna,Larsen,anna.larsen@example.com,+1 555 0101,Example Trading LLC,Analyst,CF-2026-0001,,2026-03-14,2029-03-14
1,1,1,Chen,Wei,chen.wei@example.com,,Example Manufacturing JSC,Compliance Officer,CF-2026-0002,,2026-03-14,
The package includes filled examples in Sample-Files/certificate-import-sample.csv
and Sample-Files/certificate-import-sample.xlsx. They contain the same three
fictional participants. The three numeric columns are identifiers from your own data, so
change them to match a related course, occurrence and template before uploading. Keep XLSX
identifiers and ISO dates as text. Use new certificate numbers if you have already imported
the examples.
Sample-Files/certificate-import-invalid.csv contains five deliberate row errors
for inspecting the correction screen: a missing given name, an invalid email, a nonexistent
issue date, a missing certificate number and an expiry before issue. Its headers remain valid.
Read Sample-Files/README.md for setup and the expected error for each row. The same
directory includes two blank A4 landscape PNG backgrounds and their reuse licence; upload one
through the course template editor and place your own fields before issuing.
Where the identifiers come from
The three numeric columns are database identifiers, and the import screen is not where you guess them.
course_idis shown in the course list.course_run_idis shown in the occurrence list for that course.certificate_template_idis shown in the certificate template list.
All three lists show their identifiers to Admins and Managers precisely so an import file can be prepared without a database client. An occurrence or template belonging to a different course is rejected row by row, with the reason.
Limits
| Limit | Set by | What happens at the limit |
|---|---|---|
| File size | UPLOAD_MAX_MB | The upload is refused before it is read. |
| Data rows | IMPORT_MAX_ROWS | The file is refused. Split it and upload the parts. |
Uncompressed .xlsx size | IMPORT_XLSX_MAX_UNCOMPRESSED_MB | The file is refused. This is what stops a small archive that expands to gigabytes. |
Why a row was rejected
| Message | Cause |
|---|---|
| A required field is empty | One of the required columns has no value in that row. The message names the column. |
| A value must be a whole number | One of the three identifier columns holds text, a decimal or a zero. |
| The occurrence or template does not belong to the course | The identifiers are valid on their own but do not belong together. |
| The issue date is invalid or in the future | issued_at is not YYYY-MM-DD, or it is later than today. |
| The expiry date is before the issue date | expires_at precedes issued_at. |
| The certificate number is already in use | Numbers and unique IDs are unique across the installation, including against certificates issued months ago. |
| The email address is not valid | participant_email is malformed. Correct it rather than putting a placeholder in it: the column is required, and a placeholder becomes a notification nobody receives. |
Excel and dates. Excel likes to reformat 2026-03-14 into
something local. Format the two date columns as text before typing, or save as CSV and check
the result in a text editor. This is the single most common reason a prepared file is
rejected.
One import belongs to the person who uploaded it. A staged batch can be reviewed and confirmed by its uploader, and by an Admin. Another Manager cannot see or confirm someone else's staged file.