{
  "_comment": "Workflow: NGO.tools KB Ingestion | n8n ID: D74o4IMPY9yNooJl | Webhook: /webhook/ngo-tools-ingest (POST)",
  "name": "NGO.tools Knowledge Base Ingestion",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "ngo-tools-ingest",
        "options": {}
      },
      "id": "webhook-trigger-001",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        -112,
        -16
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "// Extract documents from webhook body and prepare for Document Loader\nconst body = $input.first().json.body || $input.first().json;\nconst docs = body.documents || [];\nconst items = [];\n\nfor (const doc of docs) {\n  const source = doc.route_uri || doc.source || doc.file_path || `manual:${doc.title || 'untitled'}`;\n  \n  items.push({\n    json: {\n      data: JSON.stringify({\n        content: doc.content || '',\n        title: doc.title || 'Untitled',\n        source: source,\n        doc_type: doc.doc_type || 'unknown',\n        entity_name: doc.entity_name || ''\n      }),\n      metadata_title: doc.title || 'Untitled',\n      metadata_source: source,\n      metadata_doc_type: doc.doc_type || 'unknown',\n      metadata_entity_name: doc.entity_name || '',\n      metadata_ingested_at: new Date().toISOString()\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "code-format-001",
      "name": "Format Documents",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        160,
        -16
      ]
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "DELETE FROM n8n_vectors_ngo_tools WHERE metadata->>'source' = '{{ $json.metadata_source }}'"
      },
      "id": "delete-existing-001",
      "name": "Delete Existing",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.5,
      "position": [
        400,
        -200
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "amount": 1,
        "unit": "seconds"
      },
      "id": "wait-for-delete-001",
      "name": "Wait for Delete",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        400,
        -16
      ]
    },
    {
      "parameters": {
        "mode": "insert",
        "tableName": "n8n_vectors_ngo_tools",
        "options": {}
      },
      "id": "vectorstore-insert-001",
      "name": "Store in PGVector",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePGVector",
      "typeVersion": 1,
      "position": [
        640,
        -16
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "options": {
          "metadata": {
            "metadataValues": [
              {
                "name": "title",
                "value": "={{ $json.metadata_title }}"
              },
              {
                "name": "source",
                "value": "={{ $json.metadata_source }}"
              },
              {
                "name": "doc_type",
                "value": "={{ $json.metadata_doc_type }}"
              },
              {
                "name": "entity_name",
                "value": "={{ $json.metadata_entity_name }}"
              },
              {
                "name": "ingested_at",
                "value": "={{ $json.metadata_ingested_at }}"
              }
            ]
          }
        }
      },
      "id": "doc-loader-001",
      "name": "Default Data Loader",
      "type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
      "typeVersion": 1,
      "position": [
        440,
        200
      ]
    },
    {
      "parameters": {
        "model": "text-embedding-3-small",
        "options": {}
      },
      "id": "embedding-openai-001",
      "name": "OpenAI Embeddings",
      "type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
      "typeVersion": 1,
      "position": [
        640,
        272
      ],
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "chunkSize": 1000,
        "chunkOverlap": 200,
        "options": {}
      },
      "id": "text-splitter-001",
      "name": "Recursive Text Splitter",
      "type": "@n8n/n8n-nodes-langchain.textSplitterRecursiveCharacterTextSplitter",
      "typeVersion": 1,
      "position": [
        240,
        320
      ]
    }
  ],
  "connections": {
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Format Documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Documents": {
      "main": [
        [
          {
            "node": "Delete Existing",
            "type": "main",
            "index": 0
          },
          {
            "node": "Wait for Delete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Delete": {
      "main": [
        [
          {
            "node": "Store in PGVector",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Default Data Loader": {
      "ai_document": [
        [
          {
            "node": "Store in PGVector",
            "type": "ai_document",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Embeddings": {
      "ai_embedding": [
        [
          {
            "node": "Store in PGVector",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "Recursive Text Splitter": {
      "ai_textSplitter": [
        [
          {
            "node": "Default Data Loader",
            "type": "ai_textSplitter",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "NGO.tools"
    },
    {
      "name": "Ingestion"
    }
  ]
}