Veni AI
Wróć do szablonów
SZABLON WORKFLOW

N8n Template Toggle Raport With Projects | Operasyon için n8n Otomasyon İş Akışı Şablonu (HTTP)

n8n için hazır otomasyon şablonu: N8n Template Toggle Raport With Projects. 7 düğüm. Entegrasyonlar: HTTP. JSON'u kopyalayıp n8n'e içe aktarın.

7 węzłyN8n_Template_Toggle_Raport_With_Projects-workflow.json
{
"id": "LLC5sbrvJwsCxFXq",
"meta": {
"instanceId": "5e682be7cc4c76c24876e009260b2f7cab628b1b42d88efaa638dccb09cd1db5",
"templateCredsSetupCompleted": true
},
"name": "n8n template - toggle raport with projects",
"tags": [
"toggle"
],
"nodes": [
{
"id": "2632242c-671b-4e26-a9f4-534ecf3cfea6",
"name": "Get Toggle Projects",
"type": "n8n-nodes-base.httpRequest",
"position": [
-304,
-128
],
"parameters": {
"url": "https://api.track.toggl.com/api/v9/workspaces/TOGGL_WORKSPACE_ID/projects",
"options": {
"pagination": {
"pagination": {
"parameters": {
"parameters": [
{
"name": "page",
"value": "={{ $pageCount + 1 }}"
}
]
}
}
}
},
"sendQuery": true,
"authentication": "predefinedCredentialType",
"queryParameters": {
"parameters": [
{}
]
},
"nodeCredentialType": "togglApi"
},
"credentials": {
"togglApi": {
"id": "MiPq5PIxcT1suV6B",
"name": "Unnamed credential"
}
},
"typeVersion": 4.2
},
{
"id": "60dd3732-4a0f-4544-b52e-1b2b2cb969cb",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-704,
-240
],
"parameters": {
"rule": {
"interval": [
{
"field": "months",
"triggerAtHour": 4
}
]
}
},
"typeVersion": 1.2
},
{
"id": "63830937-0d2e-4ac2-bf8b-037483deba94",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-496,
-560
],
"parameters": {
"color": 4,
"width": 662,
"height": 602,
"content": "## Fetch Toggl clients, projects, and summary data form last month via HTTP nodes\n\n1. Enter your TOGGL_WORKSPACE_ID"
},
"typeVersion": 1
},
{
"id": "871b782a-06c8-464f-abe0-6cfbe93dfe3d",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
176,
-560
],
"parameters": {
"color": 4,
"width": 310,
"height": 602,
"content": "## Merge data to associate projects with clients.\n\n"
},
"typeVersion": 1
},
{
"id": "43d03168-970c-44a1-93ac-6a1d0289def2",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
496,
-560
],
"parameters": {
"color": 4,
"width": 294,
"height": 602,
"content": "## Generate an HTML report with hours per client and per project\n\n"
},
"typeVersion": 1
},
{
"id": "66c498df-8b6d-4ea7-b717-968411b1e918",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
-800,
-560
],
"parameters": {
"color": 4,
"width": 294,
"height": 602,
"content": "## Schedule automation (monthly)\n"
},
"typeVersion": 1
},
{
"id": "8db1e677-00e5-416d-b0c8-97faf5725149",
"name": "README",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1232,
-784
],
"parameters": {
"width": 416,
"height": 832,
"content": "## Description\nThis workflow fetches Toggl Track summary data for the previous month, aggregates hours per client and project, and emails a clean HTML report via Resend.\n\n## How it works\n1) Compute previous month period.\n2) Fetch Toggl summary (grouping=clients, sub_grouping=projects).\n3) Fetch clients and projects for proper names.\n4) Merge and aggregate seconds to hours.\n5) Generate HTML raport \n6) Send raport via Resend API.\n\n## Requirements\n- [Toggl free account](https://accounts.toggl.com/track/signup/) (Login, Pass, TOGGL_WORKSPACE_ID)\n- Resend.com free account RESEND_API_KEY\n\n## Customization\n- Change trigger time in the Schedule Trigger node.\n- Modify period calculation for weekly/quarterly in Get Toggle Summary node.\n- Add archived projects by querying with active=false&archived=true and merging.\n\n## Documentation\n- [Toggle docs](https://engineering.toggl.com/docs/)\n- [Resend.com docs](https://resend.com/docs/)\n\n## Author\nKrystian Syryca - [krsweb.pl](https://krsweb.pl)\n"
},
"typeVersion": 1
},
{
"id": "3aa03862-f0a0-40e7-9d68-20d656a0d304",
"name": "Get Toggle Summary",
"type": "n8n-nodes-base.httpRequest",
"position": [
-304,
-352
],
"parameters": {
"url": "https://api.track.toggl.com/reports/api/v3/workspace/TOGGL_WORKSPACE_ID/summary/time_entries",
"method": "POST",
"options": {},
"sendBody": true,
"sendHeaders": true,
"authentication": "predefinedCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "start_date",
"value": "={{ $now.minus({ months: 1 }).startOf('month').toFormat('yyyy-MM-dd') }}"
},
{
"name": "end_date",
"value": "={{ $now.minus({ months: 1 }).endOf('month').toFormat('yyyy-MM-dd') }}"
},
{
"name": "grouping",
"value": "clients"
},
{
"name": "sub_grouping",
"value": "projects"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"nodeCredentialType": "togglApi"
},
"credentials": {
"togglApi": {
"id": "MiPq5PIxcT1suV6B",
"name": "Unnamed credential"
}
},
"typeVersion": 4.2
},
{
"id": "6d86fedc-c291-43dd-b008-1e0395d48d9f",
"name": "Prepare html raport",
"type": "n8n-nodes-base.code",
"position": [
576,
-240
],
"parameters": {
"jsCode": "const data = $input.all().map(i => i.json);\nconst period = $json.period || \"Previous month\";\n\n// Group by clinet\nconst clients = {};\nfor (const i of data) {\n const clientId = i.client_id || i.cid;\n const clientName = i.client_name || \"(missing customer name)\";\n if (!clients[clientId]) {\n clients[clientId] = { name: clientName, projects: [] };\n }\n clients[clientId].projects.push({\n project_id: i.project_id,\n project_name: i.name || \"(bez nazwy projektu)\",\n seconds: Number(i.seconds) || 0\n });\n}\n\n// Sum clients time\nfor (const c of Object.values(clients)) {\n c.totalSeconds = c.projects.reduce((a, p) => a + p.seconds, 0);\n}\n\n// Sort clients\nconst sortedClients = Object.entries(clients)\n .sort(([, a], [, b]) => b.totalSeconds - a.totalSeconds);\n\n// Generate html raport\nlet totalSeconds = 0;\nlet html = \"\";\nhtml += \"

Time Report per Client and Project - \" + period + \"

\";\n\nfor (const [clientId, c] of sortedClients) {\n const clientHours = (c.totalSeconds / 3600).toFixed(2);\n totalSeconds += c.totalSeconds;\n\n html += \"

\" + c.name + \" – \" + clientHours + \" h

\";\n html += \"
    \";\n\n const sortedProjects = c.projects.sort((a, b) => b.seconds - a.seconds);\n for (const p of sortedProjects) {\n const hours = (p.seconds / 3600).toFixed(2);\n html += \"
  • \" + p.project_name + \" – \" + hours + \" h
  • \";\n }\n\n html += \"
\";\n}\n\nhtml += \"

Total hours: \" + (totalSeconds / 3600).toFixed(2) + \"

\";\nhtml += \"\";\n\nreturn [{\n json: {\n subject: \"Time Report per Client and Project - \" + period,\n html\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "90949ac4-fd1d-4321-a9e1-c985a23e7b12",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
800,
-560
],
"parameters": {
"color": 4,
"width": 294,
"height": 602,
"content": "## Send the report via Resend email API.\n1. Enter RESEND_API_KEY\n2. Enter FROM and TO email addresses\n\n"
},
"typeVersion": 1
},
{
"id": "3cf0af49-6e80-4dbc-8395-9861ed371a69",
"name": "Send email via Resend",
"type": "n8n-nodes-base.httpRequest",
"position": [
864,
-240
],
"parameters": {
"url": "https://api.resend.com/emails",
"method": "POST",
"options": {},
"jsonBody": "={\n \"from\": \"\",\n \"to\": [\"to@yourdomain.com\"],\n \"subject\": \"{{$json.subject}}\",\n \"html\": \"{{$json.html}}\"\n}\n",
"sendBody": true,
"sendHeaders": true,
"specifyBody": "json",
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer YOUR_TOKEN_HERE"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "be2604f7-a5ca-46fe-b711-f53a81b1363e",
"name": "Merge data",
"type": "n8n-nodes-base.merge",
"position": [
288,
-240
],
"parameters": {
"mode": "combine",
"options": {},
"advanced": true,
"joinMode": "enrichInput1",
"mergeByFields": {
"values": [
{
"field1": "project_id",
"field2": "id"
}
]
}
},
"typeVersion": 3.2
},
{
"id": "29d8af43-2951-4c9e-80de-556f65588a65",
"name": "Prepare projects list",
"type": "n8n-nodes-base.code",
"position": [
-80,
-352
],
"parameters": {
"jsCode": "const groups = $json.groups || [];\nconst result = [];\n\nfor (const g of groups) {\n const clientId = Number(g.id);\n const subs = g.sub_groups || [];\n for (const s of subs) {\n result.push({\n json: {\n client_id: clientId,\n project_id: Number(s.id),\n seconds: Number(s.seconds) || 0\n }\n });\n }\n}\n\nreturn result;\n"
},
"typeVersion": 2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "873a699b-08da-4b2e-b0af-66c2cd539290",
"connections": {
"Merge data": {
"main": [
[
{
"node": "Prepare html raport",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Get Toggle Summary",
"type": "main",
"index": 0
},
{
"node": "Get Toggle Projects",
"type": "main",
"index": 0
}
]
]
},
"Get Toggle Summary": {
"main": [
[
{
"node": "Prepare projects list",
"type": "main",
"index": 0
}
]
]
},
"Get Toggle Projects": {
"main": [
[
{
"node": "Merge data",
"type": "main",
"index": 1
}
]
]
},
"Prepare html raport": {
"main": [
[
{
"node": "Send email via Resend",
"type": "main",
"index": 0
}
]
]
},
"Prepare projects list": {
"main": [
[
{
"node": "Merge data",
"type": "main",
"index": 0
}
]
]
}
}
}

W edytorze n8n: wklej za pomocą Ctrl+VWorkflow zostanie utworzony