What it does
Miniflux gathers your curated security feeds, but raw feeds are noisy. This workflow puts an intelligent filter on top: a cheap model screens for relevance, and only what passes goes on to more expensive enrichment. The result is a low-noise flow of relevant, summarized security news.
Flow overview
Two-step screening: a cheap model filters out, an expensive model enriches only what is relevant.
(1t)
Node-by-node
| # | Node | Type | Function |
|---|---|---|---|
| 1 | Schedule Trigger | scheduleTrigger | Runs every hour (adjust the interval to feed volume) |
| 2 | Get Unread | httpRequest | GET /v1/entries?status=unread with an X-Auth-Token header |
| 3 | Split Entries | splitOut | Splits the entries array into one item per article |
| 4 | Screen | mistralAi (Small) | Cheap relevance assessment, replies with structured JSON |
| 5 | Relevant? | if | Parses the screening JSON and branches on relevant |
| 6 | Enrich | mistralAi (Large) | Summarizes relevant articles into an intel summary + IOCs |
| 7 | Mark Read | httpRequest | PUT /v1/entries sets status read |
Importable JSON
n8n importCopy the below and paste into n8n via Workflows → ⋯ → Import from clipboard. Replace the credential references and prompts with your own.
{
"name": "Miniflux News Screening",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 1
}
]
}
},
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
240,
300
]
},
{
"parameters": {
"url": "=https://rss.defencia.dk/v1/entries?status=unread&limit=100&direction=asc",
"options": {
"response": {
"response": {
"fullResponse": false
}
}
},
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-Auth-Token",
"value": "={{ $credentials.minifluxToken }}"
}
]
}
},
"name": "Get Unread (Miniflux)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
460,
300
]
},
{
"parameters": {
"fieldToSplitOut": "entries",
"options": {}
},
"name": "Split Entries",
"type": "n8n-nodes-base.splitOut",
"typeVersion": 1,
"position": [
680,
300
]
},
{
"parameters": {
"modelId": "mistral-small-latest",
"messages": {
"values": [
{
"role": "system",
"content": "You are a security analyst. Assess whether the article is relevant for threat monitoring. Reply ONLY with JSON: {\"relevant\": true/false, \"category\": \"...\", \"reasoning\": \"...\"}"
},
{
"role": "user",
"content": "=Title: {{ $json.title }}\nContent: {{ $json.content }}"
}
]
}
},
"name": "Screen (Mistral Small)",
"type": "n8n-nodes-base.mistralAi",
"typeVersion": 1,
"position": [
900,
300
]
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true
},
"conditions": [
{
"leftValue": "={{ JSON.parse($json.choices[0].message.content).relevant }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "true"
}
}
]
}
},
"name": "Relevant?",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [
1120,
300
]
},
{
"parameters": {
"modelId": "mistral-large-latest",
"messages": {
"values": [
{
"role": "system",
"content": "Summarize the article into a short threat-intel summary with IOCs if present."
},
{
"role": "user",
"content": "={{ $json.title }}\n{{ $json.content }}"
}
]
}
},
"name": "Enrich (Mistral Large)",
"type": "n8n-nodes-base.mistralAi",
"typeVersion": 1,
"position": [
1340,
220
]
},
{
"parameters": {
"method": "PUT",
"url": "=https://rss.defencia.dk/v1/entries",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "X-Auth-Token",
"value": "={{ $credentials.minifluxToken }}"
}
]
},
"sendBody": true,
"jsonBody": "={ \"entry_ids\": [ {{ $json.id }} ], \"status\": \"read\" }"
},
"name": "Mark Read (Miniflux)",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1560,
300
]
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Get Unread (Miniflux)",
"type": "main",
"index": 0
}
]
]
},
"Get Unread (Miniflux)": {
"main": [
[
{
"node": "Split Entries",
"type": "main",
"index": 0
}
]
]
},
"Split Entries": {
"main": [
[
{
"node": "Screen (Mistral Small)",
"type": "main",
"index": 0
}
]
]
},
"Screen (Mistral Small)": {
"main": [
[
{
"node": "Relevant?",
"type": "main",
"index": 0
}
]
]
},
"Relevant?": {
"main": [
[
{
"node": "Enrich (Mistral Large)",
"type": "main",
"index": 0
}
],
[
{
"node": "Mark Read (Miniflux)",
"type": "main",
"index": 0
}
]
]
},
"Enrich (Mistral Large)": {
"main": [
[
{
"node": "Mark Read (Miniflux)",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
rss.defencia.dk. The embedded token reference is a placeholder — bind it to a real n8n credential, never put it in cleartext.Credentials
| Credential | Setup |
|---|---|
| Miniflux token | Generate in Miniflux (Settings → API Keys). Store as an n8n header-auth credential or env variable — not inline in the node |
| Mistral API | n8n's Mistral credential with your API key; used by both the Screen and Enrich nodes |
Tuning & extensions
| Adjustment | Effect |
|---|---|
| Interval | Lower = fresher news, but more API calls. 1h is a good balance for ~71 feeds |
| limit | Raise if feeds pile up between runs; watch out for Mistral rate limits |
| Batching | Screen multiple articles in one Mistral call to save tokens (requires an array prompt) |
| Output | Add a node after Enrich: send to the dashboard, Slack, email or a database table |
| IOC extraction | Add a regex/Code node that extracts IPs, domains, hashes from the summary |
| Dedup | Miniflux's read status is your dedup — that's why everything is marked read in both branches |