{
  "id": "Q4LhFRDraaQuorBs",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Upwork Job Aggregator & Notifier",
  "tags": [],
  "nodes": [
    {
      "id": "834d0a42-1398-4cd4-aaf3-b1164e00b4a8",
      "name": "Daily Upwork Job Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        0,
        0
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "a0d54dfe-fc68-4020-b2ed-d191a57d5897",
      "name": "Fetch Upwork Jobs (Apify)",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        220,
        0
      ],
      "parameters": {
        "url": "https://api.apify.com/v2/actor-tasks/<YOUR_TASK_ID>/run-sync-get-dataset-items?token=<YOUR_API_TOKEN>",
        "method": "POST",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "a9812fc7-0fa5-492a-91aa-392d0c8ee43b",
      "name": "Format Job Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        440,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "24bd782a-a34f-4b98-8d8f-21763181677b",
              "name": "title",
              "type": "string",
              "value": "={{ $json.title }}"
            },
            {
              "id": "ee879b3d-339a-44c2-9e11-f653edab2e7f",
              "name": "url",
              "type": "string",
              "value": "={{ $json.url }}"
            },
            {
              "id": "b5ff7975-667b-4997-ac8f-acbe9a3b3b85",
              "name": "description",
              "type": "string",
              "value": "={{ $json.description }}"
            },
            {
              "id": "29051fc3-e4f8-4e39-a024-d7b943271acc",
              "name": "budget",
              "type": "string",
              "value": "={{ $json.budget }}"
            },
            {
              "id": "a6492128-a58a-4735-b427-12265f81da3a",
              "name": "datePosted",
              "type": "string",
              "value": "={{ $json.datePosted }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a996aa35-8f0d-4e1a-997b-4aadd402f65c",
      "name": "Log Jobs to Google Sheet",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        740,
        0
      ],
      "parameters": {
        "columns": {
          "value": {
            "url": "={{ $json.url }}",
            "title": "={{ $json.title }}",
            "budget": "={{ $json.budget }}",
            "datePosted": "={{ $json.datePosted }}",
            "description": "={{ $json.description }}"
          },
          "schema": [
            {
              "id": "title",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "title",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "url",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "url",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "description",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "description",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "budget",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "budget",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "datePosted",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "datePosted",
              "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/1dEU6uMB4ehiGXjIExjtFQHvUjANRODawKMBGKDaTcEc/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1dEU6uMB4ehiGXjIExjtFQHvUjANRODawKMBGKDaTcEc",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1dEU6uMB4ehiGXjIExjtFQHvUjANRODawKMBGKDaTcEc/edit?usp=drivesdk",
          "cachedResultName": "Upwork Scraper"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "e0cd68e5-4d6d-479a-8a48-1faccc9f3ff7",
      "name": "Summarize Job Listings",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        960,
        0
      ],
      "parameters": {
        "text": "=Provide a summary of the upwork jobs. It should be in email format\n\nTitle: {{ $json.title }}\nURL: {{ $json.url }}\nDescription:{{ $json.description }}\nBudget:{{ $json.budget }}\nDate posted: {{ $json.datePosted }}",
        "options": {},
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 1.9
    },
    {
      "id": "30002e32-6046-4e5c-a27f-a0d58f380ae9",
      "name": "Send Job Summary Email",
      "type": "n8n-nodes-base.gmail",
      "position": [
        1500,
        0
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.output.Summary }}",
        "options": {},
        "subject": "={{ $json.output.subject }}"
      },
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "a1fb5d66-b780-4b6b-96be-3391b6196301",
      "name": "OpenAI Job Summarizer",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        900,
        260
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "f79a7336-80fc-4832-862b-51d0d737d5a3",
      "name": "Parse Summary Output",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1160,
        260
      ],
      "parameters": {
        "jsonSchemaExample": "{\n  \"subject\": \"Upwork Job Opportunity: Webflow Expert Needed for Portfolio Website\",\n  \"Summary\": \"The client is seeking a designer with strong experience in Webflow to create a responsive and elegant portfolio site.\"\n}\n"
      },
      "typeVersion": 1.2
    },
    {
      "id": "85465ead-0a06-455c-9308-866c092d8ef5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -60,
        -560
      ],
      "parameters": {
        "color": 3,
        "width": 640,
        "height": 760,
        "content": "### \ud83d\udd01 **Section 1: Job Fetch & Preparation**\n\n\ud83d\udce6 **Group Name**: `\ud83d\udce5 Fetch & Prepare Upwork Jobs`\n\n#### \u2705 **Included Nodes:**\n\n1. `\ud83d\udd52 Daily Upwork Job Trigger`\n2. `\ud83c\udf10 Fetch Upwork Jobs (Apify)`\n3. `\ud83d\udee0\ufe0f Format Job Fields`\n\n#### \ud83d\udccb **What this group does:**\n\nThis group is responsible for automatically triggering the job search, pulling fresh job data from Upwork (via Apify), and formatting that data into a clean, structured format.\n\n#### \ud83d\udccc **Highlights:**\n\n* Uses Apify\u2019s `run-sync-get-dataset-items` to ensure you get fresh job results in one go.\n* Ensures only the required fields (like title, URL, budget) are passed forward.\n* Keeps your data clean and minimal for logging and summarization.\n\n---"
      },
      "typeVersion": 1
    },
    {
      "id": "3699ba73-75ee-4bf2-91eb-8d0d05143321",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        680,
        -620
      ],
      "parameters": {
        "color": 5,
        "width": 620,
        "height": 1040,
        "content": "### \ud83d\udcca **Section 2: Data Logging & Summary Generation**\n\n\ud83d\udce6 **Group Name**: `\ud83e\uddfe Log & Summarize Jobs`\n\n#### \u2705 **Included Nodes:**\n\n1. `\ud83d\udcc8 Log Jobs to Google Sheet`\n2. `\ud83e\udd16 Summarize Job Listings`\n\n   * `\ud83e\udde0 OpenAI Job Summarizer`\n   * `\ud83d\udcc4 Parse Summary Output`\n\n#### \ud83d\udccb **What this group does:**\n\nThis group handles the dual responsibility of:\n\n* Persistently logging all job entries to a **Google Sheet**\n* Generating a human-friendly **natural language summary** of the fetched jobs using the OpenAI model and parsing the result into clean output\n\n#### \ud83d\udca1 **Why this matters:**\n\n* Keeps historical records in Sheets \u2705\n* Makes email digest more meaningful and less overwhelming for the reader \ud83e\udde0\n\n---\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "2289feb5-9ab1-4dbb-a77e-f4742f0f341d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1400,
        -420
      ],
      "parameters": {
        "color": 6,
        "width": 340,
        "height": 600,
        "content": "## \ud83d\udce4 **Section 3: Job Summary Notification**\n\n\ud83d\udce6 **Group Name**: `\u2709\ufe0f Notify via Email`\n\n#### \u2705 **Included Node:**\n\n1. `\ud83d\udce7 Send Job Summary Email`\n\n#### \ud83d\udccb **What this group does:**\n\nSends a **summary email** to the desired recipient(s) with the content generated by the AI agent.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "95727922-4856-4481-acff-519638ac5d23",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1520,
        -560
      ],
      "parameters": {
        "color": 4,
        "width": 1300,
        "height": 320,
        "content": "=======================================\n            WORKFLOW ASSISTANCE\n=======================================\nFor any questions or support, please contact:\n    Yaron@nofluff.online\n\nExplore more tips and tutorials here:\n   - YouTube: https://www.youtube.com/@YaronBeen/videos\n   - LinkedIn: https://www.linkedin.com/in/yaronbeen/\n=======================================\n"
      },
      "typeVersion": 1
    },
    {
      "id": "c161b105-70a6-431a-91e2-9031ccba8fe4",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1520,
        -220
      ],
      "parameters": {
        "color": 4,
        "width": 1289,
        "height": 3058,
        "content": "# \u2705 n8n Automation: \"Upwork Job Aggregator & Notifier\"\n\n> **Purpose**: Automatically fetch job postings from Upwork with specific keywords, save them in Google Sheets, summarize them using AI, and email the results daily (or at any interval you want).\n\n---\n\n## \ud83e\uddf1 HIGH-LEVEL OVERVIEW\n\n| Feature         | Description                                          |\n| --------------- | ---------------------------------------------------- |\n| \ud83d\udd50 Trigger      | Scheduled (e.g., every 6 hours)                      |\n| \ud83c\udf10 Source       | [Apify](https://apify.com) actor for Upwork scraping |\n| \ud83d\udcc4 Storage      | Google Sheets                                        |\n| \ud83e\udde0 Summary      | OpenAI model with structured output                  |\n| \ud83d\udcec Notification | Gmail with formatted job summary                     |\n\n---\n\n## \ud83e\udde0 GROUP 1: \ud83d\udce5 Fetch & Prepare Upwork Jobs\n\n### \ud83d\udfe2 Nodes:\n\n1. `\ud83d\udd52 Daily Upwork Job Trigger`\n\n   * **Type**: Cron Trigger\n   * **Purpose**: Runs the flow automatically at your set interval (e.g., every 6 hours or daily)\n\n2. `\ud83c\udf10 Fetch Upwork Jobs (Apify)`\n\n   * **Type**: HTTP Request\n   * **Method**: POST\n   * **URL**: `https://api.apify.com/v2/actor-tasks/<TASK_ID>/run-sync-get-dataset-items?token=<API_TOKEN>`\n   * **Purpose**: Calls your Apify Upwork Scraper task to get the latest job listings\n   * **Output**: JSON array of job objects\n\n3. `\ud83d\udee0\ufe0f Format Job Fields`\n\n   * **Type**: Edit Fields (Set or Function Node)\n   * **Purpose**: Extracts needed job fields like `title`, `url`, `description`, `budget`, and `datePosted`\n   * **Reason**: Makes data uniform for Sheet storage and AI summary\n\n---\n\n## \ud83d\udcca GROUP 2: \ud83e\uddfe Log & Summarize Jobs\n\n### \ud83d\udfe2 Nodes:\n\n4. `\ud83d\udcc8 Log Jobs to Google Sheet`\n\n   * **Type**: Google Sheets \u2192 Append Sheet\n   * **Sheet Setup**: Must have headers like `Title`, `URL`, `Description`, `Budget`, `Date`\n   * **Purpose**: Appends each new job as a row in the sheet for historical tracking\n\n5. `\ud83e\udd16 Summarize Job Listings` (AI Agent node group)\n\n   * **Type**: Tools Agent (LangChain / OpenAI Integration)\n   * **Connected Sub-Nodes**:\n\n     * `\ud83e\udde0 OpenAI Job Summarizer` (OpenAI Chat Model)\n\n       * **Prompt** example:\n\n         ```\n         You are an assistant summarizing freelance job postings. Summarize each job in 1-2 lines. Include title, budget, and a short description.\n         ```\n     * `\ud83d\udcc4 Parse Summary Output` (Structured Output Parser)\n\n       * **Purpose**: Converts the AI response into structured n8n items\n   * **Input**: Job data from Google Sheets\n   * **Output**: A clear, natural language summary of all jobs\n\n---\n\n## \ud83d\udce4 GROUP 3: \u2709\ufe0f Notify via Email\n\n### \ud83d\udfe2 Node:\n\n6. `\ud83d\udce7 Send Job Summary Email`\n\n   * **Type**: Gmail \u2192 Send Message\n\n   * **Authentication**: Google OAuth2\n\n   * **To**: Yourself or your team\n\n   * **Subject**: `\"\ud83d\ude80 New Upwork Jobs Found!\"`\n\n   * **Body**:\n\n     ```\n     Hello,\n\n     Here\u2019s a summary of the latest Upwork jobs matching your criteria:\n\n     {{summary from AI}}\n\n     \u2795 View full job list here: [Google Sheet Link]\n\n     Best,  \n     Your Automation Bot \ud83e\udd16\n     ```\n\n   * **Error Handling**: Add an IF node to prevent email if no jobs were returned (optional)\n\n---\n\n## \ud83d\uddc2 Suggested Color-Coded Groups in n8n:\n\n| Group                          | Nodes                                | Color     | Description                     |\n| ------------------------------ | ------------------------------------ | --------- | ------------------------------- |\n| \ud83d\udce5 Fetch & Prepare Upwork Jobs | Trigger, HTTP Request, Format Fields | \ud83d\udfe2 Green  | Gets data and prepares it       |\n| \ud83e\uddfe Log & Summarize Jobs        | Google Sheets, AI Agent, Parser      | \ud83d\udfe1 Yellow | Logs + creates smart summaries  |\n| \u2709\ufe0f Notify via Email            | Gmail                                | \ud83d\udd34 Red    | Notifies you with clean summary |\n\n---\n\n## \ud83d\ude80 ADVANTAGES\n\n\u2705 Completely automated\n\u2705 Saves all job history\n\u2705 AI-enhanced summaries\n\u2705 Instant email alerts\n\u2705 Easy to expand or customize\n\n---\n\n## \ud83e\udde9 Bonus Enhancements (Optional)\n\n| Idea                      | Description                                                   |\n| ------------------------- | ------------------------------------------------------------- |\n| \ud83d\udd0d Add keyword filters    | Add filters before saving to Sheets (e.g., only \u201cReact\u201d jobs) |\n| \ud83d\udd75\ufe0f\u200d\u2642\ufe0f Prevent duplicates | Store job IDs and check before saving                         |\n| \ud83d\udce3 Add Slack alerts       | Add Slack node for team notification                          |\n| \ud83d\udcca Dashboard              | Build Google Data Studio report over the Sheet                |\n\n---\n\n\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "8c929fc6-2198-4adf-89c3-ff0a2b296fdd",
  "connections": {
    "Format Job Fields": {
      "main": [
        [
          {
            "node": "Log Jobs to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Summary Output": {
      "ai_outputParser": [
        [
          {
            "node": "Summarize Job Listings",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Job Summarizer": {
      "ai_languageModel": [
        [
          {
            "node": "Summarize Job Listings",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Job Listings": {
      "main": [
        [
          {
            "node": "Send Job Summary Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Daily Upwork Job Trigger": {
      "main": [
        [
          {
            "node": "Fetch Upwork Jobs (Apify)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Jobs to Google Sheet": {
      "main": [
        [
          {
            "node": "Summarize Job Listings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Upwork Jobs (Apify)": {
      "main": [
        [
          {
            "node": "Format Job Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}