This workflow follows the Gmail → Gmail Trigger 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": "RFP/RFQ Proposal Generator",
"nodes": [
{
"parameters": {
"content": "## \ud83d\udcdd RFP/RFQ Analyzer\n\n### What this workflow does\n1. Watches Gmail for RFP emails\n2. Extracts requirements & criteria\n3. Calculates deadline urgency\n4. Logs to RFP tracker\n5. Alerts sales team via Slack\n\n### Setup steps\n1. Connect Gmail OAuth2 credentials\n2. Get PDF Vector API key from pdfvector.com/api-keys\n3. Create Google Sheet with columns below\n4. Connect Slack and pick your sales channel\n5. Update credential IDs in the nodes\n\n### Sheet columns\nRFP Number, Project Title, Organization, Deadline, Days Remaining, Urgency, Budget Min, Budget Max, Required Items, Total Requirements, Questions to Answer, Source Email, Received Date, Status\n\n### Perfect for\n- Sales teams\n- Consulting firms\n- Government contractors\n- B2B service providers",
"height": 480,
"width": 320,
"color": 5
},
"id": "7a3a86c8-7dba-4f4f-b18c-9e1cb19c7323",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-336,
96
]
},
{
"parameters": {
"content": "## \ud83c\udfaf Extraction Points\n\n- Client requirements\n- Evaluation criteria\n- Submission deadline\n- Budget range\n- Scope of work\n- Compliance requirements\n- Questions to answer\n\n### Urgency Levels\n- Urgent: \u22647 days\n- High: 8-14 days\n- Normal: >14 days",
"height": 260,
"width": 260
},
"id": "134f3de1-2596-4c99-a299-5c77e51a18ce",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
208,
16
]
},
{
"parameters": {
"pollTimes": {
"item": [
{
"mode": "everyMinute"
}
]
},
"filters": {
"includeSpamTrash": false
}
},
"id": "1a9e76e7-a59b-4feb-81f2-2c14b9678227",
"name": "Gmail Trigger",
"type": "n8n-nodes-base.gmailTrigger",
"typeVersion": 1,
"position": [
-48,
288
],
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
},
"notes": "Monitor for incoming RFPs"
},
{
"parameters": {
"operation": "get",
"messageId": "={{ $json.id }}",
"simple": false,
"options": {
"downloadAttachments": true
}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.2,
"position": [
160,
288
],
"id": "bd836c3c-199e-47c8-9680-a969d679eae1",
"name": "Get a message",
"credentials": {
"gmailOAuth2": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "extract",
"inputType": "file",
"prompt": "Extract RFP/RFQ document data as flat fields. issuingOrganization, projectTitle, rfpNumber, submissionDeadline, submissionMethod, projectScope, requirementsList (semicolon-separated list of all requirements), evaluationCriteriaList (semicolon-separated list formatted as: criterion weight%), budgetMin (number), budgetMax (number), currency, projectStartDate, projectEndDate, qualificationsList (semicolon-separated required qualifications), questionsToAnswerList (semicolon-separated questions that need answers), complianceList (semicolon-separated compliance requirements), requiredCount (number of required items), preferredCount (number of preferred items).",
"schema": "{\"type\": \"object\", \"properties\": {\"issuingOrganization\": {\"type\": \"string\"}, \"projectTitle\": {\"type\": \"string\"}, \"rfpNumber\": {\"type\": \"string\"}, \"submissionDeadline\": {\"type\": \"string\"}, \"submissionMethod\": {\"type\": \"string\"}, \"projectScope\": {\"type\": \"string\"}, \"requirementsList\": {\"type\": \"string\"}, \"evaluationCriteriaList\": {\"type\": \"string\"}, \"budgetMin\": {\"type\": \"number\"}, \"budgetMax\": {\"type\": \"number\"}, \"currency\": {\"type\": \"string\"}, \"projectStartDate\": {\"type\": \"string\"}, \"projectEndDate\": {\"type\": \"string\"}, \"qualificationsList\": {\"type\": \"string\"}, \"questionsToAnswerList\": {\"type\": \"string\"}, \"complianceList\": {\"type\": \"string\"}, \"requiredCount\": {\"type\": \"number\"}, \"preferredCount\": {\"type\": \"number\"}}, \"additionalProperties\": false}"
},
"id": "37015305-f9ac-410f-a7ea-146c58ff9215",
"name": "PDF Vector - Extract RFP",
"type": "n8n-nodes-pdfvector.pdfVector",
"typeVersion": 2,
"position": [
384,
288
],
"credentials": {
"pdfVectorApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const rfp = ($input.first().json?.data || $input.first().json) || {};\nconst emailData = $('Get a message')?.item?.json || {};\n\nconst senderEmail = emailData?.from?.value?.[0]?.address || 'test@example.com';\nconst receivedDate = emailData?.date || new Date().toISOString();\n\nfunction parseDeadline(str) {\n if (!str) return null;\n let d = new Date(str);\n if (!isNaN(d.getTime())) return d;\n const clean = str.replace(/\\s+at\\s+/i,' ').replace(/\\s+(EST|CST|MST|PST|EDT|CDT|MDT|PDT)$/i,'');\n d = new Date(clean);\n if (!isNaN(d.getTime())) return d;\n const m = str.match(/([A-Za-z]+\\s+\\d{1,2},?\\s+\\d{4})/);\n if (m) { d = new Date(m[1]); if (!isNaN(d.getTime())) return d; }\n return null;\n}\n\nlet daysRemaining = 'N/A';\nconst deadlineDate = parseDeadline(rfp.submissionDeadline);\nif (deadlineDate) {\n const today = new Date(); today.setHours(0,0,0,0); deadlineDate.setHours(0,0,0,0);\n daysRemaining = Math.ceil((deadlineDate - today) / (1000*60*60*24));\n}\n\nlet urgency = 'Normal';\nif (typeof daysRemaining === 'number') {\n if (daysRemaining <= 0) urgency = 'OVERDUE';\n else if (daysRemaining <= 7) urgency = 'Urgent';\n else if (daysRemaining <= 14) urgency = 'High';\n}\n\nlet budgetDisplay = 'Not specified';\nconst bMin = parseFloat(rfp.budgetMin) || 0;\nconst bMax = parseFloat(rfp.budgetMax) || 0;\nif (bMin && bMax) budgetDisplay = `$${bMin.toLocaleString()} - $${bMax.toLocaleString()}`;\nelse if (bMax) budgetDisplay = `Up to $${bMax.toLocaleString()}`;\nelse if (bMin) budgetDisplay = `Minimum $${bMin.toLocaleString()}`;\n\nconst questionCount = rfp.questionsToAnswerList\n ? rfp.questionsToAnswerList.split(';').filter(q => q.trim()).length\n : 0;\n\nreturn [{ json: {\n issuingOrganization: rfp.issuingOrganization || 'N/A',\n projectTitle: rfp.projectTitle || 'N/A',\n rfpNumber: rfp.rfpNumber || 'N/A',\n submissionDeadline: rfp.submissionDeadline || 'N/A',\n projectScope: rfp.projectScope || 'N/A',\n requirementsList: rfp.requirementsList || 'N/A',\n evaluationCriteriaList: rfp.evaluationCriteriaList || 'N/A',\n questionsToAnswerList: rfp.questionsToAnswerList || 'N/A',\n budgetMin: bMin,\n budgetMax: bMax,\n budgetDisplay,\n requiredCount: parseInt(rfp.requiredCount) || 0,\n preferredCount: parseInt(rfp.preferredCount) || 0,\n totalRequirements: (parseInt(rfp.requiredCount) || 0) + (parseInt(rfp.preferredCount) || 0),\n questionCount,\n daysRemaining,\n urgency,\n senderEmail,\n receivedDate,\n criteriaList: rfp.evaluationCriteriaList || 'Not specified',\n processedAt: new Date().toISOString()\n}}];"
},
"id": "ab9c83d3-f815-4587-8782-f86ed9d95f03",
"name": "Analyze RFP",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
576,
288
]
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "YOUR_SPREADSHEET_ID",
"mode": "list"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"RFP Number": "={{ $json.rfpNumber || 'N/A' }}",
"Project Title": "={{ $json.projectTitle }}",
"Organization": "={{ $json.issuingOrganization || 'N/A' }}",
"Deadline": "={{ $json.submissionDeadline }}",
"Days Remaining": "={{ $json.daysRemaining }}",
"Urgency": "={{ $json.urgency }}",
"Budget Min": "={{ $json.budgetRange ? $json.budgetRange.min : 'N/A' }}",
"Budget Max": "={{ $json.budgetRange ? $json.budgetRange.max : 'N/A' }}",
"Required Items": "={{ $json.requiredCount }}",
"Total Requirements": "={{ $json.totalRequirements }}",
"Questions to Answer": "={{ $json.questionCount }}",
"Source Email": "={{ $json.senderEmail }}",
"Received Date": "={{ $json.receivedDate }}",
"Status": "=New"
}
},
"options": {}
},
"id": "1a7e784b-851f-4f39-b8bc-89d45beba443",
"name": "Log to RFP Tracker",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.4,
"position": [
784,
288
],
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "oAuth2",
"select": "channel",
"channelId": {
"__rl": true,
"value": "YOUR_SLACK_CHANNEL_ID",
"mode": "list"
},
"text": "=\ud83d\udccb *New RFP Received*\n\n*Project:* {{ $('Analyze RFP').item.json.projectTitle }}\n*Organization:* {{ $('Analyze RFP').item.json.issuingOrganization || 'N/A' }}\n*RFP #:* {{ $('Analyze RFP').item.json.rfpNumber || 'N/A' }}\n\n\u23f0 *Deadline:* {{ $('Analyze RFP').item.json.submissionDeadline }}\n*Days Remaining:* {{ $('Analyze RFP').item.json.daysRemaining }} ({{ $('Analyze RFP').item.json.urgency }})\n\n\ud83d\udcb0 *Budget:* {{ $('Analyze RFP').item.json.budgetDisplay }}\n\n\ud83d\udcdd *Requirements:*\n\u2022 Required: {{ $('Analyze RFP').item.json.requiredCount }}\n\u2022 Preferred: {{ $('Analyze RFP').item.json.preferredCount }}\n\u2022 Questions: {{ $('Analyze RFP').item.json.questionCount }}\n\n*Evaluation Criteria:*\n{{ $('Analyze RFP').item.json.criteriaList || 'Not specified' }}",
"otherOptions": {}
},
"id": "8eb1851f-e358-4e35-b5a1-8ea4a9d5cc04",
"name": "Alert Sales Team",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
976,
288
],
"credentials": {
"slackOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Gmail Trigger": {
"main": [
[
{
"node": "Get a message",
"type": "main",
"index": 0
}
]
]
},
"Get a message": {
"main": [
[
{
"node": "PDF Vector - Extract RFP",
"type": "main",
"index": 0
}
]
]
},
"PDF Vector - Extract RFP": {
"main": [
[
{
"node": "Analyze RFP",
"type": "main",
"index": 0
}
]
]
},
"Analyze RFP": {
"main": [
[
{
"node": "Log to RFP Tracker",
"type": "main",
"index": 0
}
]
]
},
"Log to RFP Tracker": {
"main": [
[
{
"node": "Alert Sales Team",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"tags": [
{
"name": "PDF Vector"
},
{
"name": "Sales"
},
{
"name": "RFP"
}
],
"meta": {
"templateCredsSetupCompleted": false
}
}
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.
gmailOAuth2googleSheetsOAuth2ApipdfVectorApislackOAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
RFP/RFQ Proposal Generator. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 8 nodes.
Source: https://github.com/khanhduyvt0101/workflows/blob/0153ee2efc0f692c931b9bb4c2a04abf11756822/n8n-workflows/rfp-rfq-proposal-generator.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.
13. Insurance Pre-Authorization. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 12 nodes.
Job Application Processor & Candidate Scorer. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.
Shipping Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.
W14 - Purchase Order Processor & Approval Workflow. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.
Insurance Claim Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.