This workflow follows the Chainllm → Google Sheets 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": "daily-briefing-local-agentic-poc",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 0 8 * * *"
}
]
}
},
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [
-1328,
-16
],
"id": "17edaa23-9a00-4683-a259-cfa29b1270c6"
},
{
"parameters": {
"url": "=https://api.github.com/search/issues?q=repo:{{$env.GITHUB_REPO}}+created:>={{ $now.minus({ hours: Number($env.GITHUB_LOOKBACK_HOURS || 24) }).toUTC().toISO() }}",
"options": {
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"name": "HTTP Request",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
-1104,
-16
],
"id": "a43cfd3e-71b5-4642-a2fc-10ed3fdd6691",
"retryOnFail": true,
"notesInFlow": false
},
{
"parameters": {
"fieldToSplitOut": "items",
"options": {}
},
"name": "Split GitHub Items",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
-880,
-16
],
"id": "10fa8ae0-2c2e-45d1-b80e-8d8d0025610a"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "e602b91b-d33c-4cee-9b9b-19e5be20ef74",
"name": "id",
"value": "={{ $json.id }}",
"type": "number"
},
{
"id": "31d57802-ae38-4a70-a9b5-699d31f6d7dd",
"name": "number",
"value": "={{ $json.number }}",
"type": "number"
},
{
"id": "1b686199-8315-4959-974c-3c661661c31e",
"name": "title",
"value": "={{ $json.title }}",
"type": "string"
},
{
"id": "0cd3f7b2-4730-49bc-b924-983e3fa52c10",
"name": "body",
"value": "={{\n(() => {\n const raw = $json.body ?? '';\n\n const cleaned = raw\n .replace(/<br\\s*\\/?>/gi, '\\n')\n .replace(/<\\/p>/gi, '\\n\\n')\n .replace(/<\\/div>/gi, '\\n')\n .replace(/<\\/li>/gi, '\\n')\n .replace(/<li[^>]*>/gi, '- ')\n .replace(/<[^>]+>/g, ' ')\n .replace(/!\\[[^\\]]*\\]\\([^)]+\\)/g, ' ')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/^#{1,6}\\s+/gm, '')\n .replace(/^\\s*[-*+]\\s+/gm, '- ')\n .replace(/^\\s*\\d+\\.\\s+/gm, '- ')\n .replace(/[`*_~]/g, '')\n .replace(/\\r/g, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n[ \\t]+/g, '\\n')\n .replace(/[ \\t]{2,}/g, ' ')\n .trim();\n\n return cleaned.length > 800 ? cleaned.slice(0, 800) + '\\n...' : cleaned;\n})()\n}}",
"type": "string"
},
{
"id": "7f4bc857-e2e7-426c-b80a-8d6f85a17216",
"name": "labels",
"value": "={{ ($json.labels ?? []).map((label) => label.name ?? '').filter((label) => label) }}",
"type": "array"
},
{
"id": "11129f5f-2afa-4a6b-9f7d-5ad6ccb37508",
"name": "state",
"value": "={{ $json.state }}",
"type": "string"
},
{
"id": "a4f6ad54-3cef-4eef-b34c-c2e4aa31efab",
"name": "html_url",
"value": "={{ $json.html_url }}",
"type": "string"
},
{
"id": "6469a87e-8220-4e28-9a7b-d5f9d47c7b4f",
"name": "isCriticalDeterministic",
"value": "={{\n(() => {\n const labels = ($json.labels ?? []).map((label) =>\n (typeof label === 'string' ? label : (label.name ?? '')).toLowerCase(),\n );\n\n const text = [\n $json.title ?? '',\n $json.body ?? '',\n ...labels,\n ].join('\\n').toLowerCase();\n\n const impactTerms = [\n 'oom',\n 'memory leak',\n 'hang',\n 'timeout',\n 'event loop',\n 'crash',\n 'data loss',\n 'outage',\n 'request hung',\n 'blocks the event loop',\n ];\n\n const evidenceTerms = [\n 'production',\n 'confirmed cause',\n 'reproduced pre-fix',\n 'flat rss',\n 'ooms',\n 'tens of seconds',\n ];\n\n const backportLabels = [\n 'backport to beta',\n 'backport to stable',\n 'backport to v1',\n ];\n\n const hasImpact = impactTerms.some((term) => text.includes(term));\n const hasEvidence = evidenceTerms.some((term) => text.includes(term));\n const hasBackport = labels.some((label) => backportLabels.includes(label));\n\n return hasImpact || hasEvidence || hasBackport;\n})()\n}}",
"type": "boolean"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-656,
-16
],
"id": "e157a4a6-b8bf-404e-a80d-58f1da6b4895",
"name": "Edit Fields"
},
{
"parameters": {
"promptType": "define",
"text": "=You classify GitHub issues and pull requests for production criticality.\\nReturn only valid JSON with this schema:\\n{\\\"isCritical\\\": boolean, \\\"criticalityReason\\\": string}\\n\\nCritical means immediate operational risk: outage, crash, data loss, major regression, severe performance degradation, or urgent backport-worthy stability impact.\\nNon-critical means routine bug, docs, enhancement, cosmetic issue, or low urgency.\\n\\nIssue input:\\nNumber: {{$json.number}}\\nTitle: {{$json.title}}\\nState: {{$json.state}}\\nLabels: {{ JSON.stringify($json.labels ?? []) }}\\nBody: {{$json.body}}",
"hasOutputParser": true,
"batching": {}
},
"type": "@n8n/n8n-nodes-langchain.chainLlm",
"typeVersion": 1.7,
"position": [
-432,
-16
],
"id": "9d6d18f1-7bc1-4981-9b36-facaa56b7705",
"name": "Issue Criticality LLM",
"retryOnFail": true,
"onError": "continueRegularOutput"
},
{
"parameters": {
"model": "={{ $env.AZURE_OPENAI_MODEL || 'gpt-5.4-nano' }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatAzureOpenAi",
"typeVersion": 1,
"position": [
-480,
208
],
"id": "f9a08e3f-e48f-4a91-aeef-b7025fc7a0f1",
"name": "Azure OpenAI Chat Model",
"credentials": {
"azureOpenAiApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsonSchemaExample": "{\n \"isCritical\": false,\n \"criticalityReason\": \"Feature enhancement without immediate production impact.\"\n}"
},
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"typeVersion": 1.2,
"position": [
-288,
208
],
"id": "19b7b3e3-5154-4f4b-aeda-e59d8e137d37",
"name": "Criticality Output Parser"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "0fccf8ce-6e3e-4f70-b8ee-652e714a8894",
"name": "id",
"value": "={{ $('Edit Fields').item.json.id }}",
"type": "number"
},
{
"id": "321ab5eb-2f71-4a5c-9fbe-9f857f91e8da",
"name": "number",
"value": "={{ $('Edit Fields').item.json.number }}",
"type": "number"
},
{
"id": "58a2da2e-3a14-46ea-9a9c-21101dd2dcf2",
"name": "title",
"value": "={{ $('Edit Fields').item.json.title }}",
"type": "string"
},
{
"id": "2f4ac5ee-9fad-4712-9f4f-6b7bc56acb5d",
"name": "body",
"value": "={{ $('Edit Fields').item.json.body }}",
"type": "string"
},
{
"id": "6f3d3f2f-3d98-4f7f-9e1c-b7566c947930",
"name": "labels",
"value": "={{ $('Edit Fields').item.json.labels ?? [] }}",
"type": "array"
},
{
"id": "24e1e973-57b6-43ec-8a3f-a739d893b78b",
"name": "state",
"value": "={{ $('Edit Fields').item.json.state }}",
"type": "string"
},
{
"id": "4f0d401a-8d56-4534-9f6f-d6af557f8a77",
"name": "html_url",
"value": "={{ $('Edit Fields').item.json.html_url }}",
"type": "string"
},
{
"id": "338fbb7c-2ee4-4420-beca-9f4c3c57f50d",
"name": "isCriticalDeterministic",
"value": "={{ Boolean($('Edit Fields').item.json.isCriticalDeterministic) }}",
"type": "boolean"
},
{
"id": "daafe338-f9e9-4fc4-8f00-9f8fdd9ea2ad",
"name": "isCriticalLLM",
"value": "={{\n(() => {\n let parsed = $json.output ?? $json.text ?? $json.response ?? $json;\n\n if (typeof parsed === 'string') {\n try {\n parsed = JSON.parse(parsed);\n } catch (error) {\n return false;\n }\n }\n\n if (parsed && typeof parsed === 'object' && typeof parsed.output === 'string') {\n try {\n parsed = JSON.parse(parsed.output);\n } catch (error) {\n return false;\n }\n }\n\n return Boolean(parsed?.isCritical);\n})()\n}}",
"type": "boolean"
},
{
"id": "1389abeb-d8c3-45f9-a9d2-9766d6788af8",
"name": "criticalityReason",
"value": "={{\n(() => {\n let parsed = $json.output ?? $json.text ?? $json.response ?? $json;\n\n if (typeof parsed === 'string') {\n try {\n parsed = JSON.parse(parsed);\n } catch (error) {\n return '';\n }\n }\n\n if (parsed && typeof parsed === 'object' && typeof parsed.output === 'string') {\n try {\n parsed = JSON.parse(parsed.output);\n } catch (error) {\n return '';\n }\n }\n\n const reason = parsed?.criticalityReason ?? '';\n return typeof reason === 'string' ? reason : '';\n})()\n}}",
"type": "string"
},
{
"id": "45b5f61c-5af2-4d4d-a851-d86eaecf01ae",
"name": "isCritical",
"value": "={{\n(() => {\n let parsed = $json.output ?? $json.text ?? $json.response ?? $json;\n\n if (typeof parsed === 'string') {\n try {\n parsed = JSON.parse(parsed);\n } catch (error) {\n return false;\n }\n }\n\n if (parsed && typeof parsed === 'object' && typeof parsed.output === 'string') {\n try {\n parsed = JSON.parse(parsed.output);\n } catch (error) {\n return false;\n }\n }\n\n return Boolean(parsed?.isCritical);\n})()\n}}",
"type": "boolean"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-80,
-16
],
"id": "07cc64f3-fb23-4eab-b2c9-ba7c80de65e4",
"name": "Finalize Criticality",
"notesInFlow": true,
"notes": "Per-item criticality assignment: LLM produces isCritical/criticalityReason for each GitHub issue item, while isCriticalDeterministic is preserved as a separate signal for auditing."
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 1
},
"conditions": [
{
"id": "a938f618-f1d6-4ad7-a1ee-dbb174af9525",
"leftValue": "={{ $json.isCritical }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"name": "IF Critical",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
144,
-16
],
"id": "3fdc536e-af66-46aa-87c6-1f9d5cc0ba45"
},
{
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"value": "={{ $env.SLACK_CRITICAL_CHANNEL }}",
"mode": "name"
},
"text": "=:rotating_light: *Critical GitHub item*\n*<{{$json.url}}|{{$json.title}}>*\n*Criticality reason:* {{$json.criticalityReason || 'Deterministic fallback matched critical criteria.'}}\n\n*Body:*\n{{$json.body}}",
"otherOptions": {}
},
"name": "Send Slack",
"type": "n8n-nodes-base.slack",
"typeVersion": 2,
"position": [
368,
-112
],
"id": "5bc14dc9-50ad-4b91-ab10-12ed397d6275",
"retryOnFail": true,
"credentials": {
"slackApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "={{ $env.GOOGLE_SHEETS_DOCUMENT_ID }}",
"mode": "id"
},
"sheetName": {
"__rl": true,
"value": "={{ $env.GOOGLE_SHEETS_MESSAGES_SHEET }}",
"mode": "name",
"cachedResultName": "={{ $env.GOOGLE_SHEETS_MESSAGES_SHEET || 'Messages' }}"
},
"columns": {
"mappingMode": "autoMapInputData",
"value": {},
"matchingColumns": [
"id"
],
"schema": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"name": "Log to Google Sheets",
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4,
"position": [
368,
80
],
"id": "0157f745-990a-4a13-843e-ddc61a682ea1",
"retryOnFail": true,
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"HTTP Request": {
"main": [
[
{
"node": "Split GitHub Items",
"type": "main",
"index": 0
}
]
]
},
"Split GitHub Items": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Issue Criticality LLM",
"type": "main",
"index": 0
}
]
]
},
"Issue Criticality LLM": {
"main": [
[
{
"node": "Finalize Criticality",
"type": "main",
"index": 0
}
]
]
},
"Azure OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "Issue Criticality LLM",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Criticality Output Parser": {
"ai_outputParser": [
[
{
"node": "Issue Criticality LLM",
"type": "ai_outputParser",
"index": 0
}
]
]
},
"Finalize Criticality": {
"main": [
[
{
"node": "IF Critical",
"type": "main",
"index": 0
}
]
]
},
"IF Critical": {
"main": [
[
{
"node": "Send Slack",
"type": "main",
"index": 0
}
],
[
{
"node": "Log to Google Sheets",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate",
"availableInMCP": false
},
"versionId": "a75663df-dba5-475f-8b4c-708a0455488f",
"nodeGroups": [],
"id": "S1Dh9kfw510lv4eL",
"tags": []
}
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.
azureOpenAiApigoogleSheetsOAuth2ApislackApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
daily-briefing-local-agentic-poc. Uses httpRequest, chainLlm, lmChatAzureOpenAi, outputParserStructured. Scheduled trigger; 11 nodes.
Source: https://github.com/DevPartner/daily-briefing-poc/blob/main/daily-briefing-local-agentic-poc.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.
YogiAI. Uses lmChatAzureOpenAi, googleSheets, outputParserAutofixing, outputParserStructured. Scheduled trigger; 31 nodes.
The YogiAI workflow automates sending daily yoga pose reminders and related information via Line Push Messages . This automation leverages data from a Google Sheets database containing yoga pose detai
Description
Cyber Zap Public. Uses httpRequest, chainLlm, outputParserStructured, telegram. Scheduled trigger; 69 nodes.
Created by: Peyton Leveillee Last updated: October 2025