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 →
{
"id": "wf002-topic-scoring-000000000",
"name": "WF-002-topic-scoring",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "*/30 * * * *"
}
]
}
},
"id": "c3000000-0002-0002-0002-000000000001",
"name": "Schedule 30min",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
240,
500
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT id, title, description, source, source_url FROM topics WHERE status = 'discovered' ORDER BY created_at ASC LIMIT 10",
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000002",
"name": "Poll Discovered Topics",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
460,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"id": "c3000000-0002-0002-0002-000000000030",
"leftValue": "={{ $json.id }}",
"rightValue": 0,
"operator": {
"type": "number",
"operation": "gt"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000003",
"name": "Has Topics?",
"type": "n8n-nodes-base.filter",
"typeVersion": 2.2,
"position": [
680,
500
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "={{ `SELECT pv.system_prompt, pv.user_prompt_template, pv.model_params FROM prompt_versions pv JOIN prompts p ON p.id = pv.prompt_id WHERE p.name = 'research_score' AND pv.version = p.current_version AND p.is_active = true LIMIT 1` }}",
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000005",
"name": "Load Prompt",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
1120,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT auto_approve_threshold FROM brand_profiles WHERE channel_id = 1 LIMIT 1",
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000011",
"name": "Load Threshold",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
1230,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const topic = $('Poll Discovered Topics').item.json;\nconst prompt = $('Load Prompt').item.json;\n\nconst userPrompt = (prompt.user_prompt_template || '')\n .replace('{{ topic_title }}', topic.title || '')\n .replace('{{ topic_description }}', topic.description || topic.title || '')\n .replace('{{ topic_source }}', topic.source || '');\n\nreturn [{\n json: {\n topic_id: topic.id,\n topic_title: topic.title,\n system_prompt: prompt.system_prompt,\n user_prompt: userPrompt,\n task_type: 'research_score',\n output_format: 'json',\n workflow_name: 'WF-002-topic-scoring'\n }\n}];"
},
"id": "c3000000-0002-0002-0002-000000000006",
"name": "Build Prompt",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1340,
500
]
},
{
"parameters": {
"workflowId": {
"__rl": true,
"value": "2673a82a-e155-4d0c-85b4-92922b74849d",
"mode": "id"
},
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000007",
"name": "Call SUB-001",
"type": "n8n-nodes-base.executeWorkflow",
"typeVersion": 1.1,
"position": [
1560,
500
]
},
{
"parameters": {
"jsCode": "const raw = $input.item.json;\nconst topicId = $('Build Prompt').item.json.topic_id;\nconst topicTitle = $('Build Prompt').item.json.topic_title;\nconst threshold = parseFloat($('Load Threshold').item.json.auto_approve_threshold) || 7.5;\n\nlet parsed = {};\ntry {\n if (raw.response_json && typeof raw.response_json === 'object' && raw.response_json.value_score !== undefined) {\n parsed = raw.response_json;\n } else {\n const text = raw.response_text || raw.response_json || '';\n const match = String(text).match(/\\{[\\s\\S]*\\}/);\n if (match) parsed = JSON.parse(match[0]);\n }\n} catch(e) {\n parsed = { value_score: 0, evergreen_score: 0, cpm_tier: 'low', reason: 'parse error: ' + e.message };\n}\n\nconst score = parseFloat(parsed.value_score) || 0;\nreturn [{\n json: {\n topic_id: topicId,\n topic_title: topicTitle,\n value_score: score,\n evergreen_score: parseFloat(parsed.evergreen_score) || 0,\n cpm_tier: parsed.cpm_tier || 'low',\n reason: parsed.reason || '',\n threshold: threshold,\n new_status: score >= threshold ? 'approved' : 'archived'\n }\n}];"
},
"id": "c3000000-0002-0002-0002-000000000008",
"name": "Parse Score",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1780,
500
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "={{ `UPDATE topics SET status = '${$json.new_status}', value_score = ${$json.value_score}, evergreen_score = ${$json.evergreen_score}, cpm_tier = '${$json.cpm_tier}', updated_at = NOW() WHERE id = ${$json.topic_id} RETURNING id, title, status, value_score` }}",
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000009",
"name": "Update Topic",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
2000,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"operation": "executeQuery",
"query": "={{ `INSERT INTO workflow_logs (workflow_name, workflow_id, execution_id, status, input_summary, output_summary, created_at) VALUES ('WF-002-topic-scoring', 'wf002-topic-scoring-000000000', '${$execution.id}', 'completed', '{}', '{}', NOW())` }}",
"options": {}
},
"id": "c3000000-0002-0002-0002-000000000010",
"name": "Log Result",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.5,
"position": [
2220,
500
],
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
}
],
"connections": {
"Schedule 30min": {
"main": [
[
{
"node": "Poll Discovered Topics",
"type": "main",
"index": 0
}
]
]
},
"Poll Discovered Topics": {
"main": [
[
{
"node": "Has Topics?",
"type": "main",
"index": 0
}
]
]
},
"Has Topics?": {
"main": [
[
{
"node": "Load Prompt",
"type": "main",
"index": 0
}
]
]
},
"Load Prompt": {
"main": [
[
{
"node": "Load Threshold",
"type": "main",
"index": 0
}
]
]
},
"Load Threshold": {
"main": [
[
{
"node": "Build Prompt",
"type": "main",
"index": 0
}
]
]
},
"Build Prompt": {
"main": [
[
{
"node": "Call SUB-001",
"type": "main",
"index": 0
}
]
]
},
"Call SUB-001": {
"main": [
[
{
"node": "Parse Score",
"type": "main",
"index": 0
}
]
]
},
"Parse Score": {
"main": [
[
{
"node": "Update Topic",
"type": "main",
"index": 0
}
]
]
},
"Update Topic": {
"main": [
[
{
"node": "Log Result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
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.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
WF-002-topic-scoring. Uses postgres. Scheduled trigger; 10 nodes.
Source: https://github.com/navin-labs/content-factory-os/blob/main/n8n/workflows/main/WF-002-topic-scoring.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.
Disparador 1.8. Uses itemLists, postgres, emailSend, httpRequest. Scheduled trigger; 85 nodes.
공유회_알림톡_크론. Uses postgres, httpRequest, n8n-nodes-solapi. Scheduled trigger; 39 nodes.
QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.
QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.
QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.