AutomationFlowsEmail & Gmail › Track Upwork Jobs From Vollna RSS with Google Sheets Logging and Slack Alerts

Track Upwork Jobs From Vollna RSS with Google Sheets Logging and Slack Alerts

ByJeremiah Wright @jerrywright on n8n.io

Freelancers and agencies who track new Upwork leads via Vollna RSS and want clean logging to Google Sheets with instant Slack alerts.

Cron / scheduled trigger★★★★☆ complexity15 nodesGoogle SheetsRSS Feed ReadSlackGmail
Email & Gmail Trigger: Cron / scheduled Nodes: 15 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #9020 — we link there as the canonical source.

This workflow follows the Gmail → 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 →

Download .json
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "a13633fc-3429-4840-8a86-0f3f552fc4f5",
      "name": "Check for foreign characters",
      "type": "n8n-nodes-base.filter",
      "position": [
        240,
        -16
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "da8372f6-230d-4bdd-9c59-a6ead041739e",
              "operator": {
                "type": "string",
                "operation": "regex"
              },
              "leftValue": "={{ $json.title }}",
              "rightValue": "=^[\\x00-\\x7F]+$"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "b52a5f5f-fd41-4dab-8b5b-a2e1935ae0e0",
      "name": "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories",
      "type": "n8n-nodes-base.code",
      "position": [
        816,
        -16
      ],
      "parameters": {
        "jsCode": "/**\n * Function node (not Function Item)\n * Processes all incoming items and returns one item per job.\n */\nconst inputs = $input.all();\n\nconst safeMultiDecode = (str, times = 3) => {\n  let prev = str, cur = str;\n  for (let i = 0; i < times; i++) {\n    try { cur = decodeURIComponent(prev); } catch { break; }\n    if (cur === prev) break;\n    prev = cur;\n  }\n  return cur;\n};\n\nconst tryExtractParam = (urlStr) => {\n  if (typeof urlStr !== \"string\" || !urlStr) return \"\";\n  try {\n    const u = new URL(urlStr);\n    const param = u.searchParams.get(\"url\");\n    if (param) return safeMultiDecode(param);\n    if (u.hostname.includes(\"upwork.com\")) return urlStr;\n    return \"\";\n  } catch {\n    const part = urlStr.split(\"url=\")[1];\n    return part ? safeMultiDecode(part) : \"\";\n  }\n};\n\nconst formatPosted = (pubDateStr) => {\n  if (typeof pubDateStr !== \"string\" || !pubDateStr.trim()) {\n    return { posted: \"\", date: \"\" };\n  }\n  const pubDate = new Date(pubDateStr);\n  if (isNaN(pubDate.getTime())) return { posted: \"\", date: \"\" };\n\n  const now = new Date();\n  const diffMs = now - pubDate;\n  const diffMinutes = Math.floor(diffMs / 60000);\n  const diffHours = Math.floor(diffMinutes / 60);\n  const diffDays = Math.floor(diffHours / 24);\n\n  let posted = \"\";\n  if (diffMinutes < 1) posted = \"Posted just now\";\n  else if (diffMinutes < 60) posted = `Posted ${diffMinutes} minute${diffMinutes !== 1 ? \"s\" : \"\"} ago`;\n  else if (diffHours < 24) posted = `Posted ${diffHours} hour${diffHours !== 1 ? \"s\" : \"\"} ago`;\n  else posted = `Posted ${diffDays} day${diffDays !== 1 ? \"s\" : \"\"} ago`;\n\n  const formattedDate = pubDate.toLocaleDateString(\"en-GB\", { day: \"numeric\", month: \"long\", year: \"numeric\" });\n  return { posted, date: formattedDate };\n};\n\nconst outputs = inputs.map(({ json }) => {\n  const out = {};\n\n  // 1) Title \u2192 { title, budget }\n  (() => {\n    const input = json?.title;\n    if (typeof input !== \"string\") {\n      out.title = \"\";\n      out.budget = \"\";\n      return;\n    }\n    const regex = /^(.*?)\\s*\\((.*?)\\)$/;\n    const match = input.match(regex);\n    out.title = match ? match[1].trim() : input;\n    out.budget = match ? match[2].trim() : \"\";\n  })();\n\n  // 2) Link \u2192 upwork_link\n  out.upwork_link = tryExtractParam(json?.link) || \"\";\n\n  // 3) pubDate \u2192 { posted, date }\n  Object.assign(out, formatPosted(json?.pubDate));\n\n  // 4) content \u2192 { job_description, skills, categories }\n  (() => {\n    const raw = json?.content;\n    if (typeof raw !== \"string\" || !raw) {\n      out.job_description = \"\";\n      out.skills = \"\";\n      out.categories = \"\";\n      return;\n    }\n    const text = raw\n      .replace(/&amp;/g, \"&\")\n      .replace(/&nbsp;/g, \" \")\n      .replace(/\\r\\n/g, \"\\n\")\n      .replace(/\\r/g, \"\\n\");\n\n    const skillsMatch = text.match(/^\\s*Skills:\\s*([^\\n\\r]+)/im);\n    const categoriesMatch = text.match(/^\\s*Categories:\\s*([^\\n\\r]+)/im);\n\n    out.skills = skillsMatch ? skillsMatch[1].trim() : \"\";\n    out.categories = categoriesMatch ? categoriesMatch[1].trim() : \"\";\n\n    out.job_description = text\n      .replace(/^\\s*Skills:.*$/gim, \"\")\n      .replace(/^\\s*Categories:.*$/gim, \"\");\n  })();\n\n  return { json: out };\n});\n\nreturn outputs;"
      },
      "typeVersion": 2
    },
    {
      "id": "407c809a-89a6-4727-b6af-f7596077bf66",
      "name": "Append row in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1776,
        -32
      ],
      "parameters": {
        "columns": {
          "value": {
            "DATE": "={{ $json.date }}",
            "TITLE": "={{ $json.title }}",
            "BUDGET": "={{ $json.budget }}",
            "POSTED": "={{ $json.posted }}",
            "SKILLS": "={{ $json.skills }}",
            "CATEGORIES": "={{ $json.categories }}",
            "JOB DESCRIPTION": "={{ $json.job_description }}",
            "UPWORK JOB LINK": "={{ $json.upwork_link }}"
          },
          "schema": [
            {
              "id": "TITLE",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "TITLE",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "BUDGET",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "BUDGET",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "POSTED",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "POSTED",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "JOB DESCRIPTION",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "JOB DESCRIPTION",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "DATE",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "DATE",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "SKILLS",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "SKILLS",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CATEGORIES",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "CATEGORIES",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "UPWORK JOB LINK",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "UPWORK JOB LINK",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit?usp=drivesdk",
          "cachedResultName": "Upwork Jobs Automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "eff67652-6bd6-4ccc-8fb6-0b1a443b40cf",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -304,
        -16
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 3
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "2cb9b9fc-0221-4039-8e32-6fb1d45c6d11",
      "name": "RSS Read",
      "type": "n8n-nodes-base.rssFeedRead",
      "position": [
        -48,
        -16
      ],
      "parameters": {
        "url": "https://www.vollna.com/rss/rftHMpSQCGeEfr2Zwjzb",
        "options": {
          "customFields": ""
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9e4f6014-dce7-4923-9cd3-b1655f1ac925",
      "name": "Sort",
      "type": "n8n-nodes-base.sort",
      "position": [
        512,
        -16
      ],
      "parameters": {
        "options": {},
        "sortFieldsUi": {
          "sortField": [
            {
              "order": "descending",
              "fieldName": "pubDate"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c6d4316a-f13f-464d-b3d7-7315f50b1a28",
      "name": "Get row(s) in sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1088,
        144
      ],
      "parameters": {
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupValue": "={{ $json.upwork_link }}",
              "lookupColumn": "UPWORK JOB LINK"
            }
          ]
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_66goIMtz3uZpxzhDGvG9a1GZ6t96i7Q72o6wzZIJ2s/edit?usp=drivesdk",
          "cachedResultName": "Upwork Jobs Automation"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "5925e47c-7174-4811-acbf-5aa7499922bc",
      "name": "Compare Datasets",
      "type": "n8n-nodes-base.compareDatasets",
      "position": [
        1360,
        -16
      ],
      "parameters": {
        "options": {},
        "mergeByFields": {
          "values": [
            {
              "field1": "upwork_link",
              "field2": "UPWORK JOB LINK"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "dd503da0-f241-42e2-a8dc-14794fc73bef",
      "name": "Send a message",
      "type": "n8n-nodes-base.slack",
      "position": [
        2112,
        -32
      ],
      "parameters": {
        "text": "=New Job Alert :fire: :fire:\n\n{{ $json.TITLE }} - {{ $json.BUDGET }}\n\n{{ $json.POSTED }}\n\n{{ $json['UPWORK JOB LINK'] }}",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": "C09DV2HQ2M9",
          "cachedResultName": "n8n-jobs"
        },
        "otherOptions": {
          "includeLinkToWorkflow": false
        },
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "7dc37ecc-6fe2-4fa3-8c54-0b1a76221a4d",
      "name": "Send a message1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        2672,
        -32
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "\u2705 A message was just sent to #n8n-jobs!",
        "options": {
          "appendAttribution": false
        },
        "subject": "Slack #n8n",
        "emailType": "text"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "86e8692d-42e3-4598-9e59-efecbae62c67",
      "name": "Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2368,
        -32
      ],
      "parameters": {
        "options": {},
        "fieldsToAggregate": {
          "fieldToAggregate": [
            {
              "fieldToAggregate": "message"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "27b4377b-c6b7-410c-a896-9e74b5af9c05",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -528
      ],
      "parameters": {
        "width": 720,
        "height": 448,
        "content": "Trigger & Input\n\nLocation: Near the Schedule Trigger and RSS Read nodes\nNote Text:\nTrigger & Input\n\n    Runs every 3 minutes\n    Fetches new jobs from Vollna RSS feed\n\nData Cleaning & Filtering\n\nLocation: Next to the Check for foreign characters node\nNote Text:\nFilter Jobs\n\n    Only allow jobs with English (ASCII) titles\n    Prevents non-English/foreign character jobs\n"
      },
      "typeVersion": 1
    },
    {
      "id": "46cf316d-a0d9-493b-b933-f44925065ab2",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        464,
        -528
      ],
      "parameters": {
        "color": 5,
        "width": 800,
        "height": 448,
        "content": "Sorting\n\nLocation: Next to the Sort node\nNote Text:\nSort Jobs\n\n    Sorts jobs by most recent (descending by pubDate)\n\nData Extraction & Formatting\n\nLocation: Next to the Title, Budget, Link, Posted, Date, Job Description, Skills, Categories node\nNote Text:\nExtract & Format Data\n\n    Splits title and budget\n    Extracts Upwork link\n    Formats posted date\n    Extracts job description, skills, and categories\n"
      },
      "typeVersion": 1
    },
    {
      "id": "b7c8fbe2-09b9-4a8f-9d57-372d4ac69e43",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1360,
        -528
      ],
      "parameters": {
        "color": 6,
        "width": 720,
        "height": 448,
        "content": "Duplicate Check\n\nLocation: Between Get row(s) in sheet and Compare Datasets nodes\nNote Text:\nCheck for Duplicates\n\n    Looks up job link in Google Sheet\n    Compares to avoid duplicate entries\n\nAppend to Sheet\n\nLocation: Next to Append row in sheet node\nNote Text:\nSave New Job\n\n    Appends new, unique jobs to Google Sheet\n"
      },
      "typeVersion": 1
    },
    {
      "id": "dca71453-7f0f-4e52-8862-a29e191372fd",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2144,
        -528
      ],
      "parameters": {
        "color": 4,
        "width": 704,
        "height": 448,
        "content": "Notification\n\nLocation: Next to Send a message node\nNote Text:\nSlack Notification\n\n    Sends new job alert to #n8n-jobs Slack channel\n\n(Optional) Email Confirmation\n\nLocation: Next to Aggregate and Send a message1 nodes\nNote Text:\nEmail Confirmation\n\n    Sends email to confirm Slack message was sent\n"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Sort": {
      "main": [
        [
          {
            "node": "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RSS Read": {
      "main": [
        [
          {
            "node": "Check for foreign characters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate": {
      "main": [
        [
          {
            "node": "Send a message1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send a message": {
      "main": [
        []
      ]
    },
    "Compare Datasets": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "RSS Read",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Append row in sheet": {
      "main": [
        [
          {
            "node": "Send a message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet": {
      "main": [
        [
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Check for foreign characters": {
      "main": [
        [
          {
            "node": "Sort",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Title, Budget, Link, Posted, Date, Job Description, Skills, Categories": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet",
            "type": "main",
            "index": 0
          },
          {
            "node": "Compare Datasets",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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

Freelancers and agencies who track new Upwork leads via Vollna RSS and want clean logging to Google Sheets with instant Slack alerts.

Source: https://n8n.io/workflows/9020/ — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

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

Email & Gmail

The workflow is triggered automatically every day at 12:00 PM using a Cron node.

RSS Feed Read, Google Sheets, Gmail +1
Email & Gmail

This workflow automatically scans AWS accounts for orphaned resources (unattached EBS volumes, old snapshots &gt;90 days, unassociated Elastic IPs) that waste money. It calculates cost impact, validat

Slack, Gmail, AWS Lambda +1
Email & Gmail

Streamline IT and operations change management by automating approval routing, Jira issue creation, audit logging, and real-time Slack alerts. This workflow ensures faster reviews, traceable approvals

Monday.com, Slack, Jira +2
Email & Gmail

Streamline IT and operations change management by automating approval routing, Jira issue creation, audit logging, and real-time Slack alerts. This workflow ensures faster reviews, traceable approvals

Monday.com, Slack, Jira +2
Email & Gmail

Automate your GoHighLevel (GHL) pipeline tracking and deal management process. This workflow fetches all opportunities, calculates the time spent in each stage, logs historical pipeline data in Google

High Level, Google Sheets, Gmail +1