AutomationFlowsData & Sheets › Wf-01: Ingestion Pipeline

Wf-01: Ingestion Pipeline

WF-01: Ingestion Pipeline. Uses httpRequest, supabase. Webhook trigger; 8 nodes.

Webhook trigger★★★★☆ complexity8 nodesHTTP RequestSupabase
Data & Sheets Trigger: Webhook Nodes: 8 Complexity: ★★★★☆ Added:

This workflow follows the HTTP Request → Supabase 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": "WF-01: Ingestion Pipeline",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "wf01-ingest",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [
        250,
        300
      ],
      "notes": "Listens for POST requests. Send content here from Slack, GitHub webhooks, manual scripts, or any source."
    },
    {
      "parameters": {
        "functionCode": "// Extract content from webhook payload\n// Handles different payload shapes from different sources\nconst body = $input.item.json.body || $input.item.json;\n\nreturn {\n  json: {\n    message_text: body.text || body.message || '',\n    user_id: body.user || body.user_id || 'anonymous',\n    source: body.source || 'manual',\n    timestamp: body.timestamp || new Date().toISOString(),\n    metadata: body.metadata || {}\n  }\n};"
      },
      "id": "extract-content",
      "name": "Extract Content",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        450,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.message_text.length}}",
              "operation": "largerEqual",
              "value2": "10"
            },
            {
              "value1": "={{$json.message_text}}",
              "operation": "notStartsWith",
              "value2": "/"
            }
          ]
        }
      },
      "id": "filter-messages",
      "name": "Filter Messages",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        650,
        300
      ],
      "notes": "Drops messages under 10 chars and slash commands to avoid classifying noise."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.anthropic.com/v1/messages",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "anthropicApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "anthropic-version",
              "value": "2023-06-01"
            },
            {
              "name": "content-type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "model",
              "value": "claude-sonnet-4-20250514"
            },
            {
              "name": "max_tokens",
              "value": "1024"
            },
            {
              "name": "messages",
              "value": "=[{\"role\": \"user\", \"content\": \"Analyze this message and extract metadata:\\n\\nMessage: {{$json.message_text}}\\nSource: {{$json.source}}\\nTimestamp: {{$json.timestamp}}\\n\\nExtract and return ONLY valid JSON (no markdown, no explanation):\\n{\\n  \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\", \\\"tag3\\\"],\\n  \\\"entities\\\": [\\\"entity1\\\", \\\"entity2\\\"],\\n  \\\"summary\\\": \\\"1-2 sentence summary\\\",\\n  \\\"narrative_role\\\": \\\"setup|origin|exploration|conflict|realisation|integration\\\",\\n  \\\"date_estimated\\\": \\\"YYYY-MM-DD or YYYY\\\",\\n  \\\"timeline_group\\\": \\\"1974-1990|2019-2023|2024-2026\\\"\\n}\\n\\nRules:\\n- Tags: 3-5 keywords\\n- Entities: People, companies, concepts\\n- Summary: Concise, factual\\n- Narrative_role: Best fit from list\\n- Date_estimated: Infer from context or use timestamp\\n- Timeline_group: Map to period\"}]"
            }
          ]
        },
        "options": {}
      },
      "id": "claude-classify",
      "name": "Claude API Classification",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        850,
        300
      ],
      "notes": "Uses claude-sonnet-4-20250514. Requires Anthropic API credentials set in n8n Settings > Credentials. Replace with any other LLM if preferred."
    },
    {
      "parameters": {
        "functionCode": "// Parse Claude API response\nconst response = $input.item.json;\nconst content = response.content && response.content[0] && response.content[0].text;\n\nif (!content) {\n  throw new Error('No content in Claude response');\n}\n\n// Extract JSON from response (remove markdown code blocks if present)\nlet jsonText = content.trim();\nif (jsonText.startsWith('```json')) {\n  jsonText = jsonText.replace(/```json\\n?/g, '').replace(/```/g, '').trim();\n}\n\nconst classified = JSON.parse(jsonText);\n\n// Get original message data\nconst originalData = $node['Extract Content'].json;\n\n// Generate unique file_name: auto_{source}_{unix_timestamp}\nconst timestamp = new Date().getTime();\nconst file_name = `auto_${originalData.source}_${timestamp}`;\n\nreturn {\n  json: {\n    file_name: file_name,\n    type: null,\n    summary: classified.summary || '',\n    tags: classified.tags || [],\n    entities: classified.entities || [],\n    emotional_tone: null,\n    source: originalData.source,\n    themes: [],\n    concepts: [],\n    keywords: [],\n    mime_type: 'text/plain',\n    cluster: null,\n    related_ids: [],\n    date_estimated: classified.date_estimated || new Date().toISOString().split('T')[0],\n    timeline_group: classified.timeline_group || '2024-2026',\n    narrative_role: classified.narrative_role || 'exploration'\n  }\n};"
      },
      "id": "parse-classification",
      "name": "Parse Classification",
      "type": "n8n-nodes-base.function",
      "typeVersion": 1,
      "position": [
        1050,
        300
      ]
    },
    {
      "parameters": {
        "operation": "insert",
        "tableId": "metadata",
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "file_name": "={{$json.file_name}}",
            "type": "={{$json.type}}",
            "summary": "={{$json.summary}}",
            "tags": "={{$json.tags}}",
            "entities": "={{$json.entities}}",
            "emotional_tone": "={{$json.emotional_tone}}",
            "source": "={{$json.source}}",
            "themes": "={{$json.themes}}",
            "concepts": "={{$json.concepts}}",
            "keywords": "={{$json.keywords}}",
            "mime_type": "={{$json.mime_type}}",
            "cluster": "={{$json.cluster}}",
            "related_ids": "={{$json.related_ids}}",
            "date_estimated": "={{$json.date_estimated}}",
            "timeline_group": "={{$json.timeline_group}}",
            "narrative_role": "={{$json.narrative_role}}"
          }
        }
      },
      "id": "supabase-insert",
      "name": "Supabase Insert",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1250,
        300
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "notes": "Update credentials with your Supabase project URL and service role key."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://YOUR-N8N-INSTANCE.onrender.com/webhook/wf02-crosslink",
        "options": {}
      },
      "id": "trigger-crosslink",
      "name": "Trigger WF-02",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 3,
      "position": [
        1450,
        300
      ],
      "notes": "Replace URL with your n8n instance URL. Remove this node if you don't use WF-02."
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\"status\": \"success\", \"message\": \"Entry created and cross-linking triggered\", \"file_name\": \"{{$json.file_name}}\"}"
      },
      "id": "respond-success",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1,
      "position": [
        1650,
        300
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Extract Content",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Content": {
      "main": [
        [
          {
            "node": "Filter Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Messages": {
      "main": [
        [
          {
            "node": "Claude API Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude API Classification": {
      "main": [
        [
          {
            "node": "Parse Classification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Classification": {
      "main": [
        [
          {
            "node": "Supabase Insert",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Supabase Insert": {
      "main": [
        [
          {
            "node": "Trigger WF-02",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Trigger WF-02": {
      "main": [
        [
          {
            "node": "Respond Success",
            "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.

Pro

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

About this workflow

WF-01: Ingestion Pipeline. Uses httpRequest, supabase. Webhook trigger; 8 nodes.

Source: https://github.com/batistai4724/zero-cost-ops/blob/main/workflows/WF-01-ingestion-pipeline.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

booking no duplicate. Uses httpRequest, supabase. Webhook trigger; 38 nodes.

HTTP Request, Supabase
Data & Sheets

01-intake-storage. Uses airtable, supabase, stopAndError, httpRequest. Webhook trigger; 33 nodes.

Airtable, Supabase, Stop And Error +2
Data & Sheets

2. Refresh Pipedrive tokens. Uses stopAndError, stickyNote, supabase, httpRequest. Webhook trigger; 29 nodes.

Stop And Error, Supabase, HTTP Request
Data & Sheets

This workflow provides an OAuth 2.0 auth token refresh process for better control. Developers can utilize it as an alternative to n8n's built-in OAuth flow to achieve improved control and visibility.

Stop And Error, Supabase, HTTP Request
Data & Sheets

n8n-supabase-ai-pipeline. Uses supabase, httpRequest, postgres, readWriteFile. Webhook trigger; 25 nodes.

Supabase, HTTP Request, Postgres +1