{
  "updatedAt": "2026-05-07T00:48:49.355Z",
  "createdAt": "2026-05-07T00:48:49.355Z",
  "id": "4YKnRQ9rOiQCeYEU",
  "name": "Paperless \u2192 RAG Ingest",
  "description": "Disabled 2026-05-24 after PD Ollama/LiteLLM hammering destabilized PlausibleDeniability. Needs redesign before re-enable.",
  "active": false,
  "isArchived": false,
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "paperless/rag-ingest",
        "options": {}
      },
      "id": "a86d7025-bbf0-4c1b-a59c-ad254ddc913e",
      "name": "Paperless RAG Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        0,
        0
      ],
      "webhookId": "f2eccecb-9935-425b-be6a-f57fe4473f34"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.first().json;\nconst body = data.body || data;\n\nlet docId = body.document_id || body.id || body.doc_id || body.pk;\n\nif (!docId || typeof docId === 'string' && docId.includes('{')) {\n  docId = null;\n}\n\nreturn [{ json: { docId } }];"
      },
      "id": "8f6af849-bdd9-466e-af81-84d057fd7ca1",
      "name": "Extract Document ID",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        0
      ]
    },
    {
      "parameters": {
        "url": "=https://paperless.paccoco.com/api/documents/{{ $json.docId ? $json.docId + '/' : '?ordering=-added&page_size=1' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Token 4fb34ba35b4ab76a0715e8c56faf00a31de4fb5d"
            }
          ]
        },
        "options": {}
      },
      "id": "0b53d951-40cd-4ef0-ab81-3dea73f8b1d7",
      "name": "Fetch Document",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        448,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const response = $input.first().json;\nconst doc = response.results ? response.results[0] : response;\n\nif (!doc || !doc.id) {\n  throw new Error('No document found in response');\n}\n\nconst content = doc.content || '';\nconst title = doc.title || 'Untitled';\nconst correspondent = doc.correspondent_name || 'Unknown';\nconst created = doc.created || new Date().toISOString();\nconst docType = doc.document_type_name || 'unknown';\nconst tags = (doc.tags || []).join(', ');\n\n// Build rich text for RAG with metadata context\nconst ragText = `Document: ${title}\\nCorrespondent: ${correspondent}\\nType: ${docType}\\nTags: ${tags}\\nDate: ${created}\\n\\n${content}`;\n\nreturn [{\n  json: {\n    documentId: doc.id,\n    title,\n    ragText,\n    source: `paperless-doc-${doc.id}`,\n    metadata: {\n      paperless_id: doc.id,\n      title,\n      correspondent,\n      document_type: docType,\n      created\n    }\n  }\n}];"
      },
      "id": "e3d506bc-1677-4825-b3d3-f6187156da22",
      "name": "Prepare RAG Text",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Split text into chunks for embedding\nconst input = $input.first().json;\nconst text = input.ragText || '';\nconst source = input.source;\nconst metadata = input.metadata;\n\nconst CHUNK_SIZE = 500;\nconst OVERLAP = 50;\n\nconst chunks = [];\nconst sentences = text.split(/(?<=[.!?])\\s+/);\nlet current = '';\n\nfor (const sentence of sentences) {\n  if ((current + ' ' + sentence).length > CHUNK_SIZE && current.length > 0) {\n    chunks.push(current.trim());\n    const words = current.split(' ');\n    current = words.slice(-OVERLAP).join(' ') + ' ' + sentence;\n  } else {\n    current = current ? current + ' ' + sentence : sentence;\n  }\n}\nif (current.trim()) chunks.push(current.trim());\n\nreturn chunks.map((chunk, i) => ({\n  json: {\n    chunk,\n    chunkIndex: i,\n    totalChunks: chunks.length,\n    source,\n    metadata\n  }\n}));"
      },
      "id": "9a523501-3adb-41a3-b963-b6bb5adb25fe",
      "name": "Chunk Text",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://10.5.30.6:11434/v1/embeddings",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"nomic-embed-text\",\n  \"input\": \"{{ $json.chunk }}\"\n}",
        "options": {
          "batching": {
            "batch": {
              "batchSize": 5,
              "batchInterval": 500
            }
          }
        }
      },
      "id": "0b631ea4-b11f-4533-960f-f5161ac798f3",
      "name": "Get Embedding",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1104,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const embeddingResponse = $input.first().json;\nconst chunkData = $('Chunk Text').item;\nconst embedding = embeddingResponse.data?.[0]?.embedding || [];\n\nconst pointId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {\n  const r = Math.random() * 16 | 0;\n  return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);\n});\n\nreturn [{\n  json: {\n    id: pointId,\n    vector: embedding,\n    payload: {\n      text: chunkData.json.chunk,\n      source: chunkData.json.source,\n      chunkIndex: chunkData.json.chunkIndex,\n      totalChunks: chunkData.json.totalChunks,\n      metadata: chunkData.json.metadata,\n      ingested_at: new Date().toISOString()\n    }\n  }\n}];"
      },
      "id": "ad9dc677-e3a2-464e-a82e-2aa4d8b9b042",
      "name": "Prepare Qdrant Point",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        0
      ]
    },
    {
      "parameters": {
        "method": "PUT",
        "url": "http://10.5.30.6:6333/collections/knowledge_base/points",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"points\": [\n    {\n      \"id\": \"{{ $json.id }}\",\n      \"vector\": {{ JSON.stringify($json.vector) }},\n      \"payload\": {{ JSON.stringify($json.payload) }}\n    }\n  ]\n}",
        "options": {}
      },
      "id": "e977b536-d470-4da0-a970-794a858e8aa9",
      "name": "Upsert to Qdrant",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1552,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const ragText = $('Prepare RAG Text').first().json;\nconst chunks  = $('Chunk Text').first().json;\nreturn [{\n  json: {\n    title:    '\ud83d\udcda Document Added to RAG',\n    message:  `${ragText.title} has been chunked and ingested into the knowledge base (${chunks.totalChunks || '?'} chunks)`,\n    priority: 3\n  }\n}];"
      },
      "id": "fmt05-0001-0000-0000-000000000005",
      "name": "Format Gotify Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1520,
        0
      ]
    },
    {
      "parameters": {
        "message": "={{ $json.message }}",
        "additionalFields": {
          "priority": "={{ $json.priority }}",
          "title": "={{ $json.title }}"
        },
        "options": {
          "contentType": "text/plain"
        }
      },
      "id": "ccdee208-b8f8-4ad4-a20c-94c4735d2e3d",
      "name": "Notify via Gotify",
      "type": "n8n-nodes-base.gotify",
      "typeVersion": 1,
      "position": [
        1760,
        0
      ],
      "credentials": {
        "gotifyApi": {
          "id": "SETUP_REQUIRED",
          "name": "Paperless RAG"
        }
      }
    }
  ],
  "connections": {
    "Paperless RAG Webhook": {
      "main": [
        [
          {
            "node": "Extract Document ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Document ID": {
      "main": [
        [
          {
            "node": "Fetch Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Document": {
      "main": [
        [
          {
            "node": "Prepare RAG Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare RAG Text": {
      "main": [
        [
          {
            "node": "Chunk Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chunk Text": {
      "main": [
        [
          {
            "node": "Get Embedding",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Embedding": {
      "main": [
        [
          {
            "node": "Prepare Qdrant Point",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Qdrant Point": {
      "main": [
        [
          {
            "node": "Upsert to Qdrant",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upsert to Qdrant": {
      "main": [
        [
          {
            "node": "Format Gotify Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Gotify Message": {
      "main": [
        [
          {
            "node": "Notify via Gotify",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  },
  "staticData": null,
  "meta": null,
  "pinData": {},
  "versionId": "3c20b423-8708-4540-8bf9-8db4880335a5",
  "activeVersionId": "3c20b423-8708-4540-8bf9-8db4880335a5",
  "versionCounter": 12,
  "triggerCount": 1,
  "tags": [
    {
      "updatedAt": "2026-05-06T22:39:46.476Z",
      "createdAt": "2026-05-06T22:39:46.476Z",
      "id": "NxHjuO4MMd5kdPR7",
      "name": "paperless"
    },
    {
      "updatedAt": "2026-05-06T22:39:33.284Z",
      "createdAt": "2026-05-06T22:39:33.284Z",
      "id": "7uuEyQrIlcuruZBk",
      "name": "rag"
    },
    {
      "updatedAt": "2026-05-06T22:38:51.678Z",
      "createdAt": "2026-05-06T22:38:51.678Z",
      "id": "S9FxzVfHLsHmyzyt",
      "name": "ai"
    }
  ],
  "shared": [
    {
      "updatedAt": "2026-05-07T00:48:49.355Z",
      "createdAt": "2026-05-07T00:48:49.355Z",
      "role": "workflow:owner",
      "workflowId": "4YKnRQ9rOiQCeYEU",
      "projectId": "dxCRnBdX5uJizCGa",
      "project": {
        "updatedAt": "2026-05-06T01:10:37.484Z",
        "createdAt": "2026-05-06T01:08:07.721Z",
        "id": "dxCRnBdX5uJizCGa",
        "name": "Wilfred Fizzlepoof <admin@paccoco.com>",
        "type": "personal",
        "icon": null,
        "description": null,
        "creatorId": "47b2227f-2fc2-4a08-bd35-ea157b92df0d"
      }
    }
  ],
  "versionMetadata": {
    "name": "Version 3c20b423",
    "description": ""
  }
}