Build a QR-code check-in app
Scan to register attendance at Northwind events — the page logs to a Sheet.
Published Sep 5, 2025
Northwind runs a handful of client events each year, and the door always becomes a bottleneck — someone with a printed list ticking names while a queue forms. Paid event platforms solve this, but they are overkill for a 40-person breakfast briefing, and they leave the attendance data locked in someone else’s dashboard.
This automation is a one-function web app. Every attendee’s badge carries a QR code that points at the web app URL with their ID attached. Scanning it with any phone camera opens the page, which logs the arrival to a Sheet and shows a confirmation. The whole “system” is a single Sheet and a few lines of script, and the attendance data stays yours.
What you’ll need
- A Google Sheet to hold check-ins, with a header row:
Timestamp,Attendee ID. Copy its ID from the URL. - A list of attendee IDs — anything unique works (a booking reference, an email, a short code).
- A QR code per attendee encoding
WEBAPP_URL/exec?id=ATTENDEE_ID. Most badge-printing tools and free QR generators accept a URL directly. - The project deployed as a web app, set to execute as you and accessible to anyone (see Deploy it).
The script
The QR on each badge links to WEBAPP_URL/exec?id=ATTENDEE_ID. Loading that URL
runs doGet, which records the arrival.
// The Sheet that collects check-ins. Copy the ID from its URL.
const CHECKINS_SHEET_ID = '1abcCheckinsId';
/**
* Runs when an attendee's QR code is scanned. The QR encodes the web app
* URL with ?id=ATTENDEE_ID, so e.parameter.id identifies who arrived.
*
* @param {Object} e The event object Apps Script passes to a web app.
* @returns {HtmlOutput} A confirmation page shown on the attendee's phone.
*/
function doGet(e) {
// Guard: a scan with no id is a malformed or hand-typed URL.
const id = e && e.parameter ? e.parameter.id : null;
if (!id) {
return HtmlService.createHtmlOutput('Missing id — please ask staff for help.');
}
const sheet = SpreadsheetApp.openById(CHECKINS_SHEET_ID).getSheets()[0];
// Record the arrival: a timestamp plus the scanned attendee ID.
sheet.appendRow([new Date(), id]);
// Show a confirmation on the phone that scanned the badge.
return HtmlService.createHtmlOutput('Checked in: ' + id);
}
How it works
- An attendee scans the QR code on their badge. Their phone camera opens the
web app URL, which carries
?id=ATTENDEE_ID. - Loading that URL runs
doGet, and Apps Script passes the query string ine.parameter. - The script reads
e.parameter.id. If there is noid— a hand-typed URL or a bad scan — it returns a friendly message and stops, so the Sheet never gains a blank row. - It opens the check-ins Sheet and appends a row: the current time and the scanned ID.
- It returns a small HTML page confirming the check-in, which the attendee sees on their phone within a second of scanning.
Example run
Three attendees scan their badges as they arrive. Each scan appends a row to the check-ins Sheet:
| Timestamp | Attendee ID |
|---|---|
| 2026-05-25 08:31 | NW-1042 |
| 2026-05-25 08:33 | NW-1108 |
| 2026-05-25 08:36 | NW-1042 |
The attendee who scanned NW-1042 sees Checked in: NW-1042 on their phone.
Note the duplicate row at 08:36 — a second scan logs a second time (see
Watch out for).
Deploy it
The QR codes only work once the project is published as a web app:
- In the Apps Script editor, click Deploy then New deployment.
- Choose Web app as the type.
- Set Execute as to yourself and Who has access to Anyone.
- Click Deploy, approve the authorisation prompt, and copy the
/execURL. - Generate each badge’s QR from
WEBAPP_URL/exec?id=ATTENDEE_ID, substituting the real ID, and print them on the badges.
Test one badge before the print run — scan it and confirm a row lands in the Sheet.
Watch out for
- Scans are not deduplicated. A second scan of the same badge adds a second
row. To check in each attendee once, read the Sheet first and skip the
appendRowif the ID is already present. - There is no attendee validation. Any
idvalue is accepted as-is. To reject unknown IDs, keep an attendee list in a second tab and look the scanned ID up before logging. - Re-deploy after edits. Saving the script is not enough — publish a new version of the deployment, or scans hit the old build.
- Anyone with the URL can check in. The
?id=value is visible in the URL, so a curious attendee could check in as someone else. For a low-stakes event briefing this is usually fine; for anything sensitive, add a per-attendee token instead of a guessable ID. doGetis read-style but writes here. Browsers and link previewers sometimes pre-fetch URLs, which can trigger an unintended check-in. For a high-stakes count, move the write behind a confirm button on the page.
Related
Build a branded approval interface
Approve Northwind requests through a custom UI — clients click, decision is logged.
Updated Nov 8, 2025
Build an interactive quiz or assessment app
Run Northwind tests with scoring and feedback — questions in a Sheet, results in another.
Updated Nov 4, 2025
Build a multi-page web app with routing
Structure a real Northwind app across views — query-param routing, shared layout.
Updated Oct 31, 2025
Build a form-to-PDF web service
Convert Northwind form submissions to PDFs on the fly — POST in, PDF out.
Updated Oct 27, 2025
Build an expiring secure-download generator
Issue time-limited Northwind links via a web app — token in URL, server-side check.
Updated Oct 23, 2025