{
  "id": "zAUNmHPItpviDVoV",
  "name": "Tender result on frontend",
  "tags": [],
  "nodes": [
    {
      "id": "d2fbcba5-ad52-49d1-9534-c92bfaad8383",
      "name": "Overview Sticky",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        320
      ],
      "parameters": {
        "width": 406,
        "height": 577,
        "content": "## \ud83c\udfaf Indian Tender Search & Email Report\n\n### How it works\nThis workflow fetches live tenders from TendersInfo.net based on user-provided keywords, filters for GEM (Government e-Marketplace) and MSE (Micro & Small Enterprises) eligible opportunities, and generates a professional HTML email report grouped by company. The webhook receives search criteria, queries the tender API, parses responses, filters relevant tenders, and formats results into a readable table.\n\n### Setup steps\n1. **Replace API authentication**: Update the `Cookie` header in \"Fetch Tender Data from API\" node with your valid `ANTI_FORGERY_TOKEN` and `SESSION_ID` from TendersInfo.net\n2. **Configure webhook**: Copy the production webhook URL from the \"Webhook Trigger\" node\n3. **Test with sample data**: Send a POST request with `{\"keywords\": \"software\", \"msme\": \"eligible\", \"email\": \"user@example.com\"}` to verify the flow\n4. **Activate workflow**: Enable the workflow to start processing real tender searches"
      },
      "typeVersion": 1
    },
    {
      "id": "bbb26d0c-d3b7-4ee3-85c1-9d3833cc5f45",
      "name": "Webhook Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        128,
        272
      ],
      "parameters": {
        "color": 2,
        "content": "## \ud83d\udce5 Webhook Entry\nReceives POST requests with search parameters including keywords, MSME preferences, and recipient email address."
      },
      "typeVersion": 1
    },
    {
      "id": "42c2b9b5-654d-466a-959d-6a08cd3ab1e5",
      "name": "API Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        416,
        288
      ],
      "parameters": {
        "color": 2,
        "width": 360,
        "height": 132,
        "content": "## \ud83d\udd0d API Fetch & Parse\nQueries TendersInfo.net API with user keywords and extracts all tender listings from the JSON response into structured individual records."
      },
      "typeVersion": 1
    },
    {
      "id": "4f2c0770-4536-4369-801c-114865c3be88",
      "name": "Filter Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        848,
        288
      ],
      "parameters": {
        "color": 2,
        "width": 360,
        "height": 132,
        "content": "## \u2705 Filter & Format\nKeeps only GEM and MSE-eligible tenders, then maps fields for email presentation including links, dates, and company details."
      },
      "typeVersion": 1
    },
    {
      "id": "192b7f46-c130-4f25-8107-4bf2ee1e450b",
      "name": "Output Section",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        272
      ],
      "parameters": {
        "color": 2,
        "width": 340,
        "height": 148,
        "content": "## \ud83d\udcca HTML Generation & Response\nBuilds a styled HTML table grouped by company name and returns it as the webhook response for email delivery or display."
      },
      "typeVersion": 1
    },
    {
      "id": "39341326-4c08-440c-9134-938aebd23644",
      "name": "Security Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        144
      ],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 140,
        "content": "## \ud83d\udd10 Credentials & Security\nReplace `__REPLACE__` tokens in the Cookie header with valid session credentials from TendersInfo.net. Never share personal session IDs or API keys publicly."
      },
      "typeVersion": 1
    },
    {
      "id": "661c29a8-c3ad-44bf-a878-a33ee6099232",
      "name": "Webhook Trigger1",
      "type": "n8n-nodes-base.webhook",
      "position": [
        176,
        464
      ],
      "parameters": {
        "path": "ec731146-5297-410c-bfc4-f6fbe59a8f3f",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "bfe068d0-b93a-43f0-a614-50fa6cc459f0",
      "name": "Parse Tender Response1",
      "type": "n8n-nodes-base.code",
      "position": [
        624,
        464
      ],
      "parameters": {
        "jsCode": "// Get the input data\nconst inputData = $input.all();\n\n// Initialize array to store all tenders\nlet allTenders = [];\n\n// Loop through each input item\nfor (const item of inputData) {\n  // Parse the JSON string if it's a string, otherwise use directly\n  let parsedData;\n  \n  if (typeof item.json.data === 'string') {\n    parsedData = JSON.parse(item.json.data);\n  } else {\n    parsedData = item.json.data || item.json;\n  }\n  \n  // Extract TenderList\n  if (parsedData.TenderList && Array.isArray(parsedData.TenderList)) {\n    allTenders = allTenders.concat(parsedData.TenderList);\n  }\n}\n\n// Format each tender as a separate output item\nconst formattedOutput = allTenders.map(tender => ({\n  json: {\n    tenderRefNo: tender.tenderrefno,\n    tcNo: tender.tcno,\n    tenderProcId: tender.tenderprocid,\n    country: tender.countryname,\n    state: tender.statename,\n    city: tender.cityname,\n    keyword: tender.keywordname,\n    subIndustry: tender.subindustryname,\n    earnestMoney: tender.earnestmoney,\n    address: tender.address,\n    quantity: tender.quantity,\n    tenderBrief: tender.tendersbriefnew || tender.tendersbrief,\n    companyName: tender.companyname,\n    tenderDate: tender.tenderdate,\n    openingDate: tender.openingdate,\n    closingDate: tender.closingdate,\n    tenderValue: tender.tendervalue,\n    currency: tender.currcode,\n    isGEM: tender.isgem,\n    isPQ: tender.ispq,\n    isBOQ: tender.isboq,\n    isDocument: tender.isdocument,\n    isFreeTender: tender.isfreetender,\n    isMSE: tender.ismse,\n    isCorrigendum: tender.iscorrigendum,\n    biddingType: tender.biddingtype,\n    publicationDate: tender.publicationdate,\n    fundAgency: tender.fundagencyname,\n    docFees: tender.docfees,\n    descriptionLink: tender.descriptionlink,\n    originalSource: tender.originalsource,\n    scorePercentage: tender.scorePercentage,\n    elasticScore: tender.elastic_score\n  }\n}));\n\nreturn formattedOutput;"
      },
      "typeVersion": 2
    },
    {
      "id": "1850abac-3bbe-4424-a78a-3ed4fd2901af",
      "name": "Select Email Fields1",
      "type": "n8n-nodes-base.set",
      "position": [
        1072,
        464
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "96e09add-c473-4795-949f-0e8e87c7b764",
              "name": "=Name",
              "type": "string",
              "value": "={{ $json.companyName }}"
            },
            {
              "id": "02a76031-a5a3-4075-aacd-97f79b539e8e",
              "name": "Description Link",
              "type": "string",
              "value": "={{ $json.descriptionLink }}"
            },
            {
              "id": "8a6b0b2d-e689-41cb-9f2b-cad8c179d01d",
              "name": "TenderRefNo",
              "type": "string",
              "value": "={{ $json.tenderRefNo }}"
            },
            {
              "id": "b6e779fb-2464-48f6-9160-0ad77c6d13f1",
              "name": "TcNo",
              "type": "string",
              "value": "={{ $json.tcNo }}"
            },
            {
              "id": "9b59a6da-4b6d-4a65-b094-e7b12cbfb57b",
              "name": "TenderProcId",
              "type": "string",
              "value": "={{ $json.tenderProcId }}"
            },
            {
              "id": "afc1819d-2a84-47b1-a3e3-46bbc85c2061",
              "name": "Country",
              "type": "string",
              "value": "={{ $json.country }}"
            },
            {
              "id": "d8d658ba-59db-4276-9aa2-4d4a9520a079",
              "name": "State",
              "type": "string",
              "value": "= {{$json.state }}"
            },
            {
              "id": "420cfa86-e752-4a7f-8a74-ce806fd29eca",
              "name": "City",
              "type": "string",
              "value": "={{ $json.city }}"
            },
            {
              "id": "479b39bf-b616-4c76-ad68-7286e2ee6df9",
              "name": "TenderBrief",
              "type": "string",
              "value": "={{ $json.tenderBrief }}"
            },
            {
              "id": "9f39f749-1cc8-4ebe-bac1-c1bb8175c86c",
              "name": "Company Name",
              "type": "string",
              "value": "={{ $json.companyName }}"
            },
            {
              "id": "d25bac11-4278-48c7-a4d7-4821af82bfca",
              "name": "Tender Date",
              "type": "string",
              "value": "={{ $json.tenderDate }}"
            },
            {
              "id": "10c45fcd-2da8-4050-b3eb-119ed216cbd0",
              "name": "Opening Date",
              "type": "string",
              "value": "={{ $json.openingDate }}"
            },
            {
              "id": "854b9608-f6d9-447d-8545-cd237374c5bf",
              "name": "Closing Date",
              "type": "string",
              "value": "={{ $json.closingDate }}"
            },
            {
              "id": "a2a7bf56-e720-44f9-b1e6-740e2d1672de",
              "name": "Original_Source",
              "type": "string",
              "value": "={{ $json.originalSource }}"
            },
            {
              "id": "ad528850-f5aa-436a-bb05-2dd9a169b8f2",
              "name": "Responder email",
              "type": "string",
              "value": "={{ $('Webhook Trigger1').item.json.body.email }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3c071e78-a5a5-4698-b1cd-99cc00e71073",
      "name": "Fetch Tender Data from API1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        400,
        464
      ],
      "parameters": {
        "url": "https://tendersinfo.net/TenderAI/GetTendersList",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "form-urlencoded",
        "sendHeaders": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "searchkeyword",
              "value": "={{ $json.body.keywords }} tenders from {{ $json.body.location }} {{ $json.body.msme }}"
            },
            {
              "name": "tabindex",
              "value": "1"
            },
            {
              "name": "maintabindex",
              "value": "0"
            },
            {
              "name": "total",
              "value": "200"
            },
            {
              "name": "tender_typewise",
              "value": "live"
            },
            {
              "name": "rescount",
              "value": "0"
            },
            {
              "name": "categoryflag",
              "value": "1"
            },
            {
              "name": "last_rescount",
              "value": "20"
            },
            {
              "name": "sortingScore",
              "value": "0"
            },
            {
              "name": "gemtender",
              "value": "1"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "*/*"
            },
            {
              "name": "Origin",
              "value": "https://tendersinfo.net"
            },
            {
              "name": "Referer",
              "value": "https://tendersinfo.net/TenderAI/TenderAIList"
            },
            {
              "name": "X-Requested-With",
              "value": "XMLHttpRequest"
            },
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0"
            },
            {
              "name": "Cookie",
              "value": "ANTI_FORGERY_TOKEN=__REPLACE__; SESSION_ID=__REPLACE__; TenderTypewise=live;"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "6203e9be-f85e-49be-8cae-0d5f7bcfb004",
      "name": "Filter GEM Tenders Only1",
      "type": "n8n-nodes-base.filter",
      "position": [
        848,
        464
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "or",
          "conditions": [
            {
              "id": "6e683df5-0eca-4fa8-8459-1abbd816a790",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isGEM }}",
              "rightValue": "1"
            },
            {
              "id": "70c84182-508f-4ba4-96fc-5f9a1e0a2be4",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isMSE }}",
              "rightValue": "=1"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a2899512-ed5b-4da3-9d6b-37e26503303b",
      "name": "Respond to Webhook1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1520,
        464
      ],
      "parameters": {
        "options": {},
        "respondWith": "text",
        "responseBody": "={{$node['Build HTML Table'].json['html']}}\n"
      },
      "typeVersion": 1.4
    },
    {
      "id": "6e3b9ca1-d8ac-4711-909d-8fbb27334249",
      "name": "Build HTML Table",
      "type": "n8n-nodes-base.code",
      "position": [
        1296,
        464
      ],
      "parameters": {
        "jsCode": "const items = $input.all().map(i => i.json);\n\n// ---------- Helpers ----------\nfunction toTitleCase(str) {\n  if (!str) return \"Unknown Department\";\n  return str\n    .toLowerCase()\n    .replace(/\\s+/g, \" \")\n    .trim()\n    .split(\" \")\n    .map(w => w.charAt(0).toUpperCase() + w.slice(1))\n    .join(\" \");\n}\n\n// ---------- Group by Department ----------\nconst grouped = {};\n\nitems.forEach(t => {\n  const rawDept = t[\"Company Name\"] || t.Name || \"Unknown Department\";\n  const deptTitle = toTitleCase(rawDept);\n\n  if (!grouped[deptTitle]) grouped[deptTitle] = [];\n  grouped[deptTitle].push(t);\n});\n\n// ---------- CSS ----------\nlet html = `\n<style>\nbody {\n  font-family: Inter, Arial, sans-serif;\n  background: #f6f8fb;\n}\n.tender-wrapper {\n  max-width: 1100px;\n  margin: auto;\n}\n.department {\n  margin: 30px 0 15px;\n  padding: 14px 18px;\n  background: linear-gradient(135deg, #1e3c72, #2a5298);\n  color: #fff;\n  border-radius: 10px;\n  font-size: 20px;\n  font-weight: 600;\n}\n.tender-card {\n  background: #fff;\n  border-radius: 12px;\n  padding: 18px 20px;\n  margin-bottom: 16px;\n  box-shadow: 0 6px 16px rgba(0,0,0,0.08);\n  border-left: 5px solid #2a5298;\n}\n.tender-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));\n  gap: 10px 20px;\n}\n.tender-item {\n  font-size: 14px;\n}\n.tender-item strong {\n  color: #1e3c72;\n}\n.tender-brief {\n  grid-column: 1 / -1;\n  margin-top: 8px;\n}\n.tender-link {\n  margin-top: 14px;\n  display: inline-block;\n  padding: 8px 14px;\n  background: #2a5298;\n  color: #fff !important;\n  border-radius: 6px;\n  text-decoration: none;\n  font-size: 13px;\n}\n.tender-link:hover {\n  background: #1e3c72;\n}\n.footer-note {\n  margin-top: 40px;\n  text-align: center;\n  font-size: 12px;\n  color: #666;\n}\n</style>\n\n<div class=\"tender-wrapper\">\n`;\n\n// ---------- HTML ----------\nObject.entries(grouped).forEach(([dept, tenders]) => {\n  html += `<div class=\"department\">${dept}</div>`;\n\n  tenders.forEach(t => {\n    html += `\n      <div class=\"tender-card\">\n        <div class=\"tender-grid\">\n          <div class=\"tender-item\"><strong>Tender Ref:</strong> ${t.TenderRefNo || \"-\"}</div>\n          <div class=\"tender-item\"><strong>TC No:</strong> ${t.TcNo || \"-\"}</div>\n          <div class=\"tender-item\"><strong>Proc ID:</strong> ${t.TenderProcId || \"-\"}</div>\n          <div class=\"tender-item\"><strong>Country:</strong> ${toTitleCase(t.Country)}</div>\n          <div class=\"tender-item\"><strong>State:</strong> ${toTitleCase(t.State)}</div>\n          <div class=\"tender-item\"><strong>City:</strong> ${toTitleCase(t.City)}</div>\n          <div class=\"tender-item\"><strong>Tender Date:</strong> ${t[\"Tender Date\"] || \"-\"}</div>\n          <div class=\"tender-item\"><strong>Opening Date:</strong> ${t[\"Opening Date\"] || \"-\"}</div>\n          <div class=\"tender-item\"><strong>Closing Date:</strong> ${t[\"Closing Date\"] || \"-\"}</div>\n\n          <div class=\"tender-brief\">\n            <strong>Brief:</strong><br>\n            ${t.TenderBrief || \"-\"}\n          </div>\n        </div>\n\n        <a class=\"tender-link\" href=\"${t[\"Description Link\"]}\" target=\"_blank\">\n          View Tender Details\n        </a>\n      </div>\n    `;\n  });\n});\n\nhtml += `\n  <div class=\"footer-note\">\n    Source: GeM / TenderTiger \u2022 Generated Automatically\n  </div>\n</div>\n`;\n\n// ---------- Return ----------\nreturn [{ json: { html } }];\n\n"
      },
      "typeVersion": 2
    }
  ],
  "active": true,
  "settings": {
    "binaryMode": "separate",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "67845b2e-27a6-45c3-963d-c0a4efed0e60",
  "connections": {
    "Build HTML Table": {
      "main": [
        [
          {
            "node": "Respond to Webhook1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger1": {
      "main": [
        [
          {
            "node": "Fetch Tender Data from API1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Select Email Fields1": {
      "main": [
        [
          {
            "node": "Build HTML Table",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Tender Response1": {
      "main": [
        [
          {
            "node": "Filter GEM Tenders Only1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter GEM Tenders Only1": {
      "main": [
        [
          {
            "node": "Select Email Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Tender Data from API1": {
      "main": [
        [
          {
            "node": "Parse Tender Response1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}