AutomationFlowsAI & RAG › Generate RFP Proposals from Gmail

Generate RFP Proposals from Gmail

Original n8n title: Rfp/rfq Proposal Generator

RFP/RFQ Proposal Generator. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 8 nodes.

Event trigger★★★★☆ complexity8 nodesGmail TriggerGmailN8N Nodes PdfvectorGoogle SheetsSlack
AI & RAG Trigger: Event Nodes: 8 Complexity: ★★★★☆ Added:

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 →

Download .json
{
  "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.

Pro

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 →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

13. Insurance Pre-Authorization. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 12 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Job Application Processor & Candidate Scorer. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Shipping Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 10 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

W14 - Purchase Order Processor & Approval Workflow. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2
AI & RAG

Insurance Claim Document Processor. Uses gmailTrigger, gmail, n8n-nodes-pdfvector, googleSheets. Event-driven trigger; 9 nodes.

Gmail Trigger, Gmail, N8N Nodes Pdfvector +2