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": "Daily Reporter",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 10 * * 1-5"
}
]
}
},
"id": "schedule",
"name": "10:00 Weekdays",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
240,
300
]
},
{
"parameters": {
"method": "GET",
"url": "=https://__GITLAB_HOST__/api/v4/projects/{{ encodeURIComponent('__GITLAB_PROJECT__') }}/merge_requests",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "state",
"value": "all"
},
{
"name": "updated_after",
"value": "={{ $now.weekday === 1 ? $now.minus({days: 3}).startOf('day').toISO() : $now.minus({days: 1}).startOf('day').toISO() }}"
},
{
"name": "per_page",
"value": "50"
}
]
},
"options": {
"timeout": 30000
}
},
"id": "get-mrs",
"name": "Get MRs",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
420,
300
],
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
},
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const mrs = $('Get MRs').all().map(i => i.json);\n\nconst mrsMerged = mrs.filter(m => m.state === 'merged').map(m => ({ iid: m.iid, title: m.title, author: m.author?.username }));\nconst mrsOpened = mrs.filter(m => m.state === 'opened').map(m => ({ iid: m.iid, title: m.title, author: m.author?.username }));\n\n// Group by author\nconst mergedByAuthor = {};\nconst openedByAuthor = {};\nfor (const m of mrsMerged) {\n mergedByAuthor[m.author] = (mergedByAuthor[m.author] || 0) + 1;\n}\nfor (const m of mrsOpened) {\n openedByAuthor[m.author] = (openedByAuthor[m.author] || 0) + 1;\n}\n\nreturn {\n json: {\n mrs_merged: mrsMerged,\n mrs_opened: mrsOpened,\n merged_by_author: mergedByAuthor,\n opened_by_author: openedByAuthor,\n total_merged: mrsMerged.length,\n total_opened: mrsOpened.length,\n collected_at: new Date().toISOString()\n }\n};"
},
"id": "aggregate-gitlab",
"name": "Aggregate GitLab",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
600,
300
]
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.N8N_API_URL }}/v1/projects/system/chat",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ user_message: 'Generate daily scrum report. Date: ' + $now.toFormat('yyyy-MM-dd (EEE)'), agent: 'Daily Reporter', source: 'n8n-scheduler', requester: 'daily-report-workflow', metadata: { gitlab_data: $json, workflow_execution_id: $executionId } }) }}",
"options": {
"timeout": 660000
}
},
"id": "execute",
"name": "Execute",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
780,
300
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.status }}",
"rightValue": "completed",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "check-success",
"name": "Success?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
960,
300
]
},
{
"parameters": {
"jsCode": "const result = $('Execute').item.json;\n\n// structured_output\uc774 \ubb38\uc790\uc5f4\uc774\uba74 \ud30c\uc2f1\nlet output = result.structured_output || {};\nif (typeof output === 'string') {\n try { output = JSON.parse(output); } catch(e) { output = {}; }\n}\n\n// AI\uac00 Slack mrkdwn\uc73c\ub85c \uc9c1\uc811 \uc0dd\uc131\ud55c \uba54\uc2dc\uc9c0 \uc0ac\uc6a9\nconst summaryMessage = output.summary_message || result.response || ':warning: \ub9ac\ud3ec\ud2b8 \uc0dd\uc131 \uc2e4\ud328';\nconst threadMessages = output.thread_messages || [];\n\nreturn { json: { text: summaryMessage, thread_messages: threadMessages, execution_id: result.id } };"
},
"id": "format-summary",
"name": "Format Summary",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1140,
200
]
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "__DAILY_REPORT_CHANNEL__"
},
"text": "={{ $json.text }}",
"otherOptions": {
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"id": "post-summary",
"name": "Post Summary",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1320,
200
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const threadMessages = $('Format Summary').item.json.thread_messages || [];\n\n// Slack \ub178\ub4dc \uc751\ub2f5\uc5d0\uc11c ts \ucd94\ucd9c (\uc5ec\ub7ec \uac00\ub2a5\ud55c \uacbd\ub85c \ud655\uc778)\nconst postSummaryOutput = $('Post Summary').item.json;\nconst summaryTs = postSummaryOutput.ts || postSummaryOutput.message?.ts || '';\n\n// \ub514\ubc84\uadf8: ts\uac00 \uc5c6\uc73c\uba74 \uc5d0\ub7ec \ubc29\uc9c0\nif (!summaryTs) {\n console.log('WARNING: No thread_ts found. Post Summary output:', JSON.stringify(postSummaryOutput));\n}\n\nconst items = threadMessages.map(t => ({\n json: {\n text: t.message,\n thread_ts: summaryTs,\n assignee: t.assignee\n }\n}));\n\nreturn items;"
},
"id": "format-threads",
"name": "Format Threads",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1500,
200
]
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "__DAILY_REPORT_CHANNEL__"
},
"text": "={{ $json.text }}",
"otherOptions": {
"thread_ts": {
"replyValues": {
"thread_ts": "={{ $json.thread_ts }}"
}
},
"unfurl_links": false,
"unfurl_media": false,
"includeLinkToWorkflow": false
}
},
"id": "post-threads",
"name": "Post Threads",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1680,
200
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.N8N_API_URL }}/v1/workflows/stats",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ workflow: 'daily-report', execution_id: $('Format Summary').item.json.execution_id, status: 'success', metadata: { thread_count: $('Format Summary').item.json.thread_messages?.length || 0 } }) }}",
"options": {
"timeout": 5000
}
},
"id": "stats-ok",
"name": "Stats OK",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1860,
200
],
"onError": "continueRegularOutput"
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "__DAILY_REPORT_CHANNEL__"
},
"text": "={{ `:x: *Daily Reporter Failed*\\n${$('Execute').item.json.status === 'timeout' ? ':hourglass: Timeout' : ':warning: ' + ($('Execute').item.json.error?.message || 'Unknown error')}` }}",
"otherOptions": {
"includeLinkToWorkflow": false
}
},
"id": "notify-failure",
"name": "Notify Failure",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.2,
"position": [
1140,
400
],
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"method": "POST",
"url": "={{ $env.N8N_API_URL }}/v1/workflows/stats",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ workflow: 'daily-report', execution_id: $('Execute').item.json.id, status: $('Execute').item.json.status === 'timeout' ? 'timeout' : 'error', metadata: { error: $('Execute').item.json.error?.message } }) }}",
"options": {
"timeout": 5000
}
},
"id": "stats-err",
"name": "Stats Err",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1320,
400
],
"onError": "continueRegularOutput"
}
],
"connections": {
"10:00 Weekdays": {
"main": [
[
{
"node": "Get MRs",
"type": "main",
"index": 0
}
]
]
},
"Get MRs": {
"main": [
[
{
"node": "Aggregate GitLab",
"type": "main",
"index": 0
}
]
]
},
"Aggregate GitLab": {
"main": [
[
{
"node": "Execute",
"type": "main",
"index": 0
}
]
]
},
"Execute": {
"main": [
[
{
"node": "Success?",
"type": "main",
"index": 0
}
]
]
},
"Success?": {
"main": [
[
{
"node": "Format Summary",
"type": "main",
"index": 0
}
],
[
{
"node": "Notify Failure",
"type": "main",
"index": 0
}
]
]
},
"Format Summary": {
"main": [
[
{
"node": "Post Summary",
"type": "main",
"index": 0
}
]
]
},
"Post Summary": {
"main": [
[
{
"node": "Format Threads",
"type": "main",
"index": 0
}
]
]
},
"Format Threads": {
"main": [
[
{
"node": "Post Threads",
"type": "main",
"index": 0
}
]
]
},
"Post Threads": {
"main": [
[
{
"node": "Stats OK",
"type": "main",
"index": 0
}
]
]
},
"Notify Failure": {
"main": [
[
{
"node": "Stats Err",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
}
}
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.
httpHeaderAuthslackApi
About this workflow
Daily Reporter. Uses scheduleTrigger, httpRequest, slack. Scheduled trigger; 12 nodes.
Source: https://github.com/junyeong-ai/claudio/blob/5899280b27ce362463a092893adca6f22728dc55/n8n-workflows/daily-report.json — original creator credit. Request a take-down →