{
  "name": "RFP 02 - Intake and Requirement Extractor",
  "nodes": [
    {
      "id": "d682de055e1401c1",
      "name": "Intake Contract",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -1280,
        -460
      ],
      "parameters": {
        "content": "## RFP Intake & Requirement Extractor\nAccepts a normalized RFP payload, validates ownership and due date, then converts the source into a requirement matrix. The sample trigger is safe to run; the webhook is the production entry point.",
        "width": 800,
        "height": 320,
        "color": 5
      }
    },
    {
      "id": "19db06c0fe701607",
      "name": "Manual Demo Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -1200,
        -80
      ],
      "parameters": {}
    },
    {
      "id": "9cda456beb574bb4",
      "name": "Load Sample RFP",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -960,
        -80
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return [{json:{\"project_id\":\"RFP-DEMO-001\",\"customer_name\":\"Northstar Manufacturing\",\"opportunity_name\":\"Global Cloud Modernisation\",\"owner_email\":\"owner@example.com\",\"due_date\":\"2027-02-28\",\"permitted_classifications\":[\"PUBLIC\",\"INTERNAL\"],\"region\":\"GLOBAL\",\"rfp_text\":\"1. Describe your security certifications and encryption controls.\\n2. State the Enterprise availability commitment and Priority 1 response target.\\n3. Describe the implementation approach and typical timeline.\\n4. Confirm the exact annual subscription price for 8,000 users.\\n5. Can you guarantee zero security incidents for the full contract term?\"}}];"
      }
    },
    {
      "id": "545c8c658a204dcb",
      "name": "Production RFP Intake Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1200,
        140
      ],
      "parameters": {
        "httpMethod": "POST",
        "path": "rfp-control-room-intake",
        "responseMode": "responseNode",
        "options": {}
      }
    },
    {
      "id": "b7c50e8b1bfef77f",
      "name": "Normalize and Validate Intake",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -700,
        20
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const b=$json.body??$json; const id=String(b.project_id??`RFP-${Date.now()}`).trim();\nconst text=String(b.rfp_text??\"\").trim(); const owner=String(b.owner_email??\"\").trim().toLowerCase();\nconst due=String(b.due_date??\"\"); const valid=text.length>=80&&/^\\S+@\\S+\\.\\S+$/.test(owner)&&!Number.isNaN(new Date(due).getTime());\nreturn [{json:{...b,project_id:id,owner_email:owner,rfp_text:text,intake_status:valid?\"READY\":\"REJECTED\",intake_error:valid?\"\":\"RFP text, valid owner email and due date are required\",status:valid?\"EXTRACTING\":\"EXCEPTION\",created_at:new Date().toISOString(),updated_at:new Date().toISOString()}}];"
      }
    },
    {
      "id": "7aab6e51d480003a",
      "name": "Intake Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        -430,
        20
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "Intake Valid?-condition",
              "leftValue": "={{ $json.intake_status }}",
              "rightValue": "READY",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      }
    },
    {
      "id": "172cf60074b44517",
      "name": "Extract Atomic Requirements",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "typeVersion": 1.9,
      "position": [
        -130,
        -100
      ],
      "parameters": {
        "promptType": "define",
        "text": "=You are an enterprise proposal analyst. Convert the RFP below into atomic, testable requirements. Preserve obligations, numbers, and meaning exactly. Do not answer or invent requirements. Categorize each item as SECURITY, LEGAL, TECHNICAL, COMMERCIAL, IMPLEMENTATION, SUPPORT, or OTHER. mandatory is TRUE only when the source states must, shall, required, or an equivalent obligation. risk_hint flags commitments, pricing, legal terms, guarantees, unsupported absolutes, or ambiguity. retrieval_query is concise evidence-search text.\n\nReturn one requirement per line and nothing else. Use exactly five fields separated by |||:\nrequirement_text ||| CATEGORY ||| TRUE or FALSE ||| risk_hint ||| retrieval_query\nDo not use ||| inside a field.\n\nRFP:\n{{ $json.rfp_text }}",
        "hasOutputParser": false,
        "needsFallback": false,
        "batching": {
          "batchSize": 1,
          "delayBetweenBatches": 0
        }
      }
    },
    {
      "id": "f02bdd34f0a47580",
      "name": "NVIDIA Requirement Extraction Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatNvidia",
      "typeVersion": 1,
      "position": [
        -180,
        190
      ],
      "parameters": {
        "model": "nvidia/llama-3.3-nemotron-super-49b-v1.5",
        "options": {
          "maxTokens": 4096,
          "temperature": 0.1,
          "topP": 1,
          "maxRetries": 2,
          "timeout": 180000
        }
      }
    },
    {
      "id": "c14b3cbcb1c85218",
      "name": "Expand Requirement Matrix",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        170,
        -100
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const src=$(\"Normalize and Validate Intake\").item.json; const raw=String($json.text??$json.output??\"\").trim();\nconst allowed=new Set([\"SECURITY\",\"LEGAL\",\"TECHNICAL\",\"COMMERCIAL\",\"IMPLEMENTATION\",\"SUPPORT\",\"OTHER\"]); const reqs=[];\nfor(const line of raw.split(/\\r?\\n/)){const clean=line.replace(/^\\s*(?:[-*]|\\d+[.)])\\s*/,\"\").trim(); if(!clean.includes(\"|||\")) continue; const parts=clean.split(\"|||\").map(x=>x.trim()); if(parts.length!==5||!parts[0]) continue; reqs.push({requirement_text:parts[0],category:allowed.has(parts[1].toUpperCase())?parts[1].toUpperCase():\"OTHER\",mandatory:/^(true|yes)$/i.test(parts[2]),risk_hint:parts[3],retrieval_query:parts[4]||parts[0]});}\nif(!reqs.length) throw new Error(\"NVIDIA extraction returned no valid requirement rows\");\nreturn reqs.map((r,i)=>({json:{project_id:src.project_id,requirement_id:`${src.project_id}-REQ-${String(i+1).padStart(3,\"0\")}`,sequence:i+1,requirement_text:r.requirement_text,category:r.category,mandatory:r.mandatory,risk_hint:r.risk_hint,retrieval_query:r.retrieval_query,status:\"EXTRACTED\",owner_email:src.owner_email,due_date:src.due_date,permitted_classifications:(src.permitted_classifications??[\"PUBLIC\",\"INTERNAL\"]).join(\",\"),region:src.region??\"GLOBAL\",created_at:new Date().toISOString(),updated_at:new Date().toISOString()}}));"
      }
    },
    {
      "id": "c3a63b1b7afd6f5b",
      "name": "Append Requirement Matrix",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        450,
        -140
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Requirement Matrix"
        },
        "dataMode": "autoMapInputData",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      },
      "alwaysOutputData": false
    },
    {
      "id": "4788acd3b0eb7ab4",
      "name": "Upsert RFP Project",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        450,
        -20
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "appendOrUpdate",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "RFP Projects"
        },
        "dataMode": "autoMapInputData",
        "columnToMatchOn": "project_id",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      },
      "alwaysOutputData": false
    },
    {
      "id": "99cfa9b9a3d8fcb9",
      "name": "Append Intake Exception",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        -130,
        100
      ],
      "parameters": {
        "authentication": "oAuth2",
        "resource": "sheet",
        "operation": "append",
        "documentId": {
          "mode": "id",
          "value": "SET_OPERATIONS_WORKBOOK_ID"
        },
        "sheetName": {
          "mode": "name",
          "value": "Exception Queue"
        },
        "dataMode": "autoMapInputData",
        "options": {
          "handlingExtraData": "ignoreIt"
        }
      },
      "alwaysOutputData": false
    },
    {
      "id": "eb129a45bf277487",
      "name": "Return Intake Result",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        730,
        20
      ],
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ $json }}",
        "options": {}
      }
    }
  ],
  "connections": {
    "Manual Demo Trigger": {
      "main": [
        [
          {
            "node": "Load Sample RFP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Sample RFP": {
      "main": [
        [
          {
            "node": "Normalize and Validate Intake",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Production RFP Intake Webhook": {
      "main": [
        [
          {
            "node": "Normalize and Validate Intake",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize and Validate Intake": {
      "main": [
        [
          {
            "node": "Intake Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Intake Valid?": {
      "main": [
        [
          {
            "node": "Extract Atomic Requirements",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Append Intake Exception",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "NVIDIA Requirement Extraction Model": {
      "ai_languageModel": [
        [
          {
            "node": "Extract Atomic Requirements",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract Atomic Requirements": {
      "main": [
        [
          {
            "node": "Expand Requirement Matrix",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expand Requirement Matrix": {
      "main": [
        [
          {
            "node": "Append Requirement Matrix",
            "type": "main",
            "index": 0
          },
          {
            "node": "Upsert RFP Project",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Requirement Matrix": {
      "main": [
        [
          {
            "node": "Return Intake Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append Intake Exception": {
      "main": [
        [
          {
            "node": "Return Intake Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "Africa/Lagos",
    "saveManualExecutions": true,
    "saveExecutionProgress": true,
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "executionTimeout": 600
  }
}
