{
  "id": "eed2a653-14c6-4ac2-bf87-1cfce78853ca",
  "name": "StudyFlow Document Workspace Pipeline",
  "nodes": [
    {
      "parameters": {
        "path": "studyflow/process-document",
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-intake",
      "name": "Webhook Intake",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1320,
        220
      ],
      "webhookId": "studyflow-process-document"
    },
    {
      "parameters": {
        "jsCode": "const body = $json.body ?? $json;\nconst required = ['userId', 'documentId', 'subject', 'fileUrl', 'mimeType', 'fileName'];\nconst missing = required.filter((key) => !body[key]);\nif (missing.length) {\n  throw new Error(`Missing required fields: ${missing.join(', ')}`);\n}\nconst supportedTypes = [\n  'text/plain',\n  'application/pdf',\n  'application/vnd.openxmlformats-officedocument.wordprocessingml.document'\n];\nif (!supportedTypes.includes(String(body.mimeType))) {\n  throw new Error(`Unsupported mimeType: ${body.mimeType}`);\n}\nconst processingMode = String(body.processingMode || 'workspace_only');\nconst aiMode = String(body.aiMode || 'mini');\nconst chatMode = 'strict_grounded';\nconst runId = `${body.documentId}-${Date.now()}`;\nreturn [{\n  json: {\n    ...body,\n    processingMode,\n    aiMode,\n    chatMode,\n    runId,\n    startedAt: new Date().toISOString(),\n    idempotencyKey: `${body.userId}:${body.documentId}`,\n    status: 'queued',\n    workflowType: 'document_workspace'\n  }\n}];"
      },
      "id": "validate-init",
      "name": "Validate + Init",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1080,
        220
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { accepted: true, runId: $json.runId, documentId: $json.documentId, status: 'queued', processingMode: $json.processingMode } }}",
        "options": {}
      },
      "id": "respond-accepted",
      "name": "Respond Accepted",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        -840,
        80
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/runs'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { runId: $json.runId, documentId: $json.documentId, userId: $json.userId, idempotencyKey: $json.idempotencyKey, status: 'queued', startedAt: $json.startedAt, source: 'n8n', workflowType: $json.workflowType } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "create-run-record",
      "name": "Create Run Record",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -840,
        360
      ]
    },
    {
      "parameters": {
        "url": "={{$json.fileUrl}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          },
          "timeout": 120000
        }
      },
      "id": "download-file",
      "name": "Download File",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -600,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_EXTRACTOR_URL + '/extract'}}",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParametersUi": {
          "parameter": [
            {
              "name": "documentId",
              "value": "={{$json.documentId}}"
            },
            {
              "name": "mimeType",
              "value": "={{$json.mimeType}}"
            },
            {
              "name": "fileName",
              "value": "={{$json.fileName}}"
            }
          ]
        },
        "sendBinaryData": true,
        "binaryPropertyName": "data",
        "options": {
          "timeout": 300000
        }
      },
      "id": "extract-text",
      "name": "Extract Text",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -360,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/documents/source'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { runId: $json.body?.runId || $json.runId, documentId: $json.documentId || $json.body?.documentId, userId: $json.body?.userId, subject: $json.body?.subject, mimeType: $json.mimeType, fileName: $json.fileName, text: $json.text, pageCount: $json.pageCount, charCount: $json.charCount, processingMode: $json.body?.processingMode, aiMode: $json.body?.aiMode, chatMode: $json.body?.chatMode } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "persist-source-document",
      "name": "Persist Source Document",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -120,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const payload = $json;\nconst body = payload.body || {};\nconst text = String(payload.text || '');\nconst estimatedInputTokens = Math.ceil(text.length / 4);\nconst estimatedTotalTokens = estimatedInputTokens + 4000;\nlet studyPackStrategy = 'none';\nif (body.processingMode === 'workspace_plus_study_pack') {\n  studyPackStrategy = estimatedTotalTokens > 80000 ? 'chunked_ai' : 'single_pass_ai';\n}\nreturn [{\n  json: {\n    ...payload,\n    estimatedInputTokens,\n    estimatedTotalTokens,\n    studyPackStrategy,\n    retrievalTopK: 6\n  }\n}];"
      },
      "id": "estimate-strategy",
      "name": "Estimate Workspace Strategy",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        120,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_TEMPLATE_RENDERER_URL + '/render-friendly'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { documentId: $json.documentId || $json.body?.documentId, subject: $json.body?.subject, text: $json.text, pageCount: $json.pageCount, mimeType: $json.mimeType, fileName: $json.fileName, template: 'studyflow-friendly-reader', highlightConcepts: true, estimatedInputTokens: $json.estimatedInputTokens, studyPackStrategy: $json.studyPackStrategy, retrievalTopK: $json.retrievalTopK, userId: $json.body?.userId, processingMode: $json.body?.processingMode, aiMode: $json.body?.aiMode, chatMode: $json.body?.chatMode, runId: $json.body?.runId } }}",
        "options": {
          "timeout": 180000
        }
      },
      "id": "render-friendly-html",
      "name": "Render Friendly HTML",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        380,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_RETRIEVAL_URL + '/index'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { documentId: $json.body?.documentId, userId: $json.body?.userId, subject: $json.body?.subject, text: $json.body?.text, pageCount: $json.body?.pageCount, mimeType: $json.body?.mimeType, fileName: $json.body?.fileName, transformedHtml: $json.result?.html || '', sections: $json.result?.sections || [], citationMode: 'chunk', maxChunkTokens: 1200, retrievalTopK: $json.body?.retrievalTopK, studyPackStrategy: $json.body?.studyPackStrategy, estimatedInputTokens: $json.body?.estimatedInputTokens, processingMode: $json.body?.processingMode, aiMode: $json.body?.aiMode, chatMode: $json.body?.chatMode, runId: $json.body?.runId } }}",
        "options": {
          "timeout": 180000
        }
      },
      "id": "build-retrieval-index",
      "name": "Build Retrieval Index",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        660,
        360
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.body.studyPackStrategy}}",
                    "rightValue": "none",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.body.studyPackStrategy}}",
                    "rightValue": "single_pass_ai",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.body.studyPackStrategy}}",
                    "rightValue": "chunked_ai",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "switch-study-pack",
      "name": "Switch Study Pack Mode",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        940,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_AI_SERVICE_URL + '/single-pass-study-pack'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { documentId: $json.body?.documentId, userId: $json.body?.userId, subject: $json.body?.subject, text: $json.body?.text, estimatedInputTokens: $json.body?.estimatedInputTokens, transformedHtml: $json.body?.transformedHtml, retrievalMetadata: $json.result || {}, targetModel: $json.body?.aiMode === 'deep' ? 'gpt-5.4' : 'gpt-5.4-mini', includeProviderMetadata: true, runId: $json.body?.runId } }}",
        "options": {
          "timeout": 600000
        }
      },
      "id": "generate-study-pack-single",
      "name": "Generate Study Pack (Single Pass)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1220,
        260
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_AI_SERVICE_URL + '/chunked-study-pack'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { documentId: $json.body?.documentId, userId: $json.body?.userId, subject: $json.body?.subject, text: $json.body?.text, estimatedInputTokens: $json.body?.estimatedInputTokens, transformedHtml: $json.body?.transformedHtml, retrievalMetadata: $json.result || {}, chunkTargetTokens: 4000, chunkModel: 'gpt-5.4-mini', finalModel: $json.body?.aiMode === 'deep' ? 'gpt-5.4' : 'gpt-5.4-mini', includeProviderMetadata: true, runId: $json.body?.runId } }}",
        "options": {
          "timeout": 900000
        }
      },
      "id": "generate-study-pack-chunked",
      "name": "Generate Study Pack (Chunked)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1220,
        460
      ]
    },
    {
      "parameters": {
        "jsCode": "const payload = $json;\nconst body = payload.body || {};\nconst studyPack = Array.isArray(payload.result?.digest) ? payload.result : null;\nconst retrievalMetadata = studyPack ? (body.retrievalMetadata || {}) : (payload.result || {});\nconst transformedHtml = studyPack ? (body.transformedHtml || '') : (body.transformedHtml || '');\nconst sections = studyPack ? (body.sections || []) : (body.sections || []);\nconst providerMessage = studyPack\n  ? `Workspace ready with optional study pack via ${payload.result?.modelUsed || payload.result?.model || 'unknown model'}.`\n  : 'Workspace ready with friendly transform and grounded chat.';\nreturn [{\n  json: {\n    runId: body.runId || payload.runId || null,\n    documentId: body.documentId || payload.documentId || null,\n    userId: body.userId || payload.userId || null,\n    subject: body.subject || payload.subject || null,\n    processingMode: body.processingMode || 'workspace_only',\n    mimeType: body.mimeType || null,\n    fileName: body.fileName || null,\n    estimatedInputTokens: body.estimatedInputTokens || null,\n    transformedDocument: {\n      html: transformedHtml,\n      sections,\n      provider: 'non-ai-template'\n    },\n    retrieval: retrievalMetadata,\n    chat: {\n      mode: 'strict_grounded',\n      retrievalReady: Boolean(retrievalMetadata.retrievalReady || retrievalMetadata.indexId)\n    },\n    studyPack,\n    provider: studyPack ? (payload.result?.provider || null) : 'non-ai-template',\n    model: studyPack ? (payload.result?.modelUsed || payload.result?.model || null) : null,\n    generationTimeMs: studyPack ? (payload.result?.generationTimeMs || null) : (retrievalMetadata.generationTimeMs || null),\n    providerMessage,\n    completedAt: new Date().toISOString()\n  }\n}];"
      },
      "id": "normalize-workspace",
      "name": "Normalize Workspace Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1500,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/document-workspaces'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { runId: $json.runId, documentId: $json.documentId, userId: $json.userId, subject: $json.subject, processingMode: $json.processingMode, mimeType: $json.mimeType, fileName: $json.fileName, estimatedInputTokens: $json.estimatedInputTokens, transformedDocument: $json.transformedDocument, retrieval: $json.retrieval, chat: $json.chat, studyPack: $json.studyPack, provider: $json.provider, model: $json.model, generationTimeMs: $json.generationTimeMs, providerMessage: $json.providerMessage, completedAt: $json.completedAt } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "persist-workspace",
      "name": "Persist Workspace Result",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1760,
        360
      ]
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/runs/' + $json.runId}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { status: 'ready', completedAt: $json.completedAt, provider: $json.provider, model: $json.model, generationTimeMs: $json.generationTimeMs, retrievalReady: $json.chat.retrievalReady, studyPackReady: Boolean($json.studyPack) } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "mark-run-success",
      "name": "Mark Run Success",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2020,
        360
      ]
    }
  ],
  "connections": {
    "Webhook Intake": {
      "main": [
        [
          {
            "node": "Validate + Init",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate + Init": {
      "main": [
        [
          {
            "node": "Respond Accepted",
            "type": "main",
            "index": 0
          },
          {
            "node": "Create Run Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Run Record": {
      "main": [
        [
          {
            "node": "Download File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download File": {
      "main": [
        [
          {
            "node": "Extract Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Text": {
      "main": [
        [
          {
            "node": "Persist Source Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Persist Source Document": {
      "main": [
        [
          {
            "node": "Estimate Workspace Strategy",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Estimate Workspace Strategy": {
      "main": [
        [
          {
            "node": "Render Friendly HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render Friendly HTML": {
      "main": [
        [
          {
            "node": "Build Retrieval Index",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Retrieval Index": {
      "main": [
        [
          {
            "node": "Switch Study Pack Mode",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch Study Pack Mode": {
      "main": [
        [
          {
            "node": "Normalize Workspace Result",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Study Pack (Single Pass)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Generate Study Pack (Chunked)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Study Pack (Single Pass)": {
      "main": [
        [
          {
            "node": "Normalize Workspace Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Study Pack (Chunked)": {
      "main": [
        [
          {
            "node": "Normalize Workspace Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Workspace Result": {
      "main": [
        [
          {
            "node": "Persist Workspace Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Persist Workspace Result": {
      "main": [
        [
          {
            "node": "Mark Run Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true,
    "timezone": "UTC"
  },
  "pinData": {},
  "versionId": "39d97591-abec-484b-89bf-cb442ad90cb5",
  "meta": {
    "templateCredsSetupCompleted": false,
    "notes": "Primary StudyFlow document-workspace pipeline: extraction, friendly transform, retrieval indexing, optional study-pack generation, and persistence."
  },
  "active": false
}
