AutomationFlowsAI & RAG › Engagement Tracking Workflow

Engagement Tracking Workflow

Engagement Tracking Workflow. Uses postgres, httpRequest. Webhook trigger; 22 nodes.

Webhook trigger★★★★☆ complexity22 nodesPostgresHTTP Request
AI & RAG Trigger: Webhook Nodes: 22 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Postgres 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": "Engagement Tracking Workflow",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "engagement-tracking",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-tracking",
      "name": "Webhook - Track Engagement",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-action",
              "leftValue": "={{ $json.body.action }}",
              "rightValue": "start_tracking",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "route-action",
      "name": "Route by Action",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        450,
        300
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT pc.*, cd.title, cd.campaign_id FROM published_content pc JOIN content_drafts cd ON pc.draft_id = cd.id WHERE pc.draft_id = {{ $('Webhook - Track Engagement').item.json.body.draft_id }}",
        "additionalFields": {}
      },
      "id": "get-publications",
      "name": "Get Published Content",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        650,
        200
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "multiplex"
      },
      "id": "split-publications",
      "name": "Split Publications",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        850,
        200
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-linkedin-channel",
              "leftValue": "={{ $json.channel }}",
              "rightValue": "linkedin",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-channel",
      "name": "If Channel Type",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3,
      "position": [
        1050,
        200
      ]
    },
    {
      "parameters": {
        "url": "http://publishing-service:8002/linkedin/stats",
        "method": "POST",
        "bodyParametersJson": "={\n  \"access_token\": \"{{ $env.LINKEDIN_ACCESS_TOKEN }}\",\n  \"post_url\": \"{{ $json.url }}\"\n}",
        "options": {}
      },
      "id": "fetch-linkedin-stats",
      "name": "Fetch LinkedIn Stats",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1250,
        100
      ]
    },
    {
      "parameters": {
        "url": "http://publishing-service:8002/wordpress/stats",
        "method": "POST",
        "bodyParametersJson": "={\n  \"wp_url\": \"{{ $env.WORDPRESS_URL }}\",\n  \"wp_username\": \"{{ $env.WORDPRESS_USERNAME }}\",\n  \"wp_password\": \"{{ $env.WORDPRESS_PASSWORD }}\",\n  \"post_id\": \"{{ $json.metadata_json.post_id }}\"\n}",
        "options": {}
      },
      "id": "fetch-wordpress-stats",
      "name": "Fetch WordPress Stats",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1250,
        250
      ]
    },
    {
      "parameters": {
        "url": "http://matomo:9000/index.php",
        "method": "GET",
        "queryParametersJson": "={\n  \"module\": \"API\",\n  \"method\": \"Actions.getPageUrl\",\n  \"pageUrl\": \"{{ $json.url }}\",\n  \"idSite\": {{ $env.MATOMO_SITE_ID }},\n  \"period\": \"day\",\n  \"date\": \"today\",\n  \"format\": \"JSON\",\n  \"token_auth\": \"{{ $env.MATOMO_AUTH_TOKEN }}\"\n}",
        "options": {}
      },
      "id": "fetch-matomo-stats",
      "name": "Fetch Matomo Analytics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1250,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "// Aggregate engagement metrics from different sources\nconst publication = $('Split Publications').item.json;\nconst channel = publication.channel;\n\nlet metrics = {\n  content_id: publication.id,\n  channel: channel,\n  views: 0,\n  clicks: 0,\n  shares: 0,\n  conversions: 0,\n  engagement_rate: 0,\n  tracked_at: new Date().toISOString()\n};\n\nif (channel === 'linkedin') {\n  const linkedin = $json;\n  metrics.views = linkedin.impressions || 0;\n  metrics.clicks = linkedin.clicks || 0;\n  metrics.shares = linkedin.shares || 0;\n  metrics.engagement_rate = linkedin.engagement_rate || 0;\n} else if (channel === 'wordpress') {\n  const wp = $json;\n  metrics.views = wp.views || 0;\n  metrics.clicks = wp.comments || 0;  // Comments as engagement\n} else {\n  // Use Matomo for general tracking\n  const matomo = $json;\n  metrics.views = matomo.nb_visits || 0;\n  metrics.clicks = matomo.nb_actions || 0;\n}\n\n// Calculate engagement rate\nif (metrics.views > 0) {\n  metrics.engagement_rate = ((metrics.clicks + metrics.shares) / metrics.views * 100).toFixed(2);\n}\n\nreturn metrics;"
      },
      "id": "aggregate-metrics",
      "name": "Aggregate Metrics",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1450,
        200
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO engagement_metrics (content_id, channel, views, clicks, shares, conversions, engagement_rate, tracked_at) VALUES ({{ $json.content_id }}, '{{ $json.channel }}', {{ $json.views }}, {{ $json.clicks }}, {{ $json.shares }}, {{ $json.conversions }}, {{ $json.engagement_rate }}, '{{ $json.tracked_at }}') ON CONFLICT (content_id, channel, DATE(tracked_at)) DO UPDATE SET views = EXCLUDED.views, clicks = EXCLUDED.clicks, shares = EXCLUDED.shares, conversions = EXCLUDED.conversions, engagement_rate = EXCLUDED.engagement_rate, tracked_at = EXCLUDED.tracked_at RETURNING *",
        "additionalFields": {}
      },
      "id": "save-metrics",
      "name": "Save Engagement Metrics",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1650,
        200
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "condition-high-engagement",
              "leftValue": "={{ $json.engagement_rate }}",
              "rightValue": "5",
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-high-engagement",
      "name": "If High Engagement",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1850,
        200
      ]
    },
    {
      "parameters": {
        "url": "http://publishing-service:8002/notifications/send",
        "method": "POST",
        "bodyParametersJson": "={\n  \"type\": \"high_engagement_alert\",\n  \"content_id\": {{ $json.content_id }},\n  \"channel\": \"{{ $json.channel }}\",\n  \"metrics\": {\n    \"views\": {{ $json.views }},\n    \"clicks\": {{ $json.clicks }},\n    \"shares\": {{ $json.shares }},\n    \"engagement_rate\": {{ $json.engagement_rate }}\n  },\n  \"message\": \"Your content is performing exceptionally well with {{ $json.engagement_rate }}% engagement rate!\"\n}",
        "options": {}
      },
      "id": "send-alert",
      "name": "Send High Engagement Alert",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        2050,
        150
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Engagement tracking initiated\",\n  \"draft_id\": {{ $('Webhook - Track Engagement').item.json.body.draft_id }},\n  \"channels_tracked\": {{ $('Get Published Content').all().length }},\n  \"tracking_interval\": \"real-time\"\n}",
        "options": {}
      },
      "id": "response-tracking-started",
      "name": "Respond Tracking Started",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        850,
        350
      ]
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "engagement/update",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-update",
      "name": "Webhook - Update Metrics",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        500
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "UPDATE engagement_metrics SET views = {{ $json.body.views || views }}, clicks = {{ $json.body.clicks || clicks }}, shares = {{ $json.body.shares || shares }}, conversions = {{ $json.body.conversions || conversions }}, tracked_at = NOW() WHERE content_id = {{ $json.body.content_id }} AND channel = '{{ $json.body.channel }}' RETURNING *",
        "additionalFields": {}
      },
      "id": "update-metrics",
      "name": "Update Engagement Metrics",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        450,
        500
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"success\": true,\n  \"message\": \"Metrics updated successfully\",\n  \"content_id\": {{ $json.content_id }},\n  \"channel\": \"{{ $json.channel }}\",\n  \"current_metrics\": {\n    \"views\": {{ $json.views }},\n    \"clicks\": {{ $json.clicks }},\n    \"shares\": {{ $json.shares }},\n    \"conversions\": {{ $json.conversions }},\n    \"engagement_rate\": {{ $json.engagement_rate }}\n  }\n}",
        "options": {}
      },
      "id": "response-updated",
      "name": "Respond Updated",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        650,
        500
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 1
            }
          ]
        }
      },
      "id": "schedule-hourly",
      "name": "Schedule - Hourly Metrics",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        250,
        700
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT pc.*, cd.campaign_id FROM published_content pc JOIN content_drafts cd ON pc.draft_id = cd.id WHERE pc.published_at > NOW() - INTERVAL '7 days' ORDER BY pc.published_at DESC",
        "additionalFields": {}
      },
      "id": "get-recent-publications",
      "name": "Get Recent Publications",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        450,
        700
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "mode": "multiplex"
      },
      "id": "split-recent",
      "name": "Split Recent Publications",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        650,
        700
      ]
    },
    {
      "parameters": {
        "url": "http://localhost:5678/webhook/engagement-tracking",
        "method": "POST",
        "bodyParametersJson": "={\n  \"action\": \"start_tracking\",\n  \"draft_id\": {{ $json.draft_id }},\n  \"publications\": [{{ $json }}]\n}",
        "options": {}
      },
      "id": "refresh-metrics",
      "name": "Refresh Publication Metrics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        850,
        700
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT c.id as campaign_id, c.name, AVG(em.engagement_rate) as avg_engagement, SUM(em.views) as total_views, SUM(em.clicks) as total_clicks, SUM(em.shares) as total_shares FROM campaigns c JOIN content_drafts cd ON c.id = cd.campaign_id JOIN published_content pc ON cd.id = pc.draft_id JOIN engagement_metrics em ON pc.id = em.content_id WHERE em.tracked_at > NOW() - INTERVAL '7 days' GROUP BY c.id, c.name ORDER BY avg_engagement DESC",
        "additionalFields": {}
      },
      "id": "calculate-campaign-performance",
      "name": "Calculate Campaign Performance",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1050,
        700
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "insert",
        "table": "campaign_analytics",
        "columns": "campaign_id, avg_engagement_rate, total_views, total_clicks, total_shares, calculated_at",
        "additionalFields": {}
      },
      "id": "save-campaign-analytics",
      "name": "Save Campaign Analytics",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        1250,
        700
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "Webhook - Track Engagement": {
      "main": [
        [
          {
            "node": "Route by Action",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Action": {
      "main": [
        [
          {
            "node": "Get Published Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Published Content": {
      "main": [
        [
          {
            "node": "Split Publications",
            "type": "main",
            "index": 0
          },
          {
            "node": "Respond Tracking Started",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Publications": {
      "main": [
        [
          {
            "node": "If Channel Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If Channel Type": {
      "main": [
        [
          {
            "node": "Fetch LinkedIn Stats",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch WordPress Stats",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fetch Matomo Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch LinkedIn Stats": {
      "main": [
        [
          {
            "node": "Aggregate Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch WordPress Stats": {
      "main": [
        [
          {
            "node": "Aggregate Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Matomo Analytics": {
      "main": [
        [
          {
            "node": "Aggregate Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Metrics": {
      "main": [
        [
          {
            "node": "Save Engagement Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Engagement Metrics": {
      "main": [
        [
          {
            "node": "If High Engagement",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "If High Engagement": {
      "main": [
        [
          {
            "node": "Send High Engagement Alert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook - Update Metrics": {
      "main": [
        [
          {
            "node": "Update Engagement Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Engagement Metrics": {
      "main": [
        [
          {
            "node": "Respond Updated",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule - Hourly Metrics": {
      "main": [
        [
          {
            "node": "Get Recent Publications",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Recent Publications": {
      "main": [
        [
          {
            "node": "Split Recent Publications",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Recent Publications": {
      "main": [
        [
          {
            "node": "Refresh Publication Metrics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Refresh Publication Metrics": {
      "main": [
        [
          {
            "node": "Calculate Campaign Performance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Campaign Performance": {
      "main": [
        [
          {
            "node": "Save Campaign Analytics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [],
  "triggerCount": 2,
  "updatedAt": "2024-01-15T00:00:00.000Z",
  "versionId": "1"
}

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

Engagement Tracking Workflow. Uses postgres, httpRequest. Webhook trigger; 22 nodes.

Source: https://github.com/Yaakovyitzchak1231/CLAUDE-CODE_Marketing-Agent/blob/a9f11690dfb44a4e0f520ab2cd1436f06b18c442/n8n-workflows/engagement_tracking.json — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

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

AI & RAG

Jigsaw API key for image processing, I use this as a gatekeeper/second pair of eyes. LINK to their website https://jigsawstack.com/ SECOND A postgress DATABASE (I use Supabase) LlamaCloud for the pars

HTTP Request, Postgres, Stop And Error +2
AI & RAG

W1 - IN WhatsApp Adapter (Secure + Fast ACK). Uses postgres, redis, httpRequest. Webhook trigger; 48 nodes.

Postgres, Redis, HTTP Request
AI & RAG

W2 - IN Instagram Adapter (Secure). Uses postgres, httpRequest. Webhook trigger; 28 nodes.

Postgres, HTTP Request
AI & RAG

W3 - IN Messenger Adapter (Secure). Uses postgres, httpRequest. Webhook trigger; 28 nodes.

Postgres, HTTP Request
AI & RAG

Content Review Loop Workflow. Uses postgres, httpRequest. Webhook trigger; 20 nodes.

Postgres, HTTP Request