This workflow follows the Execute Workflow Trigger → Google Sheets 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 →
{
"name": "DocFlow 06 - Form Extractor",
"nodes": [
{
"parameters": {},
"id": "trigger-06",
"name": "When Called by Workflow",
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1,
"position": [
240,
300
]
},
{
"parameters": {
"jsCode": "const env = $input.first().json;\nconst fields = env.client_config?.forms?.fields ?? [];\nconst fieldList = fields.map(f => `${f.name}: ${f.type}: ${f.description || ''}`).join('\\n');\nenv.form_field_spec = fields;\nenv.form_field_list_text = fieldList;\nreturn [{ json: env }];"
},
"id": "lookup-06",
"name": "Lookup Field Spec",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"method": "POST",
"url": "https://openrouter.ai/api/v1/chat/completions",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "HTTP-Referer",
"value": "https://docflow.local"
},
{
"name": "X-Title",
"value": "DocFlow AI"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"model\": \"openai/gpt-4o-mini\",\n \"messages\": [\n {\"role\": \"system\", \"content\": `You are a form data extractor. Extract ONLY the fields listed below. Anything not in the list must be ignored. For booleans: true if ticked, false if explicitly unticked, null otherwise. For dates: YYYY-MM-DD. For numbers: strip currency symbols and separators. Always include confidence (0..1). Output JSON only.\\n\\nField list:\\n${$json.form_field_list_text}`},\n {\"role\": \"user\", \"content\": $json.raw_text}\n ],\n \"response_format\": {\"type\": \"json_object\"},\n \"temperature\": 0.1,\n \"max_tokens\": 1000\n}",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
},
"timeout": 60000,
"retry": {
"tries": 3,
"waitBetweenTries": 2000
}
}
},
"id": "llm-06",
"name": "Call OpenRouter (Extract)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
680,
300
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const env = $('Lookup Field Spec').first().json;\nconst raw = $input.first().json;\nconst content = raw?.choices?.[0]?.message?.content ?? '{}';\nlet extracted;\ntry { extracted = JSON.parse(content); }\ncatch (e) { extracted = { confidence: 0, _parse_error: true }; }\nreturn [{ json: { ...env, extracted, extraction_confidence: extracted.confidence ?? 0 } }];"
},
"id": "parse-06",
"name": "Parse JSON",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
900,
300
]
},
{
"parameters": {
"jsCode": "const env = $input.first().json;\nconst e = env.extracted || {};\nconst fields = env.form_field_spec || [];\nconst errors = [];\n\nfor (const f of fields) {\n if (f.required && (e[f.name] == null || e[f.name] === '')) { errors.push('required:' + f.name); continue; }\n if (e[f.name] == null) continue;\n if (f.type === 'date' && !/^\\d{4}-\\d{2}-\\d{2}$/.test(e[f.name])) errors.push('date:' + f.name + ':not_iso');\n if (f.type === 'number' && typeof e[f.name] !== 'number') errors.push('type:' + f.name + ':expected_number');\n if (f.type === 'boolean' && typeof e[f.name] !== 'boolean') errors.push('type:' + f.name + ':expected_boolean');\n}\n\nconst conf = e.confidence ?? 0;\nconst minConf = env.client_config?.thresholds?.extract_min_confidence ?? 0.8;\nif (conf < minConf) errors.push('confidence:overall:' + conf);\n\nenv.extraction_confidence = conf;\nenv.validation = { ok: errors.length === 0, errors };\nreturn [{ json: env }];"
},
"id": "validate-06",
"name": "Validate",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1120,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "validation-gate-06",
"leftValue": "={{ $json.validation.ok }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "gate-06",
"name": "Validation Gate",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1340,
300
]
},
{
"parameters": {
"rules": {
"values": [
{
"outputKey": "sheets",
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict",
"leftValue": ""
},
"conditions": [
{
"id": "kind-sheets",
"leftValue": "={{ $json.client_config.forms.destination.kind }}",
"rightValue": "sheets",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
{
"outputKey": "notion",
"conditions": {
"options": {
"caseSensitive": true,
"typeValidation": "strict",
"leftValue": ""
},
"conditions": [
{
"id": "kind-notion",
"leftValue": "={{ $json.client_config.forms.destination.kind }}",
"rightValue": "notion",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
}
]
},
"options": {}
},
"id": "destination-switch-06",
"name": "Destination Switch",
"type": "n8n-nodes-base.switch",
"typeVersion": 3.2,
"position": [
1560,
200
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "={{ $json.client_config.forms.destination.spreadsheet_id }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $json.client_config.forms.destination.tab || 'forms' }}",
"mode": "name"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": []
},
"options": {}
},
"id": "sheets-06",
"name": "Append Form Row",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
1780,
100
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"resource": "databasePage",
"operation": "create",
"databaseId": {
"__rl": true,
"value": "={{ $json.client_config.forms.destination.database_id }}",
"mode": "id"
},
"title": "=Form: {{ $json.extracted.applicant_name || $json.source_meta?.filename || $json.doc_id }}",
"propertiesUi": {
"propertyValues": []
}
},
"id": "notion-06",
"name": "Create Notion Page",
"type": "n8n-nodes-base.notion",
"typeVersion": 2.2,
"position": [
1780,
300
],
"credentials": {
"notionApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $('Validate').item.json.client_config.telegram.chat_id }}",
"text": "=Form processed\nFile: {{ $('Validate').item.json.source_meta?.filename ?? '' }}\nConfidence: {{ $('Validate').item.json.extraction_confidence }}",
"additionalFields": {}
},
"id": "telegram-06",
"name": "Telegram Success",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
2000,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const env = $input.first().json;\nenv.review_reason = env.validation?.ok ? 'extract_low_conf' : 'validation_failed';\nreturn [{ json: env }];"
},
"id": "tag-review-06",
"name": "Tag Review Reason",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1560,
400
]
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "REPLACE_WITH_WORKFLOW_07_ID",
"mode": "id"
},
"options": {}
},
"id": "call-07-from-06",
"name": "Route to Review",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.2,
"position": [
1780,
400
]
}
],
"connections": {
"When Called by Workflow": {
"main": [
[
{
"node": "Lookup Field Spec",
"type": "main",
"index": 0
}
]
]
},
"Lookup Field Spec": {
"main": [
[
{
"node": "Call OpenRouter (Extract)",
"type": "main",
"index": 0
}
]
]
},
"Call OpenRouter (Extract)": {
"main": [
[
{
"node": "Parse JSON",
"type": "main",
"index": 0
}
]
]
},
"Parse JSON": {
"main": [
[
{
"node": "Validate",
"type": "main",
"index": 0
}
]
]
},
"Validate": {
"main": [
[
{
"node": "Validation Gate",
"type": "main",
"index": 0
}
]
]
},
"Validation Gate": {
"main": [
[
{
"node": "Destination Switch",
"type": "main",
"index": 0
}
],
[
{
"node": "Tag Review Reason",
"type": "main",
"index": 0
}
]
]
},
"Destination Switch": {
"main": [
[
{
"node": "Append Form Row",
"type": "main",
"index": 0
}
],
[
{
"node": "Create Notion Page",
"type": "main",
"index": 0
}
]
]
},
"Append Form Row": {
"main": [
[
{
"node": "Telegram Success",
"type": "main",
"index": 0
}
]
]
},
"Create Notion Page": {
"main": [
[
{
"node": "Telegram Success",
"type": "main",
"index": 0
}
]
]
},
"Tag Review Reason": {
"main": [
[
{
"node": "Route to Review",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "docflow"
}
]
}
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.
googleSheetsOAuth2ApihttpHeaderAuthnotionApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
DocFlow 06 - Form Extractor. Uses executeWorkflowTrigger, httpRequest, googleSheets, notion. Event-driven trigger; 12 nodes.
Source: https://github.com/Moamen-Elsharkawy/docflow-ai/blob/main/n8n-exports/06-form-extractor.json — 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.
simon-apify-youtube-transcript-20250724-v1-ok. Uses httpRequest, lmChatOpenAi, chainLlm, executeWorkflowTrigger. Event-driven trigger; 19 nodes.
cdp_router. Uses gmailTrigger, telegramTrigger, googleSheets, httpRequest. Event-driven trigger; 53 nodes.
cdp_router. Uses gmailTrigger, telegramTrigger, googleSheets, httpRequest. Event-driven trigger; 53 nodes.
This workflow triggers when a HubSpot deal stage changes to Closed Won and automatically generates an invoice. It collects deal and contact data, builds a styled invoice, converts it into a PDF, and s
12 - Market Intelligence Report Builder (Stakeholder v0.3). Uses googleSheets, httpRequest, executeWorkflowTrigger. Event-driven trigger; 27 nodes.