Generate personalized certificates for a name list
Produce one certificate Doc per recipient — Northwind's masterclass attendees, one PDF each.
Published Jul 13, 2025
After every Northwind masterclass, someone has to produce a certificate for each attendee — the same Doc, the same layout, only the name and course changing. Done by hand it means duplicating a template dozens of times, editing three fields in each copy, and hoping no two end up with the same name.
This script does the whole batch. It reads the attendee list, copies the certificate template once per person, swaps in their name, course, and today’s date, and saves each finished Doc to a Drive folder. A class of forty certificates becomes one click.
What you’ll need
- A certificate template Doc containing three placeholders:
{{name}},{{course}}, and{{date}}, placed wherever those values should appear. - An
Attendeessheet with a header row and three columns:name,email,course. - A Drive folder where the finished certificates are saved.
- The IDs of the template, the sheet, and the folder — fill them into the config block at the top of the script.
The script
// The certificate template Doc, copied once per attendee.
const TEMPLATE = '1abcCertTemplateId';
// The Attendees sheet: name, email, course.
const ATTENDEES = '1abcAttendeesId';
// The Drive folder finished certificates are saved into.
const CERTS_FOLDER = '1abcCertsFolderId';
/**
* Reads the attendee list and produces one personalised certificate
* Doc per attendee, saved to the certificates folder.
*/
function generateCertificates() {
// 1. Today's date, formatted for printing on the certificate.
const today = Utilities.formatDate(new Date(), 'GMT', 'd MMMM yyyy');
// 2. Read the attendee sheet — header row plus data rows.
const [h, ...rows] = SpreadsheetApp.openById(ATTENDEES)
.getSheets()[0]
.getDataRange()
.getValues();
if (!rows.length) {
Logger.log('No attendees found — nothing to generate.');
return;
}
// 3. Map header names to column indexes so rows are read by name.
const col = Object.fromEntries(h.map((k, i) => [k, i]));
const folder = DriveApp.getFolderById(CERTS_FOLDER);
// 4. Produce one certificate per attendee.
for (const r of rows) {
// Copy the template into the certificates folder.
const copy = DriveApp.getFileById(TEMPLATE).makeCopy(
`Certificate — ${r[col.name]}`, folder);
// Open the copy and swap each placeholder for this attendee's data.
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
body.replaceText('{{name}}', r[col.name]);
body.replaceText('{{course}}', r[col.course]);
body.replaceText('{{date}}', today);
doc.saveAndClose();
}
Logger.log('Generated ' + rows.length + ' certificates.');
}
How it works
generateCertificatesformats today’s date as something readable like13 July 2025— the date printed on every certificate in the batch.- It reads the whole
Attendeessheet, splitting the header row off from the data rows. If there are no data rows, it logs a message and stops. - It builds a
collookup that maps each header name to its column index, so later code can writer[col.name]instead of guessing a column number. - For each attendee row, it copies the template Doc into the certificates
folder, naming the copy
Certificate — <name>. - It opens the copy and replaces
{{name}},{{course}}, and{{date}}with that attendee’s values and the run date, then saves and closes the Doc. - The loop repeats until every attendee has a certificate.
Example run
Suppose the Attendees sheet holds:
| name | course | |
|---|---|---|
| Ada Cole | [email protected] | Advanced Spreadsheets |
| Ben Frost | [email protected] | Apps Script Basics |
Running the script on 13 July 2025 produces two Docs in the certificates folder:
| Doc name | {{name}} | {{course}} | {{date}} |
|---|---|---|---|
| Certificate — Ada Cole | Ada Cole | Advanced Spreadsheets | 13 July 2025 |
| Certificate — Ben Frost | Ben Frost | Apps Script Basics | 13 July 2025 |
Each Doc is the template with those three values filled in — ready to share, or to export as PDF.
Run it
This is an on-demand job — run it once after a masterclass:
- In the Apps Script editor, select
generateCertificatesand click Run. - Approve the authorisation prompt the first time — the script needs access to the Sheet, the template, and the Drive folder.
- Open the certificates folder to find one Doc per attendee.
Watch out for
- The script makes one copy per attendee every time it runs. Run it twice and you get duplicate Docs — clear the folder, or filter to attendees who do not already have a certificate, before re-running.
replaceTextfails silently if a placeholder is split across styled text. If part of{{name}}is bold in the template, the match misses — type each placeholder in one consistent style.- A large class can hit the six-minute execution limit, since each certificate is a copy plus an open-and-save. For a very long list, process the rows in batches across separate runs.
- The output is Docs, not PDFs. To deliver PDFs, export each copy with
copy.getAs('application/pdf')and save that to the folder. - The
emailcolumn is read into the sheet but not used here. It is there so you can extend the script to email each attendee their certificate as a next step — pair this with Mail merge with personalized PDF attachments to send every certificate out automatically.
Related
Generate personalized study guides from notes
Reformat raw notes into structured study guides — for Northwind's internal training programme.
Updated Feb 8, 2026
Build a contract-clause assembly system
Construct Northwind agreements from a library of approved clauses — drag-drop in code.
Updated Feb 1, 2026
Translate and resolve Doc comments
Localise reviewer feedback on a shared Doc so multilingual teams can collaborate.
Updated Jan 25, 2026
Auto-archive finalized Docs to dated folders
File completed Northwind Docs by month so the active folder stays focused on in-flight work.
Updated Jan 18, 2026
Build a fillable intake form inside a Doc
Create structured intake forms with placeholder fields readers can fill — for client briefs.
Updated Jan 11, 2026