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": "Quantra - Weekly Reports Generator",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtDay": 1,
"triggerAtHour": 7,
"triggerAtMinute": 0
}
]
}
},
"id": "weekly-trigger",
"name": "Every Monday at 7 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
0,
0
]
},
{
"parameters": {
"jsCode": "// Calculate date range for last week\nconst now = new Date();\nconst lastMonday = new Date(now);\nlastMonday.setDate(now.getDate() - 7);\nlastMonday.setHours(0, 0, 0, 0);\n\nconst lastSunday = new Date(lastMonday);\nlastSunday.setDate(lastMonday.getDate() + 6);\nlastSunday.setHours(23, 59, 59, 999);\n\nreturn [{\n json: {\n start_date: lastMonday.toISOString().split('T')[0],\n end_date: lastSunday.toISOString().split('T')[0],\n start_datetime: lastMonday.toISOString(),\n end_datetime: lastSunday.toISOString()\n }\n}];"
},
"id": "calc-date-range",
"name": "Calculate Date Range",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
220,
0
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/time_entries",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "clock_in",
"value": "=gte.{{ $json.start_datetime }}"
},
{
"name": "clock_in",
"value": "=lt.{{ $json.end_datetime }}"
},
{
"name": "select",
"value": "id,employee_id,clock_in,clock_out,regular_minutes,overtime_minutes,total_minutes,status"
}
]
}
},
"id": "get-time-entries",
"name": "Get Week's Time Entries",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
440,
-200
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/expense_reports",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "submitted_at",
"value": "=gte.{{ $('Calculate Date Range').first().json.start_datetime }}"
},
{
"name": "submitted_at",
"value": "=lt.{{ $('Calculate Date Range').first().json.end_datetime }}"
},
{
"name": "select",
"value": "id,employee_id,amount,category,status"
}
]
}
},
"id": "get-expenses",
"name": "Get Week's Expenses",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
440,
0
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/time_off_requests",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "created_at",
"value": "=gte.{{ $('Calculate Date Range').first().json.start_datetime }}"
},
{
"name": "created_at",
"value": "=lt.{{ $('Calculate Date Range').first().json.end_datetime }}"
},
{
"name": "select",
"value": "id,employee_id,request_type,status,hours_requested"
}
]
}
},
"id": "get-time-off",
"name": "Get Week's Time Off",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
440,
200
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/tasks",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "created_at",
"value": "=gte.{{ $('Calculate Date Range').first().json.start_datetime }}"
},
{
"name": "created_at",
"value": "=lt.{{ $('Calculate Date Range').first().json.end_datetime }}"
},
{
"name": "select",
"value": "id,status,priority"
}
]
}
},
"id": "get-tasks",
"name": "Get Week's Tasks",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
440,
400
]
},
{
"parameters": {
"jsCode": "// Compile weekly report\nconst dateRange = $('Calculate Date Range').first().json;\nconst timeEntries = $('Get Week\\'s Time Entries').first().json || [];\nconst expenses = $('Get Week\\'s Expenses').first().json || [];\nconst timeOff = $('Get Week\\'s Time Off').first().json || [];\nconst tasks = $('Get Week\\'s Tasks').first().json || [];\n\n// Time tracking stats (convert minutes to hours)\nconst totalHours = timeEntries.reduce((sum, e) => sum + ((e.regular_minutes || 0) + (e.overtime_minutes || 0)) / 60, 0);\nconst overtimeHours = timeEntries.reduce((sum, e) => sum + (e.overtime_minutes || 0) / 60, 0);\nconst uniqueEmployeesWorked = [...new Set(timeEntries.map(e => e.employee_id))].length;\n\n// Expense stats\nconst totalExpenses = expenses.reduce((sum, e) => sum + (e.amount || 0), 0);\nconst approvedExpenses = expenses.filter(e => e.status === 'approved').reduce((sum, e) => sum + (e.amount || 0), 0);\nconst pendingExpenses = expenses.filter(e => e.status === 'pending').reduce((sum, e) => sum + (e.amount || 0), 0);\n\n// Time off stats\nconst totalTimeOffRequests = timeOff.length;\nconst approvedTimeOff = timeOff.filter(t => t.status === 'approved').length;\nconst pendingTimeOff = timeOff.filter(t => t.status === 'pending').length;\n\n// Task stats\nconst totalTasks = tasks.length;\nconst completedTasks = tasks.filter(t => t.status === 'completed').length;\nconst inProgressTasks = tasks.filter(t => t.status === 'in_progress').length;\n\nconst report = {\n report_type: 'weekly_summary',\n period: {\n start_date: dateRange.start_date,\n end_date: dateRange.end_date\n },\n time_tracking: {\n total_hours: Math.round(totalHours * 100) / 100,\n overtime_hours: Math.round(overtimeHours * 100) / 100,\n employees_worked: uniqueEmployeesWorked,\n entries_count: timeEntries.length\n },\n expenses: {\n total_submitted: Math.round(totalExpenses * 100) / 100,\n total_approved: Math.round(approvedExpenses * 100) / 100,\n pending_amount: Math.round(pendingExpenses * 100) / 100,\n count: expenses.length\n },\n time_off: {\n total_requests: totalTimeOffRequests,\n approved: approvedTimeOff,\n pending: pendingTimeOff\n },\n tasks: {\n total: totalTasks,\n completed: completedTasks,\n in_progress: inProgressTasks,\n completion_rate: totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0\n },\n generated_at: new Date().toISOString()\n};\n\nreturn [{ json: report }];"
},
"id": "compile-report",
"name": "Compile Weekly Report",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
880,
100
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/reports",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
},
{
"name": "Prefer",
"value": "return=representation"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"type\": \"weekly_summary\",\n \"period_start\": \"{{ $json.period.start_date }}\",\n \"period_end\": \"{{ $json.period.end_date }}\",\n \"data\": {{ JSON.stringify($json) }},\n \"generated_at\": \"{{ $json.generated_at }}\",\n \"generated_by\": \"ai_worker\"\n}"
},
"id": "save-report",
"name": "Save Report to DB",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1100,
100
]
},
{
"parameters": {
"method": "GET",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/employees",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
}
]
},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "role",
"value": "=in.(admin,owner)"
},
{
"name": "status",
"value": "=eq.active"
},
{
"name": "select",
"value": "id,email,first_name"
}
]
}
},
"id": "get-admins",
"name": "Get Admin Users",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1320,
100
]
},
{
"parameters": {
"jsCode": "// Create notification for each admin\nconst admins = $input.first().json || [];\nconst report = $('Compile Weekly Report').first().json;\n\nconst notifications = admins.map(admin => ({\n employee_id: admin.id,\n title: '\ud83d\udcca Weekly Report Ready',\n body: `Week of ${report.period.start_date}: ${report.time_tracking.total_hours}h worked, $${report.expenses.total_submitted.toFixed(2)} expenses`,\n data: {\n type: 'weekly_report',\n report_id: $('Save Report to DB').first().json?.id,\n period_start: report.period.start_date,\n period_end: report.period.end_date\n }\n}));\n\nreturn notifications.map(n => ({ json: n }));"
},
"id": "prepare-notifications",
"name": "Prepare Admin Notifications",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1540,
100
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.N8N_WEBHOOK_URL }}/webhook/push-notification",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"type\": \"weekly_report\",\n \"employee_id\": \"{{ $json.employee_id }}\",\n \"title\": \"{{ $json.title }}\",\n \"body\": \"{{ $json.body }}\",\n \"data\": {{ JSON.stringify($json.data) }}\n}"
},
"id": "send-notification",
"name": "Send Admin Notification",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1760,
100
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.SUPABASE_URL }}/rest/v1/quantra_actions",
"authentication": "none",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "apikey",
"value": "={{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Authorization",
"value": "=Bearer {{ $env.SUPABASE_SERVICE_KEY }}"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"action_type\": \"report_generation\",\n \"action_category\": \"automation\",\n \"target_type\": \"report\",\n \"decision\": \"completed\",\n \"reasoning\": \"Generated weekly summary report for {{ $('Compile Weekly Report').first().json.period.start_date }} to {{ $('Compile Weekly Report').first().json.period.end_date }} - {{ $('Compile Weekly Report').first().json.time_tracking.total_hours }} hours, ${{ $('Compile Weekly Report').first().json.expenses.total_submitted }} expenses\",\n \"confidence_score\": 100,\n \"status\": \"completed\",\n \"executed_at\": \"{{ new Date().toISOString() }}\"\n}"
},
"id": "log-activity",
"name": "Log AI Activity",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1980,
100
]
}
],
"connections": {
"Every Monday at 7 AM": {
"main": [
[
{
"node": "Calculate Date Range",
"type": "main",
"index": 0
}
]
]
},
"Calculate Date Range": {
"main": [
[
{
"node": "Get Week's Time Entries",
"type": "main",
"index": 0
},
{
"node": "Get Week's Expenses",
"type": "main",
"index": 0
},
{
"node": "Get Week's Time Off",
"type": "main",
"index": 0
},
{
"node": "Get Week's Tasks",
"type": "main",
"index": 0
}
]
]
},
"Get Week's Time Entries": {
"main": [
[
{
"node": "Compile Weekly Report",
"type": "main",
"index": 0
}
]
]
},
"Get Week's Expenses": {
"main": [
[
{
"node": "Compile Weekly Report",
"type": "main",
"index": 0
}
]
]
},
"Get Week's Time Off": {
"main": [
[
{
"node": "Compile Weekly Report",
"type": "main",
"index": 0
}
]
]
},
"Get Week's Tasks": {
"main": [
[
{
"node": "Compile Weekly Report",
"type": "main",
"index": 0
}
]
]
},
"Compile Weekly Report": {
"main": [
[
{
"node": "Save Report to DB",
"type": "main",
"index": 0
}
]
]
},
"Save Report to DB": {
"main": [
[
{
"node": "Get Admin Users",
"type": "main",
"index": 0
}
]
]
},
"Get Admin Users": {
"main": [
[
{
"node": "Prepare Admin Notifications",
"type": "main",
"index": 0
}
]
]
},
"Prepare Admin Notifications": {
"main": [
[
{
"node": "Send Admin Notification",
"type": "main",
"index": 0
}
]
]
},
"Send Admin Notification": {
"main": [
[
{
"node": "Log AI Activity",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "quantra-core"
},
{
"name": "reports"
}
],
"triggerCount": 1
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Quantra - Weekly Reports Generator. Uses httpRequest. Scheduled trigger; 12 nodes.
Source: https://github.com/Weath123/Quantra-Autonomous-Worker/blob/c2ca42d25b36ab2ba894ff9f62cde2be4073bc95/n8n-workflows/16-weekly-reports.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.
As n8n instances scale, teams often lose track of sub-workflows—who uses them, where they are referenced, and whether they can be safely updated. This leads to inefficiencies like unnecessary copies o
This workflow is an improvement of this workflow by Greg Brzezinka.
N8N-Workflow-Github-Manager. Uses github, httpRequest, n8n. Scheduled trigger; 38 nodes.
This workflow uses KlickTipp community nodes, available for self-hosted n8n instances only.
This workflow acts as an automated engagement bot. It sends a Direct Message (DM) with a link or resource to any follower who replies to your post with a specific target keyword.