This workflow corresponds to n8n.io template #13135 — we link there as the canonical source.
This workflow follows the Form Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "8cmZ2C8vmHtA9wLl6uaML",
"name": "Generate student tests from PDFs using AI and Google Docs",
"tags": [],
"nodes": [
{
"id": "9ad448d2-3e46-4370-a94a-c5660c2a011c",
"name": "Form Trigger",
"type": "n8n-nodes-base.formTrigger",
"position": [
112,
448
],
"parameters": {
"options": {
"appendAttribution": false
},
"formTitle": "AI Test Generator",
"formFields": {
"values": [
{
"fieldLabel": "Name",
"requiredField": true
},
{
"fieldLabel": "Subject",
"requiredField": true
},
{
"fieldLabel": "Grade",
"placeholder": "e.g. 10th Grade",
"requiredField": true
},
{
"fieldType": "date",
"fieldLabel": "Date",
"requiredField": true
},
{
"fieldType": "file",
"fieldLabel": "Upload PDF",
"requiredField": true,
"acceptFileTypes": ".pdf"
},
{
"fieldType": "email",
"fieldLabel": "Email",
"requiredField": true
}
]
},
"formDescription": "Generate test questions from PDF materials"
},
"typeVersion": 2.5
},
{
"id": "9f09d15c-af0f-4dc4-b001-02bad462747a",
"name": "Extract PDF",
"type": "n8n-nodes-base.extractFromFile",
"position": [
320,
448
],
"parameters": {
"options": {},
"operation": "pdf",
"binaryPropertyName": "Upload_PDF"
},
"typeVersion": 1.1
},
{
"id": "6cae75f2-f3a2-46a7-ae32-9cba52b7a691",
"name": "AI Generate",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
496,
448
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini"
},
"options": {
"maxTokens": 3000,
"textFormat": {
"textOptions": {
"type": "json_schema",
"schema": "{\"type\":\"object\",\"properties\":{\"test_title\":{\"type\":\"string\"},\"questions\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"group\":{\"type\":\"string\",\"enum\":[\"A\",\"B\"]},\"number\":{\"type\":\"integer\"},\"type\":{\"type\":\"string\",\"enum\":[\"fill-in\",\"multiple-choice\",\"problem\"]},\"text\":{\"type\":\"string\"},\"options\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"correct_answer\":{\"type\":\"string\"},\"points\":{\"type\":\"integer\"}},\"required\":[\"group\",\"number\",\"type\",\"text\",\"options\",\"correct_answer\",\"points\"],\"additionalProperties\":false}},\"additionalProperties\":false},\"required\":[\"test_title\",\"questions\"],\"additionalProperties\":false}"
}
}
},
"responses": {
"values": [
{
"role": "system",
"content": "You are a professional test generation system for educational materials.\n\nGENERAL RULES:\n\n1. Language: Write in English only, using clear and professional language.\n\n2. Accuracy:\n - Be professionally precise\n - Do not invent data not present in the input material\n - Do not repeat concepts across multiple questions\n\n3. Pedagogical Quality:\n - Avoid questions requiring mere memorization of numbers\n - Questions must test understanding, not rote memory\n - Distractors (wrong answers) must be realistic and represent common student errors\n - For problem-solving tasks, always specify units of measurement\n\nTEST STRUCTURE:\n\nGenerate 10 questions total, divided into two independent groups:\n\nGROUP A \u2013 5 questions\nGROUP B \u2013 5 questions\n\nFor EACH group:\n- 2 \"fill-in\" questions (easy \u2013 basic concepts)\n- 2 \"multiple-choice\" questions with 4 options (medium \u2013 understanding)\n- 1 \"problem\" question (hard \u2013 application of knowledge)\n\nQuestions in Group A and Group B must be substantially different.\n\nOUTPUT FORMAT:\n- Return ONLY valid JSON\n- Follow the predefined schema exactly\n- Do not add explanations or text outside JSON"
},
{
"content": "={{ $json.text }}"
}
]
},
"builtInTools": {}
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 2.1
},
{
"id": "4ab7a9bc-c1e0-4428-a0e5-c3c5a47a2be7",
"name": "Format Test",
"type": "n8n-nodes-base.code",
"position": [
768,
448
],
"parameters": {
"jsCode": "const ai = $input.first().json;\nconst qs = ai.output[0].content[0].text.questions;\nconst form = $('Form Trigger').item.json;\n\nconst h = `SUBJECT: ${form.Subject}\\nGRADE: ${form.Grade}\\nDATE: ${form.Date}\\nSTUDENT NAME: _________________________\\n\\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\\n\\n`;\n\nfunction fmt(q) {\n let t = `${q.number}. ${q.text}\\n\\n`;\n if (q.type === \"multiple-choice\" && q.options?.length === 4) {\n t += `a) ${q.options[0]}\\nb) ${q.options[1]}\\nc) ${q.options[2]}\\nd) ${q.options[3]}\\n\\n`;\n }\n t += \"Answer:\\n______________________________\\n\";\n if (q.type === \"problem\") t += \"Work:\\n______________________________\\n\";\n t += `\\nPoints: ${q.points}\\n\\n`;\n return t;\n}\n\nconst a = qs.filter(q => q.group === \"A\");\nconst b = qs.filter(q => q.group === \"B\");\n\nlet tA = h + `=== GROUP A ===\\n\\n`;\na.forEach(q => tA += fmt(q));\n\nlet tB = h + `=== GROUP B ===\\n\\n`;\nb.forEach(q => tB += fmt(q));\n\nreturn [{json: {textA: tA, textB: tB}}];"
},
"typeVersion": 2
},
{
"id": "137926bb-065b-4612-ac7b-a4612bae4a5a",
"name": "Create Doc",
"type": "n8n-nodes-base.googleDocs",
"position": [
992,
448
],
"parameters": {
"title": "={{ $('Form Trigger').item.json.Subject }} - {{ $('Form Trigger').item.json.Grade }} - {{ $('Form Trigger').item.json.Date }}"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "35286471-c560-4175-a6b7-e1fa74444e15",
"name": "Share Doc",
"type": "n8n-nodes-base.googleDrive",
"position": [
1136,
448
],
"parameters": {
"fileId": {
"__rl": true,
"mode": "id",
"value": "={{ $json.id }}"
},
"options": {},
"operation": "share",
"permissionsUi": {
"permissionsValues": {
"role": "reader",
"type": "anyone"
}
}
},
"credentials": {
"googleDriveOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 3
},
{
"id": "fa6d3d38-6f02-48c7-aeaa-ae800affbe22",
"name": "Insert Group A",
"type": "n8n-nodes-base.googleDocs",
"position": [
1280,
448
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"text": "={{ $('Format Test').item.json.textA }}",
"action": "insert"
}
]
},
"operation": "update",
"documentURL": "={{ $('Create Doc').item.json.id }}"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "43e1d9a2-8969-42bc-96c6-ba20b7d07a4e",
"name": "Page Break",
"type": "n8n-nodes-base.googleDocs",
"position": [
1424,
448
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"action": "insert",
"object": "pageBreak"
}
]
},
"operation": "update",
"documentURL": "={{ $('Create Doc').item.json.id }}"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "65835ab4-9049-47c2-bdec-aab494584899",
"name": "Insert Group B",
"type": "n8n-nodes-base.googleDocs",
"position": [
1568,
448
],
"parameters": {
"actionsUi": {
"actionFields": [
{
"text": "={{ $('Format Test').item.json.textB }}",
"action": "insert"
}
]
},
"operation": "update",
"documentURL": "={{ $('Create Doc').item.json.id }}"
},
"credentials": {
"googleDocsOAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 2
},
{
"id": "618a02e0-1855-42c4-a208-6fdb08732839",
"name": "Send Email",
"type": "n8n-nodes-base.gmail",
"position": [
1808,
448
],
"parameters": {
"sendTo": "={{ $('Form Trigger').item.json.Email }}",
"message": "<p>Dear {{ $('Form Trigger').item.json.Name }},</p><p>Your test has been generated!</p><p><strong>\ud83d\udcc4 Link:</strong><br><a href=\"https://docs.google.com/document/d/{{ $('Create Doc').item.json.id }}\">Open Document</a></p><p><strong>Details:</strong></p><ul><li>Subject: {{ $('Form Trigger').item.json.Subject }}</li><li>Grade: {{ $('Form Trigger').item.json.Grade }}</li><li>Questions: 10 (5 per group)</li></ul><p>\u2713 Two versions (Group A & B)<br>\u2713 Ready to print</p><hr><p><small>AI Test Generator</small></p>",
"options": {},
"subject": "={{ $('Form Trigger').item.json.Subject }} - Test Ready"
},
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"typeVersion": 2.2
},
{
"id": "53a169ac-2f10-4925-bf54-f52ec3dc9716",
"name": "\ud83d\udcdd Overview",
"type": "n8n-nodes-base.stickyNote",
"position": [
64,
-256
],
"parameters": {
"width": 420,
"height": 476,
"content": "## How it works\n\n1. **User submits form** with PDF teaching materials\n2. **PDF text extraction** pulls content from document \n3. **AI generates questions** (10 total: 5 Group A, 5 Group B)\n4. **Google Doc created** with both test versions\n5. **Email sent** with document link\n\nTotal time: ~60 seconds\n\n## Setup steps\n\n1. Add OpenAI API key (for question generation)\n2. Add Google Docs OAuth2 (for document creation)\n3. Add Google Drive OAuth2 (for sharing)\n4. Add Gmail OAuth2 (for email notifications)\n5. (Optional) Set Google Drive folder ID in \"Create Doc\" node\n6. Test with sample PDF\n\n**Cost:** ~$0.002 per test (GPT-4o-mini)"
},
"typeVersion": 1
},
{
"id": "12dc0854-1599-4af4-9d57-1e4260210a12",
"name": "Group: Input",
"type": "n8n-nodes-base.stickyNote",
"position": [
64,
256
],
"parameters": {
"color": 7,
"width": 388,
"height": 404,
"content": "## \ud83d\udcdd Form & PDF Processing\n\nCaptures user input and extracts text from PDF document."
},
"typeVersion": 1
},
{
"id": "22f479f2-7881-4d9a-a8f8-30bdd041ca8f",
"name": "Group: AI",
"type": "n8n-nodes-base.stickyNote",
"position": [
480,
256
],
"parameters": {
"color": 7,
"width": 452,
"height": 396,
"content": "## \ud83e\udd16 AI Question Generation\n\nOpenAI generates 10 questions (5 per group) based on PDF content.\n\n**Model:** GPT-4o-mini \n**Cost:** ~$0.002 per test"
},
"typeVersion": 1
},
{
"id": "7e75915b-1c49-48fb-be90-7de4c8382361",
"name": "Group: Docs",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
256
],
"parameters": {
"color": 7,
"width": 756,
"height": 388,
"content": "## \ud83d\udcc4 Google Docs Output\n\nCreates formatted document with both test versions and auto-shares with link."
},
"typeVersion": 1
},
{
"id": "851da681-c3b3-495a-b59d-e0b1077650a3",
"name": "Group: Email",
"type": "n8n-nodes-base.stickyNote",
"position": [
1744,
256
],
"parameters": {
"color": 7,
"width": 260,
"height": 388,
"content": "## \u2709\ufe0f Email Notification\n\nSends professional email with document link."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "6780eb7b-1334-4560-a97b-a37d16069e2c",
"connections": {
"Share Doc": {
"main": [
[
{
"node": "Insert Group A",
"type": "main",
"index": 0
}
]
]
},
"Create Doc": {
"main": [
[
{
"node": "Share Doc",
"type": "main",
"index": 0
}
]
]
},
"Page Break": {
"main": [
[
{
"node": "Insert Group B",
"type": "main",
"index": 0
}
]
]
},
"AI Generate": {
"main": [
[
{
"node": "Format Test",
"type": "main",
"index": 0
}
]
]
},
"Extract PDF": {
"main": [
[
{
"node": "AI Generate",
"type": "main",
"index": 0
}
]
]
},
"Format Test": {
"main": [
[
{
"node": "Create Doc",
"type": "main",
"index": 0
}
]
]
},
"Form Trigger": {
"main": [
[
{
"node": "Extract PDF",
"type": "main",
"index": 0
}
]
]
},
"Insert Group A": {
"main": [
[
{
"node": "Page Break",
"type": "main",
"index": 0
}
]
]
},
"Insert Group B": {
"main": [
[
{
"node": "Send Email",
"type": "main",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
gmailOAuth2googleDocsOAuth2ApigoogleDriveOAuth2ApiopenAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Extract content from PDFs, generate two different test versions with AI, and create formatted Google Docs with automated email delivery. This workflow extracts text from PDF teaching materials, uses OpenAI to generate 10 test questions in two different versions (Group A and B),…
Source: https://n8n.io/workflows/13135/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Use cases are many: send branded quotations to new leads after a discovery call, distribute pricing proposals to inbound form submissions, or push service packages to prospects. All without manually c
What it is An automated LinkedIn content system that takes a simple form (idea + optional file), generates LinkedIn posts with OpenAI, stores them in Notion, builds Google Slides carousels, and auto-p
An n8n-based automation that generates client proposals from a form, lets you review everything in one place, and sends the proposal only when you approve it.
This workflow enables seamless speech-to-text transcription, AI-powered summarization, sentiment analysis, and automated email delivery. It supports two different input modes: Form Upload (Local File)
Stop applying manually. This workflow acts as your personal AI recruiter, automating the end-to-end process of finding high-quality jobs, tailoring your resume, and preparing personalized outreach ema