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 →
{
"name": "Upwork Pre-Bid Triage \u00b7 PRO",
"nodes": [
{
"parameters": {},
"id": "aa000000-0000-4000-8000-000000000001",
"name": "\u25b6 Run now",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-368,
624
],
"typeVersion": 1
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours"
}
]
}
},
"id": "aa000000-0000-4000-8000-000000000002",
"name": "\ud83d\udd52 Every hour",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-368,
832
],
"typeVersion": 1.2
},
{
"parameters": {
"jsCode": "// keyword set to sweep \u2014 edit freely\nreturn [\n { json: { q: 'AI automation' } },\n { json: { q: 'GoHighLevel' } },\n { json: { q: 'n8n' } },\n { json: { q: 'make.com automation' } }\n];"
},
"id": "aa000000-0000-4000-8000-000000000003",
"name": "\u2460 Keywords",
"type": "n8n-nodes-base.code",
"position": [
48,
688
],
"typeVersion": 2
},
{
"parameters": {
"url": "http://localhost:4178/api/search",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "q",
"value": "={{ $json.q }}"
},
{
"name": "limit",
"value": "6"
}
]
},
"options": {}
},
"id": "aa000000-0000-4000-8000-000000000004",
"name": "\u2461 Search (per keyword)",
"type": "n8n-nodes-base.httpRequest",
"position": [
400,
688
],
"typeVersion": 4.2
},
{
"parameters": {
"jsCode": "// flatten all keyword results + dedupe by job id\nconst seen = new Set(); const out = [];\nfor (const it of $input.all()) {\n for (const j of (it.json.jobs || [])) {\n if (seen.has(j.id)) continue; seen.add(j.id); out.push({ json: j });\n }\n}\nreturn out;"
},
"id": "aa000000-0000-4000-8000-000000000005",
"name": "\u2462 Flatten + dedupe",
"type": "n8n-nodes-base.code",
"position": [
768,
688
],
"typeVersion": 2
},
{
"parameters": {
"url": "http://localhost:4178/api/job",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "id",
"value": "={{ $json.id }}"
}
]
},
"options": {}
},
"id": "aa000000-0000-4000-8000-000000000006",
"name": "\u2463 Enrich (deep intel)",
"type": "n8n-nodes-base.httpRequest",
"position": [
1120,
688
],
"typeVersion": 4.2
},
{
"parameters": {
"jsCode": "// zip search + deep intel, score, tag, add trust gap\nconst search = $('\u2462 Flatten + dedupe').all();\nconst deep = $input.all();\nconst num = v => (v==null||v===''||isNaN(+v)) ? null : +v;\nconst out = [];\nfor (let i=0;i<deep.length;i++){\n const s = (search[i]||{}).json || {};\n const j = (deep[i].json.job) || {};\n const bid = j.bid_stats || {}; const cr = j.client_record || {};\n const wh = j.work_history || {}; const priv = [...(wh.closed||[]),...(wh.open||[])].map(r=>num(r.feedback_to_client_score)).filter(x=>x!=null&&x>0);\n const privAvg = priv.length ? +(priv.reduce((a,b)=>a+b,0)/priv.length).toFixed(2) : null;\n const spend = num(cr.spend_total); const prop = num(s.proposal_count)||0;\n const hired = (j.job_activity&&j.job_activity.totalHired)||0;\n const score = Math.round(100*(0.34*(spend!=null?Math.min(1,Math.log10(spend+1)/6):0.2)+0.28*(1-Math.min(1,prop/50))+0.20*(hired>0?0.2:1)+0.18*(bid.avg!=null?Math.min(1,bid.avg/120):0.4)));\n const tag = score>=72?'HOT':score>=55?'WATCH':'SKIP';\n const feedback = num(cr.feedback_score);\n out.push({ json: { score, tag, title: s.title||j.title, type: s.job_type, proposals: prop, bid_avg: bid.avg, bid_min: bid.min, bid_max: bid.max, client_spend: spend, client_country:(s.client||{}).country, verified:(s.client||{}).verification_status==='VERIFIED', connects_cost: num(j.connects_cost), feedback, private_avg: privAvg, trust_gap:(feedback!=null&&privAvg!=null)?+(feedback-privAvg).toFixed(2):null, id: s.id } });\n}\nout.sort((a,b)=>b.json.score-a.json.score);\nreturn out;"
},
"id": "aa000000-0000-4000-8000-000000000007",
"name": "\u2464 Score & tag",
"type": "n8n-nodes-base.code",
"position": [
1488,
688
],
"typeVersion": 2
},
{
"parameters": {
"rules": {
"values": [
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 1
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.tag }}",
"rightValue": "HOT"
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 1
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "string",
"operation": "equals"
},
"leftValue": "={{ $json.tag }}",
"rightValue": "WATCH"
}
]
}
},
{
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "loose",
"version": 1
},
"combinator": "and",
"conditions": [
{
"operator": {
"type": "number",
"operation": "gte"
},
"leftValue": "={{ $json.trust_gap }}",
"rightValue": 0.5
}
]
}
}
]
},
"options": {
"fallbackOutput": "extra"
}
},
"id": "aa000000-0000-4000-8000-000000000008",
"name": "\u2465 Route by signal",
"type": "n8n-nodes-base.switch",
"position": [
1840,
688
],
"typeVersion": 3
},
{
"parameters": {},
"id": "aa000000-0000-4000-8000-000000000009",
"name": "\ud83c\udfaf Top pick",
"type": "n8n-nodes-base.limit",
"position": [
2208,
368
],
"typeVersion": 1
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4178/api/analyze",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "id",
"value": "={{ $json.id }}"
}
]
},
"options": {
"timeout": 240000
}
},
"id": "aa000000-0000-4000-8000-00000000000a",
"name": "\u26a1 Analyze (LLM)",
"type": "n8n-nodes-base.httpRequest",
"position": [
2560,
368
],
"typeVersion": 4.2,
"onError": "continueRegularOutput"
},
{
"parameters": {
"method": "POST",
"url": "http://localhost:4178/api/draft",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "id",
"value": "={{ $('\ud83c\udfaf Top pick').first().json.id }}"
}
]
},
"options": {
"timeout": 240000
}
},
"id": "aa000000-0000-4000-8000-00000000000b",
"name": "\u270d Draft proposal (LLM)",
"type": "n8n-nodes-base.httpRequest",
"position": [
2928,
368
],
"typeVersion": 4.2,
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "// bundle the top pick with its LLM analysis + drafted proposal\nconst job = $('\ud83c\udfaf Top pick').first().json;\nconst analysis = ($('\u26a1 Analyze (LLM)').first().json||{}).analysis || '(analysis pending)';\nconst draft = ($('\u270d Draft proposal (LLM)').first().json||{}).draft || '(draft pending)';\nreturn [{ json: { ...job, llm_analysis: analysis, llm_draft: draft } }];"
},
"id": "aa000000-0000-4000-8000-00000000000c",
"name": "\ud83d\udccb Assemble intel + proposal",
"type": "n8n-nodes-base.code",
"position": [
3280,
368
],
"typeVersion": 2
},
{
"parameters": {},
"id": "aa000000-0000-4000-8000-00000000000d",
"name": "\ud83d\udc40 Watchlist",
"type": "n8n-nodes-base.noOp",
"position": [
2208,
688
],
"typeVersion": 1
},
{
"parameters": {},
"id": "aa000000-0000-4000-8000-00000000000e",
"name": "\ud83d\udea9 Risky client alert",
"type": "n8n-nodes-base.noOp",
"position": [
2208,
912
],
"typeVersion": 1
},
{
"parameters": {
"jsCode": "// build a human digest from the full scored set\nconst jobs = $('\u2464 Score & tag').all().map(i=>i.json);\nconst money=v=>v==null?'\u2014':Math.abs(v)>=1e3?'$'+(v/1e3).toFixed(0)+'K':'$'+v;\nconst hot=jobs.filter(j=>j.tag==='HOT'), watch=jobs.filter(j=>j.tag==='WATCH'), risk=jobs.filter(j=>j.trust_gap!=null&&j.trust_gap>=0.5);\nconst L=['# Pre-Bid Digest','',`Scanned **${jobs.length}** gigs \u00b7 ${hot.length} HOT \u00b7 ${watch.length} WATCH \u00b7 ${risk.length} trust-risk`,'','## Open first'];\nhot.slice(0,5).forEach(j=>L.push(`- **[${j.score}] ${j.title}** \u2014 ${j.client_country||''} \u00b7 spend ${money(j.client_spend)} \u00b7 ${j.proposals} proposals \u00b7 bid ${j.bid_avg!=null?('$'+Math.round(j.bid_avg)):'\u2014'}`));\nif(risk.length){L.push('','## \u26a0 Trust-gap flags');risk.forEach(j=>L.push(`- ${j.title} \u2014 public ${j.feedback} vs private ${j.private_avg} (gap +${j.trust_gap})`));}\nreturn [{ json: { digest: L.join('\\n'), scanned: jobs.length, hot: hot.length, watch: watch.length, risk: risk.length } }];"
},
"id": "aa000000-0000-4000-8000-00000000000f",
"name": "\ud83d\udcca Build digest",
"type": "n8n-nodes-base.code",
"position": [
1488,
1120
],
"typeVersion": 2
},
{
"parameters": {},
"id": "aa000000-0000-4000-8000-000000000010",
"name": "\ud83d\udce4 Digest ready",
"type": "n8n-nodes-base.noOp",
"position": [
1840,
1120
],
"typeVersion": 1
},
{
"parameters": {
"url": "http://localhost:4178/api/dashboard",
"options": {}
},
"id": "aa000000-0000-4000-8000-000000000011",
"name": "\ud83d\udcc8 My dashboard",
"type": "n8n-nodes-base.httpRequest",
"position": [
48,
1120
],
"typeVersion": 4.2
},
{
"parameters": {},
"id": "aa000000-0000-4000-8000-000000000012",
"name": "\ud83d\udca0 Connects snapshot",
"type": "n8n-nodes-base.noOp",
"position": [
400,
1120
],
"typeVersion": 1
},
{
"parameters": {
"content": "## \u2460 INGEST \u2014 multi-keyword sweep\nFan out across keywords \u2192 dedupe by job id. All calls go through the **MCP bridge** (OAuth + refresh handled).",
"height": 140,
"width": 1060,
"color": 4
},
"id": "aa000000-0000-4000-8000-000000000021",
"name": "note-ingest",
"type": "n8n-nodes-base.stickyNote",
"position": [
32,
480
],
"typeVersion": 1
},
{
"parameters": {
"content": "## \u2461 ENRICH + SCORE\nPull deep intel per gig (competitor bids, client spend, **private ratings**), compute an opportunity score + trust gap.",
"height": 140,
"width": 620,
"color": 5
},
"id": "aa000000-0000-4000-8000-000000000022",
"name": "note-score",
"type": "n8n-nodes-base.stickyNote",
"position": [
1120,
480
],
"typeVersion": 1
},
{
"parameters": {
"content": "## \u2462 ROUTE\nSwitch each gig \u2192 HOT \u00b7 WATCH \u00b7 Trust-risk.",
"height": 140,
"width": 340,
"color": 3
},
"id": "aa000000-0000-4000-8000-000000000023",
"name": "note-route",
"type": "n8n-nodes-base.stickyNote",
"position": [
1760,
288
],
"typeVersion": 1
},
{
"parameters": {
"content": "## \u2463 LLM AGENT (Codex, free)\nOn the top pick: **\u26a1 analyze** (fit / win-odds / suggested bid / red flags) + **\u270d draft a proposal**. Wire an approval before any submit.",
"height": 140,
"width": 1440,
"color": 2
},
"id": "aa000000-0000-4000-8000-000000000024",
"name": "note-llm",
"type": "n8n-nodes-base.stickyNote",
"position": [
2208,
192
],
"typeVersion": 1
},
{
"parameters": {
"content": "## \u2464 DIGEST + ALERTS\nHuman-readable digest of the batch + trust-gap flags. Add Slack / email here.",
"height": 140,
"width": 620,
"color": 6
},
"id": "aa000000-0000-4000-8000-000000000025",
"name": "note-digest",
"type": "n8n-nodes-base.stickyNote",
"position": [
1488,
928
],
"typeVersion": 1
}
],
"connections": {
"\u25b6 Run now": {
"main": [
[
{
"node": "\u2460 Keywords",
"type": "main",
"index": 0
},
{
"node": "\ud83d\udcc8 My dashboard",
"type": "main",
"index": 0
}
]
]
},
"\u2460 Keywords": {
"main": [
[
{
"node": "\u2461 Search (per keyword)",
"type": "main",
"index": 0
}
]
]
},
"\ud83c\udfaf Top pick": {
"main": [
[
{
"node": "\u26a1 Analyze (LLM)",
"type": "main",
"index": 0
}
]
]
},
"\u2464 Score & tag": {
"main": [
[
{
"node": "\u2465 Route by signal",
"type": "main",
"index": 0
},
{
"node": "\ud83d\udcca Build digest",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udd52 Every hour": {
"main": [
[
{
"node": "\u2460 Keywords",
"type": "main",
"index": 0
}
]
]
},
"\u26a1 Analyze (LLM)": {
"main": [
[
{
"node": "\u270d Draft proposal (LLM)",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcc8 My dashboard": {
"main": [
[
{
"node": "\ud83d\udca0 Connects snapshot",
"type": "main",
"index": 0
}
]
]
},
"\ud83d\udcca Build digest": {
"main": [
[
{
"node": "\ud83d\udce4 Digest ready",
"type": "main",
"index": 0
}
]
]
},
"\u2465 Route by signal": {
"main": [
[
{
"node": "\ud83c\udfaf Top pick",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud83d\udc40 Watchlist",
"type": "main",
"index": 0
}
],
[
{
"node": "\ud83d\udea9 Risky client alert",
"type": "main",
"index": 0
}
],
[]
]
},
"\u2462 Flatten + dedupe": {
"main": [
[
{
"node": "\u2463 Enrich (deep intel)",
"type": "main",
"index": 0
}
]
]
},
"\u2463 Enrich (deep intel)": {
"main": [
[
{
"node": "\u2464 Score & tag",
"type": "main",
"index": 0
}
]
]
},
"\u2461 Search (per keyword)": {
"main": [
[
{
"node": "\u2462 Flatten + dedupe",
"type": "main",
"index": 0
}
]
]
},
"\u270d Draft proposal (LLM)": {
"main": [
[
{
"node": "\ud83d\udccb Assemble intel + proposal",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
},
"nodeGroups": []
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Upwork Pre-Bid Triage · PRO. Uses httpRequest. Event-driven trigger; 23 nodes.
Source: https://github.com/Majboor/upwork-cli/blob/main/examples/n8n/upwork-MCP-automation.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This workflow listens for an “Approved” label on a Trello card, reads the AI draft bookkeeping JSON from card comments, and posts the corresponding transaction to Xero. It then adds a Xero deep link b
02_LLM_Pipeline v1.0. Uses executeWorkflowTrigger, httpRequest, seaTable. Event-driven trigger; 65 nodes.
This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t
[n8n] Advanced URL Parsing and Shortening Workflow - Switchy.io Integration. Uses splitInBatches, stickyNote, httpRequest, html. Event-driven trigger; 56 nodes.
[](https://youtu.be/c7yCZhmMjtI)