This workflow follows the HTTP Request → Supabase recipe pattern — see all workflows that pair these two integrations.
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": "AIPA - BMF Work Logging",
"nodes": [
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.message.text }}",
"operation": "startsWith",
"value2": "/bmf"
}
]
}
},
"id": "webhook-trigger",
"name": "BMF Command Trigger",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
250,
300
]
},
{
"parameters": {
"chatId": "={{ $json.message.chat.id }}",
"text": "\ud83d\udcbc **BMF Work Management**\\n\\nWhat would you like to do?\\n\\n1\ufe0f\u20e3 Log hours worked today\\n2\ufe0f\u20e3 View this week's hours\\n3\ufe0f\u20e3 View this month's hours\\n4\ufe0f\u20e3 Add project task\\n5\ufe0f\u20e3 View active projects\\n6\ufe0f\u20e3 Generate timesheet\\n7\ufe0f\u20e3 Submit invoice\\n\\nReply with a number.",
"additionalFields": {
"parse_mode": "Markdown",
"reply_markup": {
"keyboard": [
[
"1 - Log Hours",
"2 - This Week"
],
[
"3 - This Month",
"4 - Add Task"
],
[
"5 - Projects",
"6 - Timesheet"
],
[
"7 - Invoice"
]
],
"one_time_keyboard": true,
"resize_keyboard": true
}
}
},
"id": "show-bmf-menu",
"name": "Show BMF Menu",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
450,
300
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $node[\"BMF Command Trigger\"].json.message.chat.id }}",
"text": "\u23f1\ufe0f **Log Hours Worked**\\n\\nPlease provide:\\n\\n1. Hours worked (e.g., 7.5)\\n2. Project/task description\\n3. Date (YYYY-MM-DD) or 'today'\\n\\nExample:\\n7.5\\nWebsite maintenance and bug fixes\\ntoday",
"additionalFields": {}
},
"id": "ask-hours-details",
"name": "Ask for Hours Details",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
650,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"updates": [
"message"
]
},
"id": "wait-for-hours",
"name": "Wait for Hours Input",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1,
"position": [
850,
200
]
},
{
"parameters": {
"functionCode": "// Parse hours input\nconst input = items[0].json.message.text;\nconst lines = input.split('\\n').map(l => l.trim()).filter(l => l);\n\nif (lines.length < 2) {\n return [{\n json: {\n error: true,\n message: \"\u274c Please provide hours and description\"\n }\n }];\n}\n\nconst hours = parseFloat(lines[0]);\nconst description = lines[1];\nlet workDate = lines[2] || 'today';\n\n// Parse date\nif (workDate.toLowerCase() === 'today') {\n workDate = new Date().toISOString().split('T')[0];\n} else if (workDate.toLowerCase() === 'yesterday') {\n const yesterday = new Date();\n yesterday.setDate(yesterday.getDate() - 1);\n workDate = yesterday.toISOString().split('T')[0];\n}\n\n// BMF hourly rate (configure this)\nconst hourlyRate = 35; // \u00a335/hour\nconst earnings = hours * hourlyRate;\n\nreturn [{\n json: {\n error: false,\n hours_worked: hours,\n task_description: description,\n work_date: workDate,\n hourly_rate: hourlyRate,\n earnings: earnings,\n status: 'logged'\n }\n}];"
},
"id": "parse-hours",
"name": "Parse Hours Entry",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
1050,
200
]
},
{
"parameters": {
"operation": "insert",
"table": "bmf_work_log",
"columns": "work_date, hours_worked, task_description, hourly_rate, earnings, status",
"additionalFields": {}
},
"id": "save-hours-db",
"name": "Save Hours to Database",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1250,
200
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $node[\"Wait for Hours Input\"].json.message.chat.id }}",
"text": "=\u2705 **Hours Logged Successfully!**\\n\\n\ud83d\udcc5 Date: {{ $node[\"Parse Hours Entry\"].json.work_date }}\\n\u23f1\ufe0f Hours: {{ $node[\"Parse Hours Entry\"].json.hours_worked }}\\n\ud83d\udcb0 Earnings: \u00a3{{ $node[\"Parse Hours Entry\"].json.earnings }}\\n\ud83d\udcdd Task: {{ $node[\"Parse Hours Entry\"].json.task_description }}\\n\\nLogged to BMF work log.",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "confirm-hours-logged",
"name": "Confirm Hours Logged",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1450,
200
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT work_date, hours_worked, task_description, earnings FROM bmf_work_log WHERE work_date >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY) ORDER BY work_date DESC",
"additionalFields": {}
},
"id": "get-week-hours",
"name": "Get This Week's Hours",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
650,
300
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"functionCode": "// Format weekly hours\nconst entries = items;\n\nif (entries.length === 0) {\n return [{\n json: {\n message: \"\ud83d\udced No hours logged this week\"\n }\n }];\n}\n\nlet message = \"\ud83d\udcca **This Week's Hours - BMF**\\n\\n\";\nlet totalHours = 0;\nlet totalEarnings = 0;\n\nfor (const entry of entries) {\n const date = new Date(entry.json.work_date).toLocaleDateString('en-GB', { weekday: 'short', day: 'numeric', month: 'short' });\n const hours = parseFloat(entry.json.hours_worked || 0);\n const earnings = parseFloat(entry.json.earnings || 0);\n \n message += `**${date}**\\n`;\n message += `\u23f1\ufe0f ${hours} hours\\n`;\n message += `\ud83d\udcdd ${entry.json.task_description}\\n`;\n message += `\ud83d\udcb0 \u00a3${earnings.toFixed(2)}\\n`;\n message += `\\n`;\n \n totalHours += hours;\n totalEarnings += earnings;\n}\n\nmessage += `**WEEK TOTAL**\\n`;\nmessage += `\u23f1\ufe0f ${totalHours} hours\\n`;\nmessage += `\ud83d\udcb5 \u00a3${totalEarnings.toFixed(2)}`;\n\nreturn [{\n json: {\n message: message,\n total_hours: totalHours,\n total_earnings: totalEarnings\n }\n}];"
},
"id": "format-week-hours",
"name": "Format Week Hours",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
850,
300
]
},
{
"parameters": {
"chatId": "={{ $node[\"BMF Command Trigger\"].json.message.chat.id }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "send-week-hours",
"name": "Send Week Hours",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1050,
300
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT work_date, hours_worked, task_description, earnings FROM bmf_work_log WHERE work_date >= DATE_SUB(CURRENT_DATE, INTERVAL 30 DAY) ORDER BY work_date DESC",
"additionalFields": {}
},
"id": "get-month-hours",
"name": "Get This Month's Hours",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
650,
400
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"functionCode": "// Format monthly hours with weekly breakdown\nconst entries = items;\n\nif (entries.length === 0) {\n return [{\n json: {\n message: \"\ud83d\udced No hours logged this month\"\n }\n }];\n}\n\nlet message = \"\ud83d\udcca **This Month's Hours - BMF**\\n\\n\";\nlet totalHours = 0;\nlet totalEarnings = 0;\n\n// Group by week\nconst weeks = {};\n\nfor (const entry of entries) {\n const date = new Date(entry.json.work_date);\n const weekStart = new Date(date);\n weekStart.setDate(date.getDate() - date.getDay());\n const weekKey = weekStart.toISOString().split('T')[0];\n \n if (!weeks[weekKey]) {\n weeks[weekKey] = {\n hours: 0,\n earnings: 0,\n entries: []\n };\n }\n \n const hours = parseFloat(entry.json.hours_worked || 0);\n const earnings = parseFloat(entry.json.earnings || 0);\n \n weeks[weekKey].hours += hours;\n weeks[weekKey].earnings += earnings;\n weeks[weekKey].entries.push(entry.json);\n \n totalHours += hours;\n totalEarnings += earnings;\n}\n\n// Format output\nconst weekKeys = Object.keys(weeks).sort().reverse();\n\nfor (const weekKey of weekKeys) {\n const weekData = weeks[weekKey];\n const weekDate = new Date(weekKey).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' });\n \n message += `**Week of ${weekDate}**\\n`;\n message += `\u23f1\ufe0f ${weekData.hours} hours | \ud83d\udcb0 \u00a3${weekData.earnings.toFixed(2)}\\n`;\n message += `\\n`;\n}\n\nmessage += `\\n**MONTH TOTAL**\\n`;\nmessage += `\u23f1\ufe0f ${totalHours} hours\\n`;\nmessage += `\ud83d\udcb5 \u00a3${totalEarnings.toFixed(2)}\\n`;\nmessage += `\ud83d\udcca Avg per week: \u00a3${(totalEarnings / weekKeys.length).toFixed(2)}`;\n\nreturn [{\n json: {\n message: message,\n total_hours: totalHours,\n total_earnings: totalEarnings,\n weeks_worked: weekKeys.length\n }\n}];"
},
"id": "format-month-hours",
"name": "Format Month Hours",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
850,
400
]
},
{
"parameters": {
"chatId": "={{ $node[\"BMF Command Trigger\"].json.message.chat.id }}",
"text": "={{ $json.message }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "send-month-hours",
"name": "Send Month Hours",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1050,
400
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $node[\"BMF Command Trigger\"].json.message.chat.id }}",
"text": "\ud83d\udcc4 **Generate BMF Timesheet**\\n\\nPlease specify the period:\\n\\n1. Start date (YYYY-MM-DD)\\n2. End date (YYYY-MM-DD)\\n\\nExample:\\n2025-11-01\\n2025-11-30",
"additionalFields": {}
},
"id": "ask-timesheet-period",
"name": "Ask Timesheet Period",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
650,
500
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"updates": [
"message"
]
},
"id": "wait-for-timesheet-period",
"name": "Wait for Timesheet Period",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1,
"position": [
850,
500
]
},
{
"parameters": {
"functionCode": "// Parse timesheet period\nconst input = items[0].json.message.text;\nconst lines = input.split('\\n').map(l => l.trim()).filter(l => l);\n\nif (lines.length < 2) {\n return [{\n json: {\n error: true,\n message: \"\u274c Please provide start and end dates\"\n }\n }];\n}\n\nreturn [{\n json: {\n error: false,\n start_date: lines[0],\n end_date: lines[1]\n }\n}];"
},
"id": "parse-timesheet-period",
"name": "Parse Timesheet Period",
"type": "n8n-nodes-base.function",
"typeVersion": 1,
"position": [
1050,
500
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "=SELECT * FROM bmf_work_log WHERE work_date BETWEEN '{{ $json.start_date }}' AND '{{ $json.end_date }}' ORDER BY work_date ASC",
"additionalFields": {}
},
"id": "get-timesheet-data",
"name": "Get Timesheet Data",
"type": "n8n-nodes-base.supabase",
"typeVersion": 1,
"position": [
1250,
500
],
"credentials": {
"supabaseApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"authentication": "headerAuth",
"requestMethod": "POST",
"url": "https://api.anthropic.com/v1/messages",
"options": {},
"headerParametersJson": "={\n \"x-api-key\": \"{{$credentials.anthropicApi.apiKey}}\",\n \"anthropic-version\": \"2023-06-01\",\n \"content-type\": \"application/json\"\n}",
"bodyParametersJson": "={\n \"model\": \"claude-3-5-sonnet-20241022\",\n \"max_tokens\": 2048,\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"Create a professional timesheet document for BMF work.\\n\\nPeriod: {{ $node[\"Parse Timesheet Period\"].json.start_date }} to {{ $node[\"Parse Timesheet Period\"].json.end_date }}\\n\\nWork Log:\\n{{ JSON.stringify($json) }}\\n\\nFormat as a professional timesheet with:\\n- Header with period\\n- Table of dates, hours, tasks\\n- Daily subtotals\\n- Weekly subtotals\\n- Total hours and earnings\\n- Professional formatting\\n\\nReturn as formatted text suitable for email or PDF.\"\n }\n ]\n}"
},
"id": "generate-timesheet",
"name": "Generate Timesheet (Claude)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 3,
"position": [
1450,
500
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"chatId": "={{ $node[\"Wait for Timesheet Period\"].json.message.chat.id }}",
"text": "=\ud83d\udcc4 **BMF Timesheet Generated**\\n\\n{{ $json.content[0].text }}",
"additionalFields": {
"parse_mode": "Markdown"
}
},
"id": "send-timesheet",
"name": "Send Timesheet",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1,
"position": [
1650,
500
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
}
],
"connections": {
"BMF Command Trigger": {
"main": [
[
{
"node": "Show BMF Menu",
"type": "main",
"index": 0
}
]
]
},
"Show BMF Menu": {
"main": [
[
{
"node": "Ask for Hours Details",
"type": "main",
"index": 0
},
{
"node": "Get This Week's Hours",
"type": "main",
"index": 0
},
{
"node": "Get This Month's Hours",
"type": "main",
"index": 0
},
{
"node": "Ask Timesheet Period",
"type": "main",
"index": 0
}
]
]
},
"Ask for Hours Details": {
"main": [
[
{
"node": "Wait for Hours Input",
"type": "main",
"index": 0
}
]
]
},
"Wait for Hours Input": {
"main": [
[
{
"node": "Parse Hours Entry",
"type": "main",
"index": 0
}
]
]
},
"Parse Hours Entry": {
"main": [
[
{
"node": "Save Hours to Database",
"type": "main",
"index": 0
}
]
]
},
"Save Hours to Database": {
"main": [
[
{
"node": "Confirm Hours Logged",
"type": "main",
"index": 0
}
]
]
},
"Get This Week's Hours": {
"main": [
[
{
"node": "Format Week Hours",
"type": "main",
"index": 0
}
]
]
},
"Format Week Hours": {
"main": [
[
{
"node": "Send Week Hours",
"type": "main",
"index": 0
}
]
]
},
"Get This Month's Hours": {
"main": [
[
{
"node": "Format Month Hours",
"type": "main",
"index": 0
}
]
]
},
"Format Month Hours": {
"main": [
[
{
"node": "Send Month Hours",
"type": "main",
"index": 0
}
]
]
},
"Ask Timesheet Period": {
"main": [
[
{
"node": "Wait for Timesheet Period",
"type": "main",
"index": 0
}
]
]
},
"Wait for Timesheet Period": {
"main": [
[
{
"node": "Parse Timesheet Period",
"type": "main",
"index": 0
}
]
]
},
"Parse Timesheet Period": {
"main": [
[
{
"node": "Get Timesheet Data",
"type": "main",
"index": 0
}
]
]
},
"Get Timesheet Data": {
"main": [
[
{
"node": "Generate Timesheet (Claude)",
"type": "main",
"index": 0
}
]
]
},
"Generate Timesheet (Claude)": {
"main": [
[
{
"node": "Send Timesheet",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-11-01T18:00:00.000Z",
"versionId": "1"
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
httpHeaderAuthsupabaseApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
AIPA - BMF Work Logging. Uses telegram, telegramTrigger, supabase, httpRequest. Webhook trigger; 19 nodes.
Source: https://github.com/DirtyDerv/AIPA/blob/defb52161b0d387cd66b2a6d3bbb1e71b4afc6ae/n8n-workflows/05-bmf-work-logging.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.
Telegram-auth. Uses telegramTrigger, telegram, supabase, httpRequest. Event-driven trigger; 18 nodes.
Http Telegram. Uses telegramTrigger, httpRequest, supabase, telegram. Event-driven trigger; 17 nodes.
Http Telegram. Uses telegramTrigger, httpRequest, supabase, telegram. Event-driven trigger; 17 nodes.
N8N Complete Final. Uses telegramTrigger, dataTable, telegram, mqtt. Event-driven trigger; 58 nodes.
TextMain. Uses telegramTrigger, stopAndError, telegram, httpRequest. Event-driven trigger; 56 nodes.