Turn Google Form submissions into PDF invoices automatically
Every service business has the same midnight: a form submission comes in, and someone manually re-types it into an invoice document, saves a PDF, attaches it to an email, and hits send. It takes five minutes per order and fails exactly when you're busiest.
Here is the entire pipeline I build for clients — form to branded PDF invoice in their inbox, in under a minute, for the cost of a Google account they already pay for.
The pieces
- Google Form — collects the order (client name, items, amount, email).
- Google Sheet — the Form's response sheet doubles as the invoice ledger.
- Google Doc template — the invoice layout with
{{placeholders}}where the data goes. - Apps Script — the glue: on submit, copy the template, replace placeholders, export PDF, email it.
The trigger
In your Form's response sheet: Extensions → Apps Script, then wire the on-submit trigger so the script runs the moment a response lands:
function onSubmit(e) {
const row = e.range;
const sheet = e.range.getSheet();
const values = sheet.getRange(row.getRow(), 1, 1, sheet.getLastColumn()).getValues()[0];
processInvoice(values);
}
Template → PDF → Email
The heart of the system is three Apps Script calls. Copy the Doc template, swap the placeholders, export as PDF:
function processInvoice([timestamp, name, items, amount, email]) {
const doc = DriveApp.getFileById('TEMPLATE_ID').makeCopy(`Invoice - ${name}`);
const body = DocumentApp.openById(doc.getId()).getBody();
const replacements = {
'{{name}}': name,
'{{items}}': items,
'{{amount}}': amount,
'{{invoice_no}}': 'INV-' + Date.now().toString(36).toUpperCase(),
'{{date}}': new Date().toLocaleDateString('en-GB')
};
for (const [key, value] of Object.entries(replacements)) {
body.replaceText(key, value);
}
body.saveAndClose();
const pdf = DriveApp.getFileById(doc.getId()).getAs('application/pdf');
GmailApp.sendEmail(email, 'Your invoice', 'Hi! Your invoice is attached.', {
attachments: [pdf],
name: 'Noushad — Invoicing'
});
DriveApp.getFileById(doc.getId()).setTrashed(true); // ledger stays in the Sheet
}
Details that separate a demo from production
- Idempotency: mark each row "invoiced" in a status column before sending, so a trigger retry never double-bills a client.
- Error mailbox: wrap the pipeline in try/catch and forward failures to yourself — a silent failure is worse than no automation.
- Quotas: consumer Gmail allows ~100 recipients/day; Workspace accounts get 1,500. Above that, move to a second-stage design.
- Line items: if invoices need itemised tables, build the table rows in the Doc with
appendTableRowinstead of a single{{items}}field.
Total cost: one Google account. Setup time: about two hours. This is the cheapest, most reliable automation I ship — and it's usually the project that turns a skeptical client into a repeat one.
Want this in your Workspace? Tell me what your form collects and I'll map it to an invoice template in one call.