{
  "id": "2a429de3-f688-4990-a958-3f78f5d9c321",
  "name": "StudyFlow Grounded Chat",
  "nodes": [
    {
      "parameters": {
        "path": "studyflow/chat",
        "httpMethod": "POST",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "webhook-chat",
      "name": "Webhook Chat Intake",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -1160,
        240
      ],
      "webhookId": "studyflow-chat"
    },
    {
      "parameters": {
        "jsCode": "const body = $json.body ?? $json;\nconst required = ['userId', 'documentId', 'conversationId', 'message'];\nconst missing = required.filter((key) => !body[key]);\nif (missing.length) {\n  throw new Error(`Missing required fields: ${missing.join(', ')}`);\n}\nreturn [{\n  json: {\n    ...body,\n    chatRunId: `${body.documentId}-${Date.now()}`,\n    topK: Number(body.topK || 6),\n    chatMode: 'strict_grounded',\n    receivedAt: new Date().toISOString()\n  }\n}];"
      },
      "id": "validate-chat-request",
      "name": "Validate Chat Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -920,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/document-workspaces/context'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { userId: $json.userId, documentId: $json.documentId, conversationId: $json.conversationId, message: $json.message, topK: $json.topK, chatRunId: $json.chatRunId, chatMode: $json.chatMode } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "load-workspace-context",
      "name": "Load Workspace Context",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -660,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_RETRIEVAL_URL + '/retrieve'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { documentId: $json.body?.documentId, userId: $json.body?.userId, subject: $json.result?.subject || $json.body?.subject || '', query: $json.body?.message, topK: $json.body?.topK, chatMode: $json.body?.chatMode, conversationId: $json.body?.conversationId, chatRunId: $json.body?.chatRunId } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "retrieve-relevant-chunks",
      "name": "Retrieve Relevant Chunks",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -380,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/conversations/history'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { conversationId: $json.body?.conversationId, documentId: $json.body?.documentId, userId: $json.body?.userId, message: $json.body?.message, chatMode: $json.body?.chatMode, retrievedChunks: $json.result?.chunks || [], chatRunId: $json.body?.chatRunId } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "fetch-conversation-history",
      "name": "Fetch Conversation History",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -100,
        240
      ]
    },
    {
      "parameters": {
        "jsCode": "const payload = $json;\nreturn [{\n  json: {\n    userId: payload.body?.userId,\n    documentId: payload.body?.documentId,\n    conversationId: payload.body?.conversationId,\n    chatRunId: payload.body?.chatRunId,\n    question: payload.body?.message,\n    chatMode: payload.body?.chatMode || 'strict_grounded',\n    retrievedChunks: payload.body?.retrievedChunks || [],\n    conversationHistory: payload.result?.messages || [],\n    topK: payload.body?.topK || 6\n  }\n}];"
      },
      "id": "prepare-grounded-input",
      "name": "Prepare Grounded Answer Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        180,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_AI_SERVICE_URL + '/grounded-chat'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { userId: $json.userId, documentId: $json.documentId, conversationId: $json.conversationId, chatRunId: $json.chatRunId, question: $json.question, chatMode: $json.chatMode, retrievedChunks: $json.retrievedChunks, conversationHistory: $json.conversationHistory, targetModel: 'gpt-5.4-mini', citations: true } }}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "generate-grounded-answer",
      "name": "Generate Grounded Answer",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        460,
        240
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$env.STUDYFLOW_DB_API_BASE + '/conversations/messages'}}",
        "sendBody": true,
        "contentType": "json",
        "jsonBody": "={{ { userId: $json.body?.userId, documentId: $json.body?.documentId, conversationId: $json.body?.conversationId, chatRunId: $json.body?.chatRunId, question: $json.body?.question, answer: $json.result?.answer || '', citations: $json.result?.citations || [], provider: $json.result?.provider || null, model: $json.result?.modelUsed || $json.result?.model || null, generationTimeMs: $json.result?.generationTimeMs || null, answeredAt: new Date().toISOString() } }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "persist-chat-turn",
      "name": "Persist Chat Turn",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        740,
        240
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { conversationId: $json.body?.conversationId, documentId: $json.body?.documentId, answer: $json.body?.answer, citations: $json.body?.citations, provider: $json.body?.provider, model: $json.body?.model, generationTimeMs: $json.body?.generationTimeMs } }}",
        "options": {}
      },
      "id": "respond-chat-answer",
      "name": "Respond With Answer",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1020,
        240
      ]
    }
  ],
  "connections": {
    "Webhook Chat Intake": {
      "main": [
        [
          {
            "node": "Validate Chat Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Chat Request": {
      "main": [
        [
          {
            "node": "Load Workspace Context",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Workspace Context": {
      "main": [
        [
          {
            "node": "Retrieve Relevant Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Retrieve Relevant Chunks": {
      "main": [
        [
          {
            "node": "Fetch Conversation History",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Conversation History": {
      "main": [
        [
          {
            "node": "Prepare Grounded Answer Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Grounded Answer Input": {
      "main": [
        [
          {
            "node": "Generate Grounded Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Grounded Answer": {
      "main": [
        [
          {
            "node": "Persist Chat Turn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Persist Chat Turn": {
      "main": [
        [
          {
            "node": "Respond With Answer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "saveDataErrorExecution": "all",
    "saveDataSuccessExecution": "all",
    "saveManualExecutions": true,
    "timezone": "UTC"
  },
  "pinData": {},
  "versionId": "1b24e0fe-f03a-4156-b227-6b51b0bbf3b1",
  "meta": {
    "templateCredsSetupCompleted": false,
    "notes": "Grounded chat workflow for StudyFlow. Answers questions using only retrieved document content and persists conversation turns."
  },
  "active": false
}
