Skip to content

Instantly share code, notes, and snippets.

@masylum
Created July 9, 2025 07:55
Show Gist options
  • Select an option

  • Save masylum/f8e918bc59da8522011620b12b3079ca to your computer and use it in GitHub Desktop.

Select an option

Save masylum/f8e918bc59da8522011620b12b3079ca to your computer and use it in GitHub Desktop.
A job search bot example using n8n and handinger
{
"name": "Soham Bot",
"nodes": [
{
"parameters": { "rule": { "interval": [ {} ] } },
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [ 40, 60 ],
"id": "3ff110bf-6a51-4fef-aa1a-cd188c3b7792",
"name": "Schedule Trigger"
},
{
"parameters": {
"fromEmail": "Soham Bot <soham@handinger.com>",
"toEmail": "Test <test@handinger.com>",
"subject": "=Soham Bot: We have discovered {{ $json.data.length }} job offers",
"html": "={{ $json.htmlTable }}",
"options": {}
},
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [ 2240, 120 ],
"id": "8bd43c9b-0a72-4d7d-8efc-dfe607fcdd8b",
"name": "Send email",
"webhookId": "075c9e02-e865-4898-a222-95e819b895c1",
"credentials": {
}
},
{
"parameters": {
"jsCode": "const items = $input.all().flatMap((item) => item.json).flatMap(json => json.data);\nconsole.log(items)\nconst htmlTable = `\n <table>\n <tr>\n <th>Company</th>\n <th>Offer</th>\n <th>Salary Range</th>\n <th>Summary</th>\n <th>Is the job offer good fit?</th>\n </tr>\n ${items.map((item) => `\n <tr>\n <td>${item.company}</td>\n <td><a href=\"${item.link}\">${item.title}</a></td>\n <td>${item.salary_range}</td>\n <td>${item.summary}</td>\n <td style=\"${\n item.fit === 'good'\n ? 'background: #ddffcc'\n : item.fit === 'medium' ? 'background: #ffffdd'\n : 'background: #ffdddd'\n }\"\n >\n <strong style=\"${\n item.fit === 'good'\n ? 'color: #447733'\n : item.fit === 'medium' ? 'color: #777733'\n : 'color: #773333'\n }\">${item.fit}</strong>:\n ${item.fit_reason}\n </td>\n </tr>\n`,\n )\n .join(\"\")}\n</table>\n`;\n\nreturn { htmlTable };\n"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [ 1800, 220 ],
"id": "d10dcd3e-91d0-48e0-bdde-2f932db6f319",
"name": "Code1"
},
{
"parameters": {},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [ 1140, 120 ],
"id": "250ed82b-a322-4dbd-951c-5ff8eab66fc8",
"name": "Merge"
},
{
"parameters": {
"fieldToSplitOut": "link",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [ 480, -40 ],
"id": "db1411ca-a9b0-4bbe-8f8a-4bbacf2fa14b",
"name": "Split Out"
},
{
"parameters": {
"fieldToSplitOut": "items",
"options": {}
},
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [ 480, 220 ],
"id": "499d95b1-9dd1-4e1c-b7c4-8bb9da4d4c19",
"name": "Split Out1"
},
{
"parameters": {
"aggregate": "aggregateAllItemData",
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [ 1580, 120 ],
"id": "bc18385d-2af1-4c01-a0ee-d263614891fc",
"name": "Aggregate"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [ 2020, 120 ],
"id": "5aba0de4-74ea-4300-9b55-a5fb85080a5c",
"name": "Merge1"
},
{
"parameters": {
"mode": "combine",
"combineBy": "combineByPosition",
"options": {}
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3.2,
"position": [ 920, -40 ],
"id": "4e83cb49-1416-4e89-9e85-3c0d2cfe0a1f",
"name": "Merge2"
},
{
"parameters": {
"type": "code",
"code": "const fieldName = 'fit';\nconst priority = { good: 0, medium: 1, bad: 2 };\n\nif (priority[a.json[fieldName]] < priority[b.json[fieldName]]) {\n return -1;\n}\nif (priority[a.json[fieldName]] > priority[b.json[fieldName]]) {\n return 1;\n}\nreturn 0;"
},
"type": "n8n-nodes-base.sort",
"typeVersion": 1,
"position": [ 1360, 120 ],
"id": "d6ec33fc-7a6f-4104-90b1-e427d0c88e0e",
"name": "Sort"
},
{
"parameters": {
"url": "https://api.handinger.com/llm",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "prompt",
"value": "=I'm a senior frontend engineer with 5 years experience. I've worked with React, Angular, Typescript, CSS, HTML and also Tailwind. I can also design and have some basic knowledge on devops and backend.\n\nI live in a remote town in Spain and I want to earn at least $70k.\n\nExtract the following information from a markdown document containing a list of jobs. Output a json.\n\nFor each job offer return:\n- company name\n- job title\n- job link\n- the salary range if available.\n- a short summary of the job offer\n- fit: do the technologies, the experience, the salary, and the location approximately match? Can it be \"bad\", \"medium\", \"good\"\n- fit reason: Explanation of the fit\n\nContent:"
},
{
"name": "json_schema",
"value": "={\n \"type\":\"object\",\n \"properties\":{\n \"items\":{\n \"type\":\"array\",\n \"items\":{\n \"type\":\"object\",\n \"properties\":{\n \"company\":{ \"type\":\"string\" },\n \"title\":{ \"type\":\"string\" },\n \"link\": {\"type\": \"string\"},\n \"salary_range\":{ \"type\":\"string\" },\n \"summary\":{ \"type\":\"string\" },\n \"fit\":{ \"enum\": [\"bad\", \"medium\", \"good\"] },\n \"fit_reason\":{ \"type\":\"string\" }\n },\n \"required\":[ \"company\", \"title\", \"link\", \"salary_range\", \"summary\", \"fit\", \"fit_reason\" ]\n }\n }\n },\n \"required\":[ \"items\" ]\n}"
},
{
"name": "url",
"value": "https://remoteok.com/?min_salary=70000&order_by=date&action=get_jobs"
},
{
"name": "fresh",
"value": "true"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer XXXXXXXX"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [ 260, 220 ],
"id": "427cad8f-2c77-4105-85da-f0b0a5a7b96c",
"name": "Fetch remoteok"
},
{
"parameters": {
"url": "https://api.handinger.com/llm",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "prompt",
"value": "=I'm a senior frontend engineer with 5 years experience. I've worked with React, Angular, Typescript, CSS, HTML and also Tailwind. I can also design and have some basic knowledge on devops and backend.\n\nI live in a remote town in Spain and I want to earn at least $70k.\n\nExtract the following information from a job offer written in markdown:\n- company name\n- job title\n- the salary range if available.\n- a short summary of the job offer\n- fit: do the technologies, the experience, the salary, and the location approximately match? Can it be \"bad\", \"medium\", \"good\"\n- fit reason: Explanation of the fit\n\nContent:"
},
{
"name": "json_schema",
"value": "={\n \"type\": \"object\",\n \"properties\": {\n \"company\": {\n \"type\": \"string\"\n },\n \"title\": {\n \"type\": \"string\"\n },\n \"salary_range\": {\n \"type\": \"string\"\n },\n \"summary\": {\n \"type\": \"string\"\n },\n \"fit\": {\n \"enum\": [\n \"bad\",\n \"medium\",\n \"good\"\n ]\n },\n \"fit_reason\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"company\",\n \"title\",\n \"salary_range\",\n \"summary\",\n \"fit\",\n \"fit_reason\"\n ]\n}"
},
{
"name": "fresh",
"value": "true"
},
{
"name": "url",
"value": "={{ $json.link }}"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer XXXXXXXX"
}
]
},
"options": {
"batching": {
"batch": {
"batchSize": 20,
"batchInterval": 20000
}
}
}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [ 700, 35 ],
"id": "90be77d8-a512-43eb-8756-834f749edb0d",
"name": "Fetch details: weworkremotely"
},
{
"parameters": {
"url": "https://api.handinger.com/llm",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "prompt",
"value": "=Given a markdown with a list of job offers.\nFor each job, extract the job offer link.\n\nContent:"
},
{
"name": "json_schema",
"value": "={\n \"type\":\"object\",\n \"properties\":{\n \"link\":{\n \"type\":\"array\",\n \"items\":{\n \"type\":\"string\",\n }\n }\n }\n}"
},
{
"name": "url",
"value": "https://weworkremotely.com/categories/remote-full-stack-programming-jobs"
}
]
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer XXXXXXXX"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [ 260, -40 ],
"id": "a165f020-2145-4100-982f-cd997dabfda3",
"name": "Fetch list from weworkremotely",
"retryOnFail": true
}
],
"pinData": {},
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Fetch remoteok",
"type": "main",
"index": 0
},
{
"node": "Fetch list from weworkremotely",
"type": "main",
"index": 0
}
]
]
},
"Send email": {
"main": [
[]
]
},
"Code1": {
"main": [
[
{
"node": "Merge1",
"type": "main",
"index": 1
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "Fetch details: weworkremotely",
"type": "main",
"index": 0
},
{
"node": "Merge2",
"type": "main",
"index": 0
}
]
]
},
"Split Out1": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 1
}
]
]
},
"Merge": {
"main": [
[
{
"node": "Sort",
"type": "main",
"index": 0
}
]
]
},
"Aggregate": {
"main": [
[
{
"node": "Code1",
"type": "main",
"index": 0
},
{
"node": "Merge1",
"type": "main",
"index": 0
}
]
]
},
"Merge1": {
"main": [
[
{
"node": "Send email",
"type": "main",
"index": 0
}
]
]
},
"Merge2": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"Sort": {
"main": [
[
{
"node": "Aggregate",
"type": "main",
"index": 0
}
]
]
},
"Fetch remoteok": {
"main": [
[
{
"node": "Split Out1",
"type": "main",
"index": 0
}
]
]
},
"Fetch details: weworkremotely": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 1
}
]
]
},
"Fetch list from weworkremotely": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "d9ac3f9c-fef1-443b-a067-56227521cb6c",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "380234b0f7d1bfa6f3115ce8d46ba296f995cd10c30cff5125872ac16ea20d4b"
},
"id": "Kr2zGjIw7o36PD4v",
"tags": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment