Recipe: event badges in bulk
Goal: turn the attendee list exported from your ticketing tool into one print-ready badge per person.
You’ll use: a badge template, a CSV file, and a Node.js script that calls the export API with limited concurrency.
1. Design the badge
Section titled “1. Design the badge”-
Create a template named
Badge. -
Add a colored band at the top with the event logo, and a band at the bottom whose color reflects the ticket type (you can design one template per ticket type, see below).
-
Add the variables, large and centered:
Variable Required Default Style firstName✅ Anton, 96 px lastName✅ Open Sans, 48 px companyOpen Sans, 32 px, grey ticketAttendeeCourier Prime, 28 px, bold
2. Prepare the data
Section titled “2. Prepare the data”firstName,lastName,company,ticketAda,Lovelace,Analytical Engines,SpeakerGrace,Hopper,US Navy,AttendeeAlan,Turing,,StaffColumn names match variable names exactly, so each row can be sent as-is.
3. Generate the badges
Section titled “3. Generate the badges”Directorybadges/
- attendees.csv
- generate.mjs
Directoryout/
- …
import { readFile, writeFile, mkdir } from 'node:fs/promises';
const TEMPLATE_ID = process.env.BADGE_TEMPLATE_ID;const API_KEY = process.env.IMPRIME_API_KEY;const CONCURRENCY = 4;
// Minimal CSV parsing — use a real CSV library if fields may contain commas or quotesconst [header, ...lines] = (await readFile('attendees.csv', 'utf8')).trim().split(/\r?\n/);const keys = header.split(',');const attendees = lines.map((line) => Object.fromEntries(line.split(',').map((value, i) => [keys[i], value])),);
await mkdir('out', { recursive: true });
async function renderBadge(attendee, index) { const res = await fetch(`https://app.imprime.io/api/export/${TEMPLATE_ID}/pdf`, { method: 'POST', headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' }, body: JSON.stringify(attendee), }); if (!res.ok) throw new Error(`${attendee.firstName} ${attendee.lastName}: ${await res.text()}`);
const file = `out/${String(index + 1).padStart(3, '0')}-${attendee.lastName}.pdf`; await writeFile(file, Buffer.from(await res.arrayBuffer())); console.log('✓', file);}
// Simple worker pool: at most CONCURRENCY requests in flightlet next = 0;const failures = [];await Promise.all( Array.from({ length: CONCURRENCY }, async () => { while (next < attendees.length) { const i = next++; await renderBadge(attendees[i], i).catch((err) => failures.push(err.message)); } }),);
if (failures.length) { console.error(`\n${failures.length} badge(s) failed:\n` + failures.join('\n')); process.exit(1);}BADGE_TEMPLATE_ID=<templateId> IMPRIME_API_KEY=<key> node generate.mjs4. Variations
Section titled “4. Variations”- One template per ticket type — map
ticketto a template ID in the script ({ Speaker: '…', Staff: '…' }) to change colors per audience. - Print sheet — merge the generated PDFs with a tool like
pdfuniteorqpdfto send a single file to the printer.