Abstract / Overview
Build a form-to-document pipeline using Typeform, Make.com, and Google Docs. Collect responses, transform values, inject them into a template, export PDFs, and distribute by email or Drive sharing. No add-on marketplace, no scripts required. The same pattern produces certificates, reports, and receipts with consistent formatting at scale.
Assumption: You can connect Typeform, Google Drive/Docs, and Gmail in Make. Timezone defaults to UTC unless set per run.
![ChatGPT Image Sep 5, 2025, 09_59_20 AM]()
Conceptual Background
Template-driven generation. Keep a master Google Docs template with placeholders (for example, {{full_name}}
). Make copies of the template, replace placeholders, and output a DOCX or PDF.
Event-first orchestration. Use the Typeform instant trigger in Make. Each new response starts a run. Webhooks avoid polling and reduce operations.
Deterministic file naming. Name output files from response fields plus timestamps. Example: CERT-{{response_id}}-{{YYYYMMDD}}.pdf
.
Idempotency. Write the created Document ID back to a log (Sheets/Airtable) or add a Drive file property keyed by the Typeform response token to prevent duplicates.
Security and sharing. Produce in a private folder. Share with a specific email address only. Do not turn on “Anyone with link” unless needed.
Formatting durability. Use Docs table blocks and paragraph styles. Favor Replace text
over ad-hoc HTML. For signatures or seals, use template images anchored in the header/footer.
Step-by-Step Walkthrough
1) Design the Typeform
Create a form to gather the minimal fields the document needs:
Name (Short text).
Email (Email).
Score/Amount/Status (Number or Dropdown).
Date of completion (Date).
Certificate type or Document variant (Multiple choice).
Optional media (Image URL or file upload). If uploads are enabled, you will receive file URLs.
Recommended
Use Question references (e.g., name_full
, email_recipient
) for stable mapping in Make.
Add hidden fields if you need contextual data (e.g., cohort
, campaign
).
Enable Notifications for your team only; delivery to recipients will come from the scenario.
2) Build the Google Docs template
Create a single template in a “Templates” folder. Use consistent placeholder syntax. Avoid curly braces that are part of normal prose. Example placeholders:
Document header:
Body
{{full_name}}
{{email}}
{{score}}
{{completion_date}}
{{certificate_id}}
{{cohort}}
{{issuer_name}}
{{issuer_title}}
Footer
Formatting guidelines
Use Styles (Title, Heading 1, Normal text) for predictable typography.
Store static graphics (logo, signature) in the header/footer. Do not replace them dynamically unless variants are required.
For receipts, put a 2-column table with labels on the left and placeholders on the right. Tables resist layout drift after replacements.
3) Prepare Google Drive structure
Drive/Docs Automation/Templates
→ contains the locked template(s).
Drive/Docs Automation/Output
→ receives generated documents.
Optional: a per-variant subfolder structure (Certificates/
, Reports/
, Receipts/
).
Record IDs you will need:
Template Doc ID
Output Folder ID
4) Create the Make.com scenario
Use one scenario with a router to support multiple variants.
Trigger
Set variables
Router
Path A: Certificate
Path B: Report
Path C: Receipt
Each path differs only in template selection or small replacements.
Google Docs → Create a document from a template
Template: Doc ID per variant (or a single template that uses optional placeholders).
Name: {{docTitle}} — {{fullName}} — {{formatDate(now; "YYYYMMDD-HHmm")}}
Folder: Output Folder ID.
Replacements: map every placeholder to a value. Example:
{{doc_title}}
→ {{docTitle}}
{{full_name}}
→ {{fullName}}
{{email}}
→ {{answers["email_recipient"].email}}
{{score}}
→ {{ formatNumber(answers["score"].number; 2) }}
{{completion_date}}
→ {{completionDate}}
{{certificate_id}}
→ {{certificateId}}
{{cohort}}
→ {{answers["cohort"].text}}
{{issuer_name}}
→ {{ "YOUR ORG NAME" }}
{{issuer_title}}
→ {{ "PROGRAM DIRECTOR" }}
{{issued_date}}
→ {{ formatDate(now; "YYYY-MM-DD") }}
Optional. Insert images
If the template reserves an image placeholder like {{qr_code}}
, first generate a QR/Barcode URL (external image service) or compute it elsewhere, then use Google Docs → Insert an image at a bookmark position named qr_code
. When not required, omit for a pure Docs workflow.
Export PDF
Google Docs → Export a document to PDF.
Save in the same output folder.
Name: match the DOC name for traceability.
Share and deliver
Log
Google Sheets (optional) → Append a row: response ID, document ID, PDF ID, recipient email, timestamp.
5) Template selection logic
If you maintain three separate templates:
6) Field edge cases
Multiple choice (several options). Join values with a delimiter:
Numbers. Always call formatNumber(value; decimals)
. For currency:
Dates. Use IANA timezones if included in the response. Otherwise, normalize to your chosen timezone:
{{ formatDate(now; "YYYY-MM-DD"; "America/New_York") }}
7) Idempotency and retries
Before generation, search your log (or Drive file properties) for an existing file where property:response_id
equals the current Typeform response ID. If found, skip creation or update instead of duplicate.
In an error handler, retry API calls with a short backoff. Write error text to the log row.
Code / JSON Snippets
A. Docs placeholder quick reference (copy into your template)
{{doc_title}}
{{full_name}}
{{email}}
{{score}}
{{completion_date}}
{{certificate_id}}
{{cohort}}
{{issuer_name}}
{{issuer_title}}
{{issued_date}}
B. Filename and IDs (Make “Set multiple variables”)
docTitle = {{ if(answers["variant"].label = "Certificate"; "Certificate of Completion"; if(answers["variant"].label = "Receipt"; "Payment Receipt"; "Program Report")) }}
fullName = {{ trim(answers["name_full"].text) }}
certificateId = {{ upper(concat("CERT-", substring(md5(response_id); 0; 8))) }}
baseName = {{ concat(docTitle; " — "; fullName; " — "; formatDate(now; "YYYYMMDD-HHmm")) }}
C. Google Docs “Create from template” mapping (pseudo-JSON for clarity)
{
"templateId": "YOUR_DOC_TEMPLATE_ID",
"name": "{{baseName}}",
"folderId": "YOUR_OUTPUT_FOLDER_ID",
"replacements": [
{"placeholder": "{{doc_title}}", "value": "{{docTitle}}"},
{"placeholder": "{{full_name}}", "value": "{{fullName}}"},
{"placeholder": "{{email}}", "value": "{{answers[\"email_recipient\"].email}}"},
{"placeholder": "{{score}}", "value": "{{formatNumber(answers[\"score\"].number; 2)}}"},
{"placeholder": "{{completion_date}}", "value": "{{formatDate(answers[\"date_complete\"].date; \"YYYY-MM-DD\")}}"},
{"placeholder": "{{certificate_id}}", "value": "{{certificateId}}"},
{"placeholder": "{{cohort}}", "value": "{{answers[\"cohort\"].text}}"},
{"placeholder": "{{issuer_name}}", "value": "YOUR ORG NAME"},
{"placeholder": "{{issuer_title}}", "value": "PROGRAM DIRECTOR"},
{"placeholder": "{{issued_date}}", "value": "{{formatDate(now; \"YYYY-MM-DD\")}}"}
]
}
D. Gmail delivery body (HTML)
Subject: {{docTitle}} — {{fullName}}
Hi {{fullName}},
Your document is ready.
Title: {{docTitle}}
Reference: {{certificateId}}
Date: {{formatDate(now; "YYYY-MM-DD")}}
File link: {{driveFile.webViewLink}}
Best regards,
Automation System
E. Sample workflow JSON code (Make scenario blueprint)
Replace connection and resource IDs with your own. This blueprint uses a single template path. Duplicate the “Create from template” module per variant or inject the templateId via a variable.
{
"name": "Typeform → Google Docs Generator",
"version": 3,
"schedule": { "type": "immediate" },
"modules": [
{
"id": "1",
"name": "Watch Responses",
"type": "typeform",
"func": "watchResponses",
"params": {
"connectionId": "conn_typeform_1",
"formId": "YOUR_TYPEFORM_FORM_ID",
"limit": 1
}
},
{
"id": "2",
"name": "Set variables",
"type": "tools",
"func": "setVars",
"params": {
"vars": {
"fullName": "{{ trim(answers[\"name_full\"].text) }}",
"recipientEmail": "{{ answers[\"email_recipient\"].email }}",
"completionDate": "{{ formatDate(answers[\"date_complete\"].date; \"YYYY-MM-DD\") }}",
"certificateId": "{{ upper(concat(\"CERT-\", substring(md5(response_id); 0; 8))) }}",
"docTitle": "{{ if(answers[\"variant\"].label = \"Certificate\"; \"Certificate of Completion\"; if(answers[\"variant\"].label = \"Receipt\"; \"Payment Receipt\"; \"Program Report\")) }}",
"baseName": "{{ concat(docTitle; \" — \"; fullName; \" — \"; formatDate(now; \"YYYYMMDD-HHmm\")) }}"
}
}
},
{
"id": "3",
"name": "Create doc from template",
"type": "google-docs",
"func": "createFromTemplate",
"params": {
"connectionId": "conn_gdocs_1",
"templateId": "YOUR_DOC_TEMPLATE_ID",
"name": "{{baseName}}",
"folderId": "YOUR_OUTPUT_FOLDER_ID",
"replacements": [
{"key": "{{doc_title}}", "value": "{{docTitle}}"},
{"key": "{{full_name}}", "value": "{{fullName}}"},
{"key": "{{email}}", "value": "{{recipientEmail}}"},
{"key": "{{score}}", "value": "{{ formatNumber(answers[\"score\"].number; 2) }}"},
{"key": "{{completion_date}}", "value": "{{completionDate}}"},
{"key": "{{certificate_id}}", "value": "{{certificateId}}"},
{"key": "{{cohort}}", "value": "{{answers[\"cohort\"].text}}"},
{"key": "{{issuer_name}}", "value": "YOUR ORG NAME"},
{"key": "{{issuer_title}}", "value": "PROGRAM DIRECTOR"},
{"key": "{{issued_date}}", "value": "{{ formatDate(now; \"YYYY-MM-DD\") }}"}
]
}
},
{
"id": "4",
"name": "Export to PDF",
"type": "google-docs",
"func": "export",
"params": {
"connectionId": "conn_gdocs_1",
"documentId": "{{3.documentId}}",
"mimeType": "application/pdf",
"fileName": "{{baseName}}.pdf",
"folderId": "YOUR_OUTPUT_FOLDER_ID"
}
},
{
"id": "5",
"name": "Share PDF",
"type": "google-drive",
"func": "shareFile",
"params": {
"connectionId": "conn_gdrive_1",
"fileId": "{{4.fileId}}",
"role": "reader",
"type": "user",
"emailAddress": "{{recipientEmail}}",
"sendNotificationEmail": false
}
},
{
"id": "6",
"name": "Email PDF link",
"type": "gmail",
"func": "sendEmail",
"params": {
"connectionId": "conn_gmail_1",
"to": "{{recipientEmail}}",
"subject": "{{docTitle}} — {{fullName}}",
"htmlBody": "Hi {{fullName}},<br><br>Your document is ready.<br><br>Title: {{docTitle}}<br>Reference: {{certificateId}}<br>Date: {{ formatDate(now; \"YYYY-MM-DD\") }}<br><br>File link: {{5.webViewLink}}",
"attachments": [ { "fileId": "{{4.fileId}}" } ]
}
},
{
"id": "7",
"name": "Log to Google Sheets",
"type": "google-sheets",
"func": "appendRow",
"params": {
"connectionId": "conn_gsheets_1",
"spreadsheetId": "YOUR_SPREADSHEET_ID",
"sheetName": "Generation Log",
"values": [
"{{response_id}}",
"{{3.documentId}}",
"{{4.fileId}}",
"{{recipientEmail}}",
"{{ formatDate(now; \"YYYY-MM-DDTHH:mm:ssZ\"; \"UTC\") }}"
]
}
}
],
"links": [
{"from_module": "1", "to_module": "2"},
{"from_module": "2", "to_module": "3"},
{"from_module": "3", "to_module": "4"},
{"from_module": "4", "to_module": "5"},
{"from_module": "5", "to_module": "6"},
{"from_module": "6", "to_module": "7"}
]
}
F. Minimal “Replace text” fallback (if your plan lacks Create-from-template)
Copy the file, then run multiple replace steps.
{
"copyTemplate": {
"type": "google-drive.copyFile",
"params": { "fileId": "YOUR_DOC_TEMPLATE_ID", "name": "{{baseName}}", "folderId": "YOUR_OUTPUT_FOLDER_ID" }
},
"replace1": {
"type": "google-docs.replaceText",
"params": { "documentId": "{{copyTemplate.newFileId}}", "search": "{{full_name}}", "replace": "{{fullName}}" }
},
"replace2": {
"type": "google-docs.replaceText",
"params": { "documentId": "{{copyTemplate.newFileId}}", "search": "{{email}}", "replace": "{{recipientEmail}}" }
}
/* add more replace steps as needed */
}
Use Cases / Scenarios
Certificates. Program completion certificates with unique IDs, date, and instructor signature.
Reports. Post-assessment summaries with scores and recommendations.
Receipts. Payment acknowledgements that match accounting formats, plus a PDF sent to the payer.
Workshop attendance letters. Pull participant name, session date, and organizer info.
Onboarding documents. Auto-filled NDA or policy acknowledgements.
Limitations / Considerations
Typeform quotas. Free plans cap responses and integrations. If volume spikes, buffer with a queue (Data Store or Sheets) and process in batches.
Gmail limits. Daily send caps apply per account. Keep messages transactional and low-volume. Use a Workspace account for higher thresholds.
Docs layout sensitivity. Large replacements can reflow paragraphs. Use tables for label–value sections.
File ownership. Docs belong to the connection account. Use a service account or shared drive to prevent orphaned files.
PII handling. Scope access to the output folder. Avoid exposing emails in public sharing links.
Uploads. Typeform file hosting URLs can expire under some settings. If you embed uploaded images, fetch and re-store them to Drive before insertion.
Fixes (common pitfalls with solutions and troubleshooting)
Placeholders not replaced. Ensure the placeholder text in the template exactly matches the key, including braces and case. Avoid smart quotes. Verify there are no line breaks inside placeholders.
Wrong date or number format. Apply formatDate
and formatNumber
before replacement. Do not rely on Docs' locale to format strings.
Duplicate documents. Implement a “seen” check keyed by response_id
. Store the created document ID in a log. If re-processing is needed, update the existing file, not copy again.
Broken permissions. After PDF export, explicitly share with the recipient. If you attach the PDF, you may skip link sharing but still keep the file private.
Emails are landing in spam. Remove promotional wording, keep concise HTML, and set SPF/DKIM for your domain account.
Images are not appearing. Ensure the link is a direct image URL or upload the image to Drive and insert by file ID. For QR codes, pre-generate as PNG and store in Drive.
Diagram
![diagram]()
Budget calculation
Let:
Examples:
Light cohort: N = 100
→ ~700 ops/month
fits typical free tiers that offer ~1,000 ops.
Growing program: N = 400
→ ~2,800 ops/month
suggests a paid Make plan or batching to reduce sends.
Attachment choice: If you skip PDF export and only send a Docs link, subtract 1 op per run.
Gmail send limits and Typeform response quotas vary by plan. To estimate email capacity:
Cost levers:
Use the Typeform instant trigger to avoid interval polling overhead.
Consolidate logging writes (append batched rows) if you chain other flows.
Avoid unnecessary image fetches or repeated replace steps.
Future enhancements
Multi-template catalog. Drive a template lookup from a Google Sheet keyed by variant and language, including signature blocks.
Serial numbering. Reserve certificate numbers from a counter in Sheets/Airtable to ensure gapless sequences.
Brand pack. Inject theme variables (colors, logo IDs) per partner or cohort.
E-signature. Append a signature page or call a signature service before delivery.
Revocation registry. Publish a public “verify certificate” page keyed by certificateId
.
Conclusion
Typeform collects structured inputs. Make orchestrates the run on each submission, normalizes data, selects a template, and generates consistent Google Docs and PDFs. The same mechanism emits certificates, reports, or receipts with traceable IDs and minimal manual work. The design stays template-centric, idempotent, and easy to audit.