AutomationFlowsData & Sheets › Bulletin Synthesis Workflow

Bulletin Synthesis Workflow

Bulletin Synthesis Workflow. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 9 nodes.

Cron / scheduled trigger★★★★☆ complexity9 nodesPostgresHTTP RequestError Trigger
Data & Sheets Trigger: Cron / scheduled Nodes: 9 Complexity: ★★★★☆ Added:

This workflow follows the Error Trigger → HTTP Request 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 →

Download .json
{
  "name": "Bulletin Synthesis Workflow",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 4 * * *"
            }
          ]
        }
      },
      "id": "schedule-node",
      "name": "Schedule (04:00 AM)",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        100,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT rates FROM fx_rates ORDER BY fetched_at DESC LIMIT 1;"
      },
      "id": "fetch-fx",
      "name": "Fetch FX Rates",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        300,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT commodity, json_agg(row_to_json(market_data.*)) as data FROM market_data WHERE fetched_at > NOW() - INTERVAL '48 hours' GROUP BY commodity;"
      },
      "id": "fetch-market",
      "name": "Fetch Market Data",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        500,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const fxRates = $('Fetch FX Rates').first().json.rates || {};\nconst usdToVnd = fxRates.VND || 25000;\n\nconst commodity = $input.item.json.commodity;\nconst dataArr = $input.item.json.data || [];\n\nlet rawContext = `D\u1eef li\u1ec7u th\u1ecb tr\u01b0\u1eddng cho ${commodity} trong 48h qua:\\n`;\nlet sources = [];\n\ndataArr.forEach(row => {\n  rawContext += `- [${row.source}] ${row.metric}: ${row.value} ${row.unit} (Th\u1eddi gian: ${row.period})\\n`;\n  sources.push({\n    source: row.source,\n    metric: row.metric,\n    value: row.value,\n    date: row.period\n  });\n});\n\nrawContext += `\\nT\u1ef7 gi\u00e1 tham chi\u1ebfu: 1 USD = ${usdToVnd} VND.`;\n\nreturn {\n  commodity: commodity,\n  raw_context: rawContext,\n  sources_json: sources\n};"
      },
      "id": "code-rag",
      "name": "Code (Prepare RAG Context)",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        700,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $env.GROQ_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "={{ $env.OLLAMA_MODEL }}"
            },
            {
              "name": "stream",
              "value": false
            },
            {
              "name": "messages",
              "value": "={{ [\n  {\n    \"role\": \"system\",\n    \"content\": \"B\u1ea1n l\u00e0 chuy\u00ean gia ph\u00e2n t\u00edch th\u1ecb tr\u01b0\u1eddng n\u00f4ng nghi\u1ec7p. H\u00e3y vi\u1ebft b\u1ea3n tin th\u1ecb tr\u01b0\u1eddng b\u1eb1ng ti\u1ebfng Vi\u1ec7t ng\u1eafn g\u1ecdn, s\u00fac t\u00edch. KH\u00d4NG bao g\u1ed3m suy lu\u1eadn hay gi\u1ea3i th\u00edch qu\u00e1 tr\u00ecnh suy ngh\u0129. Ch\u1ec9 tr\u1ea3 v\u1ec1 b\u1ea3n tin ho\u00e0n ch\u1ec9nh. QUY T\u1eaeC AI INVARIANT B\u1eaeT BU\u1ed8C:\\n1. CH\u1ec8 tr\u00ecnh b\u00e0y s\u1ef1 th\u1eadt c\u00f3 tr\u00edch d\u1eabn ngu\u1ed3n.\\n2. KH\u00d4NG ra quy\u1ebft \u0111\u1ecbnh thay HTX.\\n3. KH\u00d4NG khuy\u1ebfn ngh\u1ecb h\u00e0nh \u0111\u1ed9ng c\u1ee5 th\u1ec3.\\n4. M\u1ecdi s\u1ed1 li\u1ec7u ph\u1ea3i k\u00e8m ngu\u1ed3n.\"\n  },\n  {\n    \"role\": \"user\",\n    \"content\": $json.raw_context\n  }\n] }}"
            }
          ]
        },
        "options": {}
      },
      "id": "ask-ollama",
      "name": "Ask Ollama",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        900,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Strip <think>...</think> blocks from reasoning model output\nconst rawContent = $input.item.json.choices[0].message.content;\nconst cleanContent = rawContent.replace(/<think>[\\s\\S]*?<\\/think>/g, '').trim();\n\nreturn {\n  bulletin_text: cleanContent,\n  commodity: $('Code (Prepare RAG Context)').item.json.commodity,\n  sources_json: $('Code (Prepare RAG Context)').item.json.sources_json\n};"
      },
      "id": "strip-thinking",
      "name": "Strip Thinking Block",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1050,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE bulletins SET is_latest = false WHERE commodity = '{{ $('Strip Thinking Block').item.json.commodity }}';"
      },
      "id": "deprecate-old",
      "name": "Deprecate Old Bulletins",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        1200,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO bulletins (commodity, bulletin_vi, sources_json, model_used, is_latest) VALUES ($${{ $('Strip Thinking Block').item.json.commodity }}$$, $${{ $('Strip Thinking Block').item.json.bulletin_text }}$$, $${{ JSON.stringify($('Strip Thinking Block').item.json.sources_json) }}$$::jsonb, $${{ $env.OLLAMA_MODEL }}$$, true);"
      },
      "id": "insert-new",
      "name": "Insert New Bulletin",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        1400,
        300
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {},
      "id": "error-trigger",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "typeVersion": 1,
      "position": [
        100,
        100
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO notifications (id, type, title, body, created_at, is_read) VALUES (gen_random_uuid()::text, 'SYSTEM', 'n8n Workflow Error', 'Workflow ' || '{{$workflow.name}}' || ' failed (Execution ID: ' || '{{$json.execution.id}}' || ')', NOW(), false);"
      },
      "id": "log-error",
      "name": "Log Error to Postgres",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        300,
        100
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Schedule (04:00 AM)": {
      "main": [
        [
          {
            "node": "Fetch FX Rates",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch FX Rates": {
      "main": [
        [
          {
            "node": "Fetch Market Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Market Data": {
      "main": [
        [
          {
            "node": "Code (Prepare RAG Context)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code (Prepare RAG Context)": {
      "main": [
        [
          {
            "node": "Ask Ollama",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ask Ollama": {
      "main": [
        [
          {
            "node": "Strip Thinking Block",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Strip Thinking Block": {
      "main": [
        [
          {
            "node": "Deprecate Old Bulletins",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deprecate Old Bulletins": {
      "main": [
        [
          {
            "node": "Insert New Bulletin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Log Error to Postgres",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

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.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Bulletin Synthesis Workflow. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 9 nodes.

Source: https://github.com/nguyentrananhhoang13122005/OPEN-DX-AGRIMARKET/blob/2d67c75d4c2768aaa976413d50faa3dd9bf6ebe1/workflows/bulletin-synthesis.json — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

Bulletin Synthesis Workflow. Uses postgres, httpRequest, errorTrigger. Scheduled trigger; 10 nodes.

Postgres, HTTP Request, Error Trigger
Data & Sheets

Disparador 1.8. Uses itemLists, postgres, emailSend, httpRequest. Scheduled trigger; 85 nodes.

Item Lists, Postgres, Email Send +1
Data & Sheets

Workflow B — AI Listing Engine. Uses httpRequest, postgres, errorTrigger. Webhook trigger; 47 nodes.

HTTP Request, Postgres, Error Trigger
Data & Sheets

공유회_알림톡_크론. Uses postgres, httpRequest, n8n-nodes-solapi. Scheduled trigger; 39 nodes.

Postgres, HTTP Request, N8N Nodes Solapi
Data & Sheets

QuepasaAutomatic. Uses postgres, postgresTrigger, httpRequest. Scheduled trigger; 39 nodes.

Postgres, Postgres Trigger, HTTP Request