# workflow_versions

workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 16:29:46
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:24:15.279Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threads = $('Get Thread Status (HTTP - Secure)').first().json;\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ const patients = $input.all(); const patientIds = patients.map(p => p.json.id).join(','); 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + patientIds + ')&select=id,patient_id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"801b29ed-7e23-4d56-bdcc-7038a8c35e57","versionCounter":10,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"f08ccce7-2820-4a07-a6b6-d502903e39c0","updates":{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}}}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 16:31:09
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:29:46.885Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threads = $('Get Thread Status (HTTP - Secure)').first().json;\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"1cd30268-8f8e-4215-9972-ebf0666f8941","versionCounter":12,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"format_response","updates":{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];}"}}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 16:33:10
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:31:10.081Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];}"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"1123561c-2b62-426a-a779-6cd7bf80dc1a","versionCounter":14,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"format_response","updates":{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"}}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 16:52:37
workflow_snapshot:
  {"updatedAt":"2025-11-11T20:22:11.000Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from previous node\nconst patient = $('Get Patient (HTTP - Secure)').first().json;\n\n// Get conversation threads status from the new node\nconst threads = $('Get Thread Status (HTTP - Secure)').first().json;\n\n// Create a map of approach_id -> status\nconst statusMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    statusMap[thread.approach_id] = thread.status;\n  });\n}\n\n// Transform patient_approaches to approaches array with status\nconst approaches = (patient.patient_approaches || []).map(pa => ({\n  id: pa.approaches.id,\n  name: pa.approaches.name,\n  description: pa.approaches.description || null,\n  status: statusMap[pa.approaches.id] || 'active' // default to 'active' if no thread found\n}));\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"40c74289-b145-43c4-ad15-871353a14324","versionCounter":6,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","updates":{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}}}},{"type":"updateNode","nodeId":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","updates":{"parameters":{"jsCode":"// Get patient data from previous node\nconst patient = $('Get Patient (HTTP - Secure)').first().json;\n\n// Get conversation threads from the HTTP node\nconst threads = $('Get Thread Status (HTTP - Secure)').first().json;\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    threadMap[thread.approach_id] = {\n      thread_id: thread.id,\n      thread_status: thread.status\n    };\n  });\n}\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadData = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadData) {\n    approachObj.thread_id = threadData.thread_id;\n    approachObj.thread_status = threadData.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"}}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 16:57:42
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:52:38.076Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from previous node\nconst patient = $('Get Patient (HTTP - Secure)').first().json;\n\n// Get conversation threads from the HTTP node\nconst threads = $('Get Thread Status (HTTP - Secure)').first().json;\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    threadMap[thread.approach_id] = {\n      thread_id: thread.id,\n      thread_status: thread.status\n    };\n  });\n}\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadData = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadData) {\n    approachObj.thread_id = threadData.thread_id;\n    approachObj.thread_status = threadData.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"1b5a8dab-0c51-4202-96bf-63636da158a4","versionCounter":8,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","updates":{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\nconst threadData = $('Get Thread Status (HTTP - Secure)').first().json;\n// Supabase returns array of threads\nconst threads = Array.isArray(threadData) ? threadData : [];\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"}}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 16:59:08
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:57:43.221Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\nconst threadData = $('Get Thread Status (HTTP - Secure)').first().json;\n// Supabase returns array of threads\nconst threads = Array.isArray(threadData) ? threadData : [];\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"881f4b37-7810-4455-aa52-1838b8f0d7ce","versionCounter":10,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"removePinnedData","nodeId":"GET /patients/:id"},{"type":"removePinnedData","nodeId":"GET /patients"}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 17:02:06
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:57:43.221Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\nconst threadData = $('Get Thread Status (HTTP - Secure)').first().json;\n// Supabase returns array of threads\nconst threads = Array.isArray(threadData) ? threadData : [];\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"881f4b37-7810-4455-aa52-1838b8f0d7ce","versionCounter":10,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","updates":{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"}}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 17:26:58
workflow_snapshot:
  {"updatedAt":"2025-11-12T16:33:11.237Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"ffb73f5a-ddcf-491a-b9eb-dda8d8ec3535","versionCounter":16,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeName":"Get Thread Status (HTTP - Secure)","updates":{"parameters.url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status&order=status.asc'"}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 17:27:23
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:26:58.660Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build a map: patientId -> approachId -> thread\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Store only one thread per approach (priority: active threads)\n    const existing = threadMap[thread.patient_id][thread.approach_id];\n    if (!existing || (existing.thread_status === 'archived' && thread.status === 'active')) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add thread data if it exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      });\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status&order=status.asc'","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"e0309350-44e1-41fe-abfa-8da846abbd71","versionCounter":18,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeName":"Format Response","updates":{"parameters.jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: patientId -> approachId -> array of threads\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    if (!threadMap[thread.patient_id][thread.approach_id]) {\n      threadMap[thread.patient_id][thread.approach_id] = [];\n    }\n    threadMap[thread.patient_id][thread.approach_id].push({\n      thread_id: thread.id,\n      thread_status: thread.status\n    });\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threads = threadMap[patient.id]?.[approachId] || [];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add threads array if threads exist\n        if (threads.length > 0) {\n          approachObj.threads = threads;\n        }\n        \n        return approachObj;\n      }).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 17:27:57
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:02:06.568Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"0a768895-acfd-4846-b860-1c6fd88666f1","versionCounter":12,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeName":"Get Thread Status (HTTP - Secure)","updates":{"parameters.url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status&order=status.asc'"}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 17:28:32
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:27:58.198Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> { thread_id, thread_status }\nconst threadMap = {};\nthreads.forEach(thread => {\n  threadMap[thread.approach_id] = {\n    thread_id: thread.id,\n    thread_status: thread.status\n  };\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadInfo = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Only add thread fields if thread exists\n  if (threadInfo) {\n    approachObj.thread_id = threadInfo.thread_id;\n    approachObj.thread_status = threadInfo.thread_status;\n  }\n  \n  return approachObj;\n});\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status&order=status.asc'","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"c5664d78-a882-410e-971e-f420676a7646","versionCounter":14,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeName":"Format Response","updates":{"parameters.jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> array of threads\nconst threadMap = {};\nthreads.forEach(thread => {\n  if (!threadMap[thread.approach_id]) {\n    threadMap[thread.approach_id] = [];\n  }\n  threadMap[thread.approach_id].push({\n    thread_id: thread.id,\n    thread_status: thread.status\n  });\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threads = threadMap[approachId] || [];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Add threads array if threads exist\n  if (threads.length > 0) {\n    approachObj.threads = threads;\n  }\n  \n  return approachObj;\n}).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 17:40:24
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:27:23.944Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: patientId -> approachId -> array of threads\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    if (!threadMap[thread.patient_id][thread.approach_id]) {\n      threadMap[thread.patient_id][thread.approach_id] = [];\n    }\n    threadMap[thread.patient_id][thread.approach_id].push({\n      thread_id: thread.id,\n      thread_status: thread.status\n    });\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threads = threadMap[patient.id]?.[approachId] || [];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add threads array if threads exist\n        if (threads.length > 0) {\n          approachObj.threads = threads;\n        }\n        \n        return approachObj;\n      }).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $input.all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status&order=status.asc'","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"50b4f674-5957-4d03-b5d9-22d4d0893abd","versionCounter":20,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"f08ccce7-2820-4a07-a6b6-d502903e39c0","updates":{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $('Get Patients (HTTP)').all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status&order=status.asc' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}}}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 17:40:52
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:28:34.173Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> array of threads\nconst threadMap = {};\nthreads.forEach(thread => {\n  if (!threadMap[thread.approach_id]) {\n    threadMap[thread.approach_id] = [];\n  }\n  threadMap[thread.approach_id].push({\n    thread_id: thread.id,\n    thread_status: thread.status\n  });\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threads = threadMap[approachId] || [];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Add threads array if threads exist\n  if (threads.length > 0) {\n    approachObj.threads = threads;\n  }\n  \n  return approachObj;\n}).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"='https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status&order=status.asc'","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"ca0637e1-337b-47e1-ad65-2942c3ca0a4f","versionCounter":16,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","updates":{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status&order=status.asc' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}}}}]


workflow_id: bP0W2MsqeSbHYIXE
workflow_name: GET /patients - List All Patients
trigger: partial_update
created_at: 2025-11-12 18:07:19
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:40:24.391Z","createdAt":"2025-10-17T15:12:23.346Z","id":"bP0W2MsqeSbHYIXE","name":"GET /patients - List All Patients","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients","responseMode":"responseNode","options":{}},"id":"webhook_trigger","name":"GET /patients","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[176,368],"webhookId":"2408796d-487b-4f69-8d3c-39aedd07c7f4","onError":"continueRegularOutput"},{"parameters":{"jsCode":"const webhookData = $('GET /patients').item.json;\nconst statusParam = webhookData.query?.status || 'active';\n\n// If status is 'all', don't filter by status\nif (statusParam === 'all') {\n  return [{\n    filter_by_status: false\n  }];\n}\n\nreturn [{\n  status: statusParam,\n  filter_by_status: true\n}];"},"id":"parse_query_params","name":"Parse Query Parameters","type":"n8n-nodes-base.code","typeVersion":2,"position":[1200,448],"onError":"continueRegularOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?select=*,patient_approaches(approach_id,approaches(id,name,description))' + ($json.status ? '&status=eq.' + $json.status : '') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"get_patients","name":"Get Patients (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,448],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"empty_check","leftValue":"={{ Array.isArray($json) ? $json.length === 0 : (Object.keys($json).length === 0 || $json === null || $json === undefined) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"check_empty_results","name":"Check If Patients Found","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2080,416],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: true,\n  data: [],\n  message: 'No patients found',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: 0\n  }\n} }}","options":{}},"id":"empty_response","name":"Empty Results Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2560,352],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: patientId -> approachId -> array of threads\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    if (!threadMap[thread.patient_id][thread.approach_id]) {\n      threadMap[thread.patient_id][thread.approach_id] = [];\n    }\n    threadMap[thread.patient_id][thread.approach_id].push({\n      thread_id: thread.id,\n      thread_status: thread.status\n    });\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threads = threadMap[patient.id]?.[approachId] || [];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add threads array if threads exist\n        if (threads.length > 0) {\n          approachObj.threads = threads;\n        }\n        \n        return approachObj;\n      }).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"},"id":"format_response","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2560,560],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"success_response","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2784,560],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients**\n\nOdbiera żądanie GET do pobrania listy wszystkich pacjentów użytkownika.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients\n\n**Parametry zapytania:**\n• status (opcjonalny): 'active' | 'archived' (domyślnie 'active')\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** Dane z triggera → JWT Validation","height":860,"width":320,"color":2},"id":"03c0cab3-8ef1-4591-a0a2-68f09476da71","name":"📝 Dokumentacja: Webhook Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[96,-128]},{"parameters":{"content":"**🔧 Przetwarzanie Parametrów**\n\nParsuje parametry zapytania i przygotowuje filtr dla bazy danych.\n\n**Logika:**\n```javascript\nconst status = query.status || 'active';\nreturn {\n  userId: userData.user_id,\n  status: status\n};\n```\n\n**Domyślnie:** Zwraca tylko aktywnych pacjentów\n\n**Zwraca:** userId, status → Get Patients","height":864,"width":320,"color":3},"id":"03484625-3a56-4b62-b01e-07cfae522c92","name":"📝 Dokumentacja: Query Processing","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1104,-128]},{"parameters":{"content":"**💾 Pobieranie Pacjentów z Bazy**\n\nWykonuje zapytanie do Supabase REST API z JWT token.\n\n**Method:** GET\n**URL:** /rest/v1/patients\n**Headers:**\n• Authorization: User JWT token (enforces RLS)\n• apikey: Supabase anon key\n\n**Query:**\n• select=*\n• status=eq.{{status}} (optional)\n\n**RLS:** Automatycznie filtruje do pacjentów użytkownika przez JWT token\n\n**ZMIANA (2025-10-23):** Zastąpiono Supabase node → HTTP Request\n**Powód:** Supabase node używał service role key, który omijał RLS\n\n**Zwraca:** Array pacjentów (tylko użytkownika!)","height":864,"width":320,"color":7},"id":"3d7f520e-0a18-471d-9f07-3213b428a9dc","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1440,-128]},{"parameters":{"content":"**✅ Sprawdzenie Wyniku**\n\nWeryfikuje czy znaleziono pacjentów.\n\n**Warunek:**\n• Output 0 (TRUE): Brak pacjentów → Empty Results Response\n• Output 1 (FALSE): Pacjenci znalezieni → Format Response\n\n**Logika:**\n```javascript\n$json.length === 0\n```\n\n**Zwraca:** Rozgałęzienie na podstawie wyniku","height":848,"width":320,"color":3},"id":"1480b847-b720-4103-9ae4-05fa8cc83e3b","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1776,-112]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Empty Results Response (200):**\n```json\n{\n  \"patients\": [],\n  \"count\": 0\n}\n```\n\n**Format Response:**\nFormatuje dane pacjentów:\n• id, name, pseudonym, age, status\n• approaches: [{id, name}]\n• created_at, updated_at\n\n**Success Response (200):**\n```json\n{\n  \"patients\": [...],\n  \"count\": N\n}\n```","height":848,"width":648,"color":4},"id":"f4fb7958-0241-48b1-9e4f-1bca2b6b4375","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2128,-112]},{"parameters":{"content":"**🔐 Walidacja JWT i Autoryzacja**\n\nSprawdza ważność tokenu JWT i wydobywa dane użytkownika.\n\n**Sub-workflow:** JWT Validation Middleware (dL4gKHrO0Mtdhpmo)\n\n**Przepływ:**\n1. JWT Validation1 → sprawdza token\n2. Check JWT Error → weryfikuje błędy\n3. Jeśli błąd JWT → JWT Error Response (401)\n4. Jeśli auth error → Return Error (500)\n5. Jeśli sukces → Validate & Prepare Update\n\n**Output 0 (sukces):** userId, user data\n**Output 1 (błąd):** Error details\n\n**RLS:** Automatyczna weryfikacja własności pacjenta (user_id)","height":840,"width":736,"color":7},"id":"b742bdae-19cf-49a2-8694-6d3e5d5867d6","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[416,-160]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"2a748c00-2892-470f-9df5-6274b7202c03","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[688,336]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"342d6a34-6d71-4e49-a534-bea579fae1fb","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[880,224]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"00b9aaec-4e74-4669-bc56-4b5b7386ad08","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[688,544]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"5597ab35-f0f4-4379-8ece-319e8b9a65a0","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[512,432],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=in.(' + $('Get Patients (HTTP)').all().map(p => p.json.id).join(',') + ')&select=id,patient_id,approach_id,status&order=status.asc' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.3,"position":[1728,448],"id":"f08ccce7-2820-4a07-a6b6-d502903e39c0","name":"Get Thread Status (HTTP - Secure)"}],"connections":{"GET /patients":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Parse Query Parameters":{"main":[[{"node":"Get Patients (HTTP)","type":"main","index":0}]]},"Get Patients (HTTP)":{"main":[[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Check If Patients Found":{"main":[[{"node":"Empty Results Response","type":"main","index":0}],[{"node":"Format Response","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Parse Query Parameters","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Check If Patients Found","type":"main","index":0}]]}},"settings":{"saveExecutionProgress":true,"saveManualExecutions":true,"saveDataErrorExecution":"all","saveDataSuccessExecution":"all","executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzE4NjE5LCJpYXQiOjE3NjA3MTUwMTksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.OUDcRGH7591nG8JH280JjvCQiHsT73A8mC80lGH_OVM","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}]},"versionId":"06a2a641-9cbf-47d9-b25e-5ce98c4681f0","versionCounter":22,"triggerCount":1,"shared":[{"updatedAt":"2025-10-17T15:12:23.349Z","createdAt":"2025-10-17T15:12:23.349Z","role":"workflow:owner","workflowId":"bP0W2MsqeSbHYIXE","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"format_response","updates":{"parameters":{"jsCode":"const patients = $('Get Patients (HTTP)').all();\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: patientId -> approachId -> single thread object\n// Since HTTP request has order=status.asc, first thread is active if exists\nconst threadMap = {};\nif (Array.isArray(threads)) {\n  threads.forEach(thread => {\n    if (!threadMap[thread.patient_id]) {\n      threadMap[thread.patient_id] = {};\n    }\n    // Only store FIRST thread for each patient/approach combination\n    // This implements \"one thread per approach\" rule with active preference\n    if (!threadMap[thread.patient_id][thread.approach_id]) {\n      threadMap[thread.patient_id][thread.approach_id] = {\n        thread_id: thread.id,\n        thread_status: thread.status\n      };\n    }\n  });\n}\n\nreturn [{\n  success: true,\n  data: patients.map(p => {\n    const patient = p.json;\n    let approaches = [];\n    \n    if (patient.patient_approaches && Array.isArray(patient.patient_approaches)) {\n      approaches = patient.patient_approaches.map(pa => {\n        const approachId = pa.approaches.id;\n        const threadData = threadMap[patient.id]?.[approachId];\n        \n        const approachObj = {\n          id: approachId,\n          name: pa.approaches.name,\n          description: pa.approaches.description\n        };\n        \n        // Add flat thread fields if thread exists\n        if (threadData) {\n          approachObj.thread_id = threadData.thread_id;\n          approachObj.thread_status = threadData.thread_status;\n        }\n        \n        return approachObj;\n      }).filter(approach => approach.thread_id); // Only show approaches with a thread\n    }\n    \n    if (patient.patient_approaches) {\n      delete patient.patient_approaches;\n    }\n    \n    return {\n      ...patient,\n      approaches: approaches\n    };\n  }),\n  message: 'Patients retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString(),\n    count: patients.length\n  }\n}];"}}}]


workflow_id: OEf9HuEXwqAeXZyk
workflow_name: GET /patients/:id - Get Patient Details
trigger: partial_update
created_at: 2025-11-12 18:08:00
workflow_snapshot:
  {"updatedAt":"2025-11-12T17:40:52.366Z","createdAt":"2025-10-16T14:59:35.676Z","id":"OEf9HuEXwqAeXZyk","name":"GET /patients/:id - Get Patient Details","active":true,"isArchived":false,"nodes":[{"parameters":{"path":"patients/:patient_id","responseMode":"responseNode","options":{"responseHeaders":{"entries":[{"name":"Access-Control-Allow-Methods","value":"GET, OPTIONS"},{"name":"Access-Control-Allow-Headers","value":"Content-Type, Authorization"},{"name":"Access-Control-Max-Age","value":"86400"},{"name":"X-Content-Type-Options","value":"nosniff"},{"name":"X-Frame-Options","value":"DENY"},{"name":"Strict-Transport-Security","value":"max-age=31536000; includeSubDomains"}]}}},"id":"webhook_get_patients","name":"GET /patients/:id","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[304,592],"webhookId":"631ffa3b-9085-43f9-96da-ce4e1262ee3a","onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get webhook data and JWT data\nconst webhookData = $('GET /patients/:id').item.json;\nconst jwtData = $input.item.json;\n\n// Extract patient_id from params\nconst patientId = webhookData.params.patient_id;\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n// Validate patient ID exists\nif (!patientId) {\n  throw new Error('Patient ID is required');\n}\n\n// Validate UUID format\nif (!uuidRegex.test(patientId)) {\n  throw new Error('Invalid patient ID format');\n}\n\n// Return validated data\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: patientId\n}];"},"id":"5641f7d1-d7e8-4570-848a-c970e8f88a67","name":"Validate Patient ID","type":"n8n-nodes-base.code","typeVersion":2,"position":[1264,608],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"0","leftValue":"={{ $json.error }}","rightValue":"={{ true }}","operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"d352fefb-8321-4830-bca8-c51747a9ec80","name":"Check Database Error","type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2416,688],"onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"DATABASE_ERROR\",\n    \"message\": \"Failed to retrieve patient\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{}},"id":"40ff0049-b72c-4a2b-a7a3-8f2c8b909bd8","name":"Database Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2720,80],"onError":"continueRegularOutput"},{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> array of threads\nconst threadMap = {};\nthreads.forEach(thread => {\n  if (!threadMap[thread.approach_id]) {\n    threadMap[thread.approach_id] = [];\n  }\n  threadMap[thread.approach_id].push({\n    thread_id: thread.id,\n    thread_status: thread.status\n  });\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threads = threadMap[approachId] || [];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Add threads array if threads exist\n  if (threads.length > 0) {\n    approachObj.threads = threads;\n  }\n  \n  return approachObj;\n}).filter(approach => approach.threads && approach.threads.length > 0); // Filter out approaches with no threads\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"},"id":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[2816,976],"onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={{ JSON.stringify($json) }}","options":{}},"id":"8157a5cc-9d3c-4166-ab99-ae8ce53a885b","name":"Success Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[3024,976],"onError":"continueRegularOutput"},{"parameters":{"conditions":{"options":{"version":2,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"conditions":[{"id":"check_empty","leftValue":"={{ $json.id }}","rightValue":"","operator":{"type":"string","operation":"notExists"}}],"combinator":"and"},"options":{}},"type":"n8n-nodes-base.if","typeVersion":2.2,"position":[2192,672],"id":"129716d0-fe33-4adb-abf4-80236822a468","name":"Check If Patient Found","onError":"continueErrorOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Patient not found\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":404}},"type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[2416,512],"id":"0ddcdc75-6a27-41fe-8e77-2a22aa3ebed6","name":"Patient Not Found Response","onError":"continueRegularOutput"},{"parameters":{"respondWith":"json","responseBody":"={\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INVALID_INPUT\",\n    \"message\": \"{{ $json.error?.message || 'Invalid patient ID' }}\"\n  },\n  \"metadata\": {\n    \"timestamp\": \"{{ new Date().toISOString() }}\"\n  }\n}","options":{"responseCode":400}},"id":"97dab3bb-e3ac-4fcd-a670-7f31cb1696f3","name":"Validation Error Response","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[1408,752],"onError":"continueRegularOutput"},{"parameters":{"content":"**🌐 Punkt Wejścia - GET /patients/:id**\n\nOdbiera żądanie GET do pobrania szczegółów jednego pacjenta.\n\n**Endpoint:** GET /webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:id\n\n**Path Parameters:**\n• id: UUID pacjenta (wymagane)\n\n**Autoryzacja:** Bearer token (JWT)\n\n**Zwraca:** patient_id → JWT Validation","height":588,"width":360,"color":2},"id":"995024a1-abef-4e9f-ba9e-ff8b28bf8b12","name":"📝 Dokumentacja: Trigger","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[192,272]},{"parameters":{"content":"**🔐 Walidacja JWT**\n\nSprawdza ważność tokenu i pobiera user_id.\n\n**Sub-workflow:** JWT Validation Middleware\n\n**Proces:**\n1. JWT Validation1 → walidacja\n2. Check JWT Error → sprawdzenie błędów\n3. Błąd → JWT Error Response (401)\n4. Auth error → Return Error (500)\n5. Sukces → Validate Patient ID\n\n**Zwraca:** user_id lub error","height":588,"width":612,"color":7},"id":"81cf0cdd-b102-4886-baf0-1aa14612bc60","name":"📝 Dokumentacja: JWT Auth","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[576,272]},{"parameters":{"content":"**✅ Walidacja UUID Pacjenta**\n\nSprawdza czy patient_id ma poprawny format UUID.\n\n**Walidacja:**\n```javascript\nconst uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n```\n\n**Output:**\n• Output 0 (valid): UUID poprawny → Get Patient\n• Output 1 (invalid): Błąd → Validation Error Response (400)\n\n**Zwraca:** patient_id i user_id","height":596,"width":428,"color":3},"id":"ee8658a7-851d-47cc-9480-03cece07b481","name":"📝 Dokumentacja: ID Validation","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1200,272]},{"parameters":{"content":"**💾 Pobranie Danych Pacjenta**\n\nWykonuje zapytanie do tabeli patients.\n\n**Credentials:** Supabase production\n\n**Operation:** Get\n**Table:** patients\n**Filters:**\n• id = {{patient_id}}\n• user_id = {{user_id}}\n\n**Include:** patient_approaches.approaches\n\n**RLS:** Tylko pacjenci własnego użytkownika\n\n**Zwraca:** Obiekt pacjenta lub pusty","height":596,"width":292,"color":7},"id":"2b6f4354-5cd1-436f-98c0-518a254f6507","name":"📝 Dokumentacja: Database Query","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1632,272]},{"parameters":{"content":"**🔍 Sprawdzenie Wyniku**\n\nWeryfikuje czy pacjent został znaleziony.\n\n**Check If Patient Found:**\n• TRUE: Nie znaleziono → Patient Not Found (404)\n• FALSE: Znaleziono → Check Database Error\n\n**Check Database Error:**\n• TRUE: Błąd bazy → Database Error Response (500)\n• FALSE: OK → Format Response\n\n**Zwraca:** Dane pacjenta lub error","height":604,"width":596,"color":3},"id":"8bab4966-b72f-41e6-b0a1-481927d82a82","name":"📝 Dokumentacja: Result Check","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[1936,272]},{"parameters":{"content":"**✨ Odpowiedzi HTTP**\n\n**Format Response:**\nFormatuje obiekt pacjenta:\n```javascript\n{\n  id, name, pseudonym, age, status,\n  approaches: [{id, name, description}],\n  created_at, updated_at\n}\n```\n\n**Success Response (200):**\nZwraca pełne dane pacjenta z podejściami.\n\n**Inne odpowiedzi:**\n• 404: Patient Not Found\n• 400: Invalid UUID\n• 401: Unauthorized\n• 500: Database Error","height":600,"width":420,"color":4},"id":"9bbe0bf3-3409-4cd8-97a7-500b7e0c6963","name":"📝 Dokumentacja: Responses","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2560,-336]},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/patients?id=eq.' + $json.patient_id + '&select=*,patient_approaches(approaches(*))' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"ec9b64a9-f9d1-468b-8842-e43e9ab79644","name":"Get Patient (HTTP - Secure)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1696,704],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"cfc2af6f-08d2-41bd-80e3-baff47c36541","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[848,528]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"ca0e26c2-3ac2-42a4-bb2a-1d1a0dd54cf3","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[1040,416]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"437696f4-433a-4c66-b575-0215de7d27da","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[848,736]},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"workflowInputs":{"mappingMode":"defineBelow","value":{"headers":"={{ $json.headers }}","body":"={{ $json.body }}"},"matchingColumns":[],"schema":[{"id":"headers","displayName":"headers","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object"},{"id":"body","displayName":"body","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true},{"id":"params","displayName":"params","required":false,"defaultMatch":false,"display":true,"canBeUsedToMatch":true,"type":"object","removed":true}],"attemptToConvertTypes":false,"convertFieldsToString":true},"options":{}},"id":"bcb04ebb-ae6b-432c-aede-c505d89f0fa8","name":"JWT Validation1","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.3,"position":[672,624],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?patient_id=eq.' + $('Validate Patient ID').first().json.patient_id + '&select=id,approach_id,status&order=status.asc' }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('GET /patients/:id').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjAzMzM0NjYsImV4cCI6MjA3NTkwOTQ2Nn0.gVvLw4aHwKG3TFhoQv6MwvoennmFVgmbLTOOpZLBQO8"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[2640,976],"id":"097ee70b-4998-41dd-a3e8-f32b7a0f2d43","name":"Get Thread Status (HTTP - Secure)"},{"parameters":{"content":"**📊 Wzbogacenie o Status Podejść**\n\nDodaje pole `status` do każdego podejścia.\n\n**Get Thread Status (HTTP - Secure):**\nPobiera status z conversation_threads:\n```\nGET /conversation_threads\n  ?patient_id=eq.{id}\n  &select=approach_id,status\n```\n\n**Format Response:**\nŁączy dane używając statusMap:\n```javascript\nconst statusMap = {};\nthreads.forEach(thread => {\n  statusMap[thread.approach_id] = thread.status;\n});\n\napproaches.map(pa => ({\n  ...pa,\n  status: statusMap[pa.id] || 'active'\n}));\n```\n\n**Status Logic:**\n• 'active': Thread aktywny lub brak threadu\n• 'archived': Thread zarchiwizowany\n\n**Result:** Frontend otrzymuje status dla każdego podejścia","height":872,"width":420,"color":5},"id":"status_enrichment_doc","name":"📝 Dokumentacja: Status Enrichment","type":"n8n-nodes-base.stickyNote","typeVersion":1,"position":[2576,304]}],"connections":{"GET /patients/:id":{"main":[[{"node":"JWT Validation1","type":"main","index":0}]]},"Validate Patient ID":{"main":[[{"node":"Get Patient (HTTP - Secure)","type":"main","index":0}],[{"node":"Validation Error Response","type":"main","index":0}]]},"Get Patient (HTTP - Secure)":{"main":[[{"node":"Check If Patient Found","type":"main","index":0}]]},"Check If Patient Found":{"main":[[{"node":"Patient Not Found Response","type":"main","index":0}],[{"node":"Check Database Error","type":"main","index":0}]]},"Check Database Error":{"main":[[{"node":"Database Error Response","type":"main","index":0}],[{"node":"Get Thread Status (HTTP - Secure)","type":"main","index":0}]]},"Format Response":{"main":[[{"node":"Success Response","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Validate Patient ID","type":"main","index":0}]]},"JWT Validation1":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Get Thread Status (HTTP - Secure)":{"main":[[{"node":"Format Response","type":"main","index":0}]]}},"settings":{"callerPolicy":"workflowsFromSameOwner","executionOrder":"v0","availableInMCP":false,"errorWorkflow":"SR7ikNdko60G24uG"},"staticData":null,"meta":null,"pinData":{"GET /patients":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"axios/1.12.2","accept":"application/json, text/plain, */*","accept-encoding":"gzip, compress, deflate, br","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzA3Mjg5LCJpYXQiOjE3NjA3MDM2ODksImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDcwMzY4OX1dLCJzZXNzaW9uX2lkIjoiYjNjZDMzMTUtNGZjYy00NTg1LTg4YjktMjY3MmUxNmRmODJjIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.vB2l4fLQlq2ggAhAkZ96ILL_dcV9cwVg-3pMz0cn0Ug","content-type":"application/json","via":"1.1 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https"},"params":{},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/patients","executionMode":"production"}}],"GET /patients/:id":[{"json":{"headers":{"host":"n8n.wooapp.eu","user-agent":"curl/8.7.1","accept":"*/*","authorization":"Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6ImgxRVRGRCtIYTBVN3MyNmciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL3h4Y2l3bnRobm55d2FuYnBscXdnLnN1cGFiYXNlLmNvL2F1dGgvdjEiLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYiLCJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNzYwNzc4NDIzLCJpYXQiOjE3NjA3NzQ4MjMsImVtYWlsIjoicm9tdWFsZEBhaWFkdmlzb3JzLnBsIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJlbWFpbCIsInByb3ZpZGVycyI6WyJlbWFpbCJdfSwidXNlcl9tZXRhZGF0YSI6eyJlbWFpbCI6InJvbXVhbGRAYWlhZHZpc29ycy5wbCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpbnN0cnVjdGlvbnNfdmlld2VkIjpmYWxzZSwicGhvbmVfdmVyaWZpZWQiOmZhbHNlLCJzdWIiOiI5M2M0NzAxNi0wY2FmLTQ1NjYtYWFkZS0yMDA4OGIyM2RhMTYifSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQiLCJhYWwiOiJhYWwxIiwiYW1yIjpbeyJtZXRob2QiOiJwYXNzd29yZCIsInRpbWVzdGFtcCI6MTc2MDc3NDgyM31dLCJzZXNzaW9uX2lkIjoiNmJiZTc4ODEtNjIwZi00MmE0LWEwNDgtYjNlMDVjY2QyZjAyIiwiaXNfYW5vbnltb3VzIjpmYWxzZX0.aUEQCO1fkmOaEMjBgIspfVfkWRqR2fUyi1zggLyVNZg","via":"2.0 Caddy","x-forwarded-for":"87.207.177.66","x-forwarded-host":"n8n.wooapp.eu","x-forwarded-proto":"https","accept-encoding":"gzip"},"params":{"patient_id":"ae275cb1-b953-4d8a-9f49-d8faf8bd06c8"},"query":{},"body":{},"webhookUrl":"https://n8n.wooapp.eu/webhook/631ffa3b-9085-43f9-96da-ce4e1262ee3a/patients/:patient_id","executionMode":"production"}}]},"versionId":"732dbdb9-0423-47c9-b1f9-db5b3d3db773","versionCounter":18,"triggerCount":1,"shared":[{"updatedAt":"2025-10-16T14:59:35.678Z","createdAt":"2025-10-16T14:59:35.678Z","role":"workflow:owner","workflowId":"OEf9HuEXwqAeXZyk","projectId":"sVGwG1Ss6aXKNorC","project":{"updatedAt":"2025-07-30T12:20:06.951Z","createdAt":"2025-07-30T12:18:11.485Z","id":"sVGwG1Ss6aXKNorC","name":"Rafał Dębski <rdebski90@gmail.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-07-30T12:18:11.486Z","createdAt":"2025-07-30T12:18:11.486Z","userId":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","projectId":"sVGwG1Ss6aXKNorC","user":{"updatedAt":"2025-11-11T23:00:41.000Z","createdAt":"2025-07-30T12:18:10.851Z","id":"512c6fc4-ef1e-46ec-99da-b435fcbf2339","email":"rdebski90@gmail.com","firstName":"Rafał","lastName":"Dębski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-07-30T12:20:09.619Z","personalization_survey_n8n_version":"1.104.1"},"settings":{"userActivated":true,"easyAIWorkflowOnboarded":true,"firstSuccessfulWorkflowId":"gp796pP6j8IFZodb","userActivatedAt":1753881412699,"npsSurvey":{"responded":true,"lastShownAt":1755785806897},"dismissedCallouts":{"ragStarterCallout":true}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-11","isPending":false}}]}}],"tags":[{"updatedAt":"2025-10-15T09:41:37.430Z","createdAt":"2025-10-15T09:41:37.430Z","id":"ElqeP6a9h0QgrOdz","name":"documented"},{"updatedAt":"2025-11-11T20:13:17.394Z","createdAt":"2025-11-11T20:13:17.394Z","id":"H9O9hTnua4TEP227","name":"error_handler"},{"updatedAt":"2025-10-15T08:59:55.157Z","createdAt":"2025-10-15T08:59:55.157Z","id":"IakxFnVI8xqZDQZE","name":"tested"},{"updatedAt":"2025-10-15T08:03:11.202Z","createdAt":"2025-10-15T08:03:11.202Z","id":"imoCmf4e9O7UEhbR","name":"reviewed"}]}

operations:
  [{"type":"updateNode","nodeId":"b1b6f7d0-06d5-4362-a084-546c72a7dc89","updates":{"parameters":{"jsCode":"// Get patient data from Get Patient HTTP node\nconst patientData = $('Get Patient (HTTP - Secure)').first().json;\n// Supabase returns array even for single patient query\nconst patient = Array.isArray(patientData) ? patientData[0] : patientData;\n\n// Get thread data from Get Thread Status HTTP node\n// Important: .all() returns all items, each item.json contains one thread\nconst threadItems = $('Get Thread Status (HTTP - Secure)').all();\nconst threads = threadItems.map(item => item.json);\n\n// Build threadMap: approach_id -> single thread object\n// Since HTTP request has order=status.asc, first thread is active if exists\nconst threadMap = {};\nthreads.forEach(thread => {\n  // Only store FIRST thread for each approach\n  // This implements \"one thread per approach\" rule with active preference\n  if (!threadMap[thread.approach_id]) {\n    threadMap[thread.approach_id] = {\n      thread_id: thread.id,\n      thread_status: thread.status\n    };\n  }\n});\n\n// Transform patient_approaches to approaches array with thread enrichment\nconst approaches = (patient.patient_approaches || []).map(pa => {\n  const approachId = pa.approaches.id;\n  const threadData = threadMap[approachId];\n  \n  const approachObj = {\n    id: approachId,\n    name: pa.approaches.name,\n    description: pa.approaches.description || null\n  };\n  \n  // Add flat thread fields if thread exists\n  if (threadData) {\n    approachObj.thread_id = threadData.thread_id;\n    approachObj.thread_status = threadData.thread_status;\n  }\n  \n  return approachObj;\n}).filter(approach => approach.thread_id); // Only show approaches with a thread\n\nreturn [{\n  success: true,\n  data: {\n    id: patient.id,\n    name: patient.name,\n    pseudonym: patient.pseudonym || null,\n    age: patient.age || null,\n    status: patient.status,\n    approaches: approaches,\n    created_at: patient.created_at,\n    updated_at: patient.updated_at,\n    notes: patient.notes || null\n  },\n  message: 'Patient retrieved successfully',\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n}];"}}}]


workflow_id: 019Vrw56aROeEzVj
workflow_name: WOO_Workflow_21_POST_Chat_Send_AI_Agent
trigger: partial_update
created_at: 2025-11-13 09:02:53
workflow_snapshot:
  {"updatedAt":"2025-10-23T22:20:04.000Z","createdAt":"2025-10-23T21:31:02.360Z","id":"019Vrw56aROeEzVj","name":"WOO_Workflow_21_POST_Chat_Send_AI_Agent","active":false,"isArchived":true,"nodes":[{"parameters":{"httpMethod":"POST","path":"chat/send","responseMode":"responseNode","options":{}},"id":"2483395f-9d90-4105-8401-a64558f7a177","name":"POST /chat/send","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[-672,192],"webhookId":"b6bdd103-53ee-4fe3-b2c9-6bc89efcabf1","onError":"continueRegularOutput","notes":"Testing fix for issue #357 - AI node validation"},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"options":{}},"id":"e72c8395-ba13-4196-bc0b-e5b5bf31d874","name":"JWT Validation","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[-448,192],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"aeb8f7d7-4442-4adb-b707-5093f70e64af","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[-224,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"92454687-d663-47db-903f-5add6c3685fc","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[0,0]},{"parameters":{"jsCode":"const webhookData = $('POST /chat/send').item.json;\nconst jwtData = $('JWT Validation').item.json;\n\n// Extract request body\nconst body = webhookData.body || {};\n\n// Validate required fields\nif (!body.approach_id) {\n  throw new Error('Missing required field: approach_id');\n}\n\nif (!body.message && !body.file_url) {\n  throw new Error('Either message or file_url is required');\n}\n\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: body.patient_id || null,\n  approach_id: body.approach_id,\n  tool_id: body.tool_id || null,\n  message: body.message || '',\n  file_url: body.file_url || null,\n  file_type: body.file_type || null\n}];"},"id":"06c62c44-6679-4a44-8520-7b1417b43127","name":"Extract Request Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[0,192],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?user_id=eq.' + $json.user_id + '&approach_id=eq.' + $json.approach_id + '&status=eq.active' + ($json.patient_id ? '&patient_id=eq.' + $json.patient_id : '&patient_id=is.null') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('POST /chat/send').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjk1MTMwODgsImV4cCI6MjA0NTA4OTA4OH0.8DKKCeQxH6nJTFDNlOaP5yLMGufqMZ_4GNwwFgDY0xY"},{"name":"Prefer","value":"return=representation"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"7c506a85-08f1-46c8-ab0b-6325c7179a18","name":"Lookup Thread (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[224,96],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.length === 0 }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"b992f141-590d-4e45-82cd-b90030b1893a","name":"Thread Not Found?","type":"n8n-nodes-base.if","typeVersion":2,"position":[448,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Conversation thread not found. Please assign an approach to the patient first.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":404}},"id":"0bb72000-9ce2-4958-9a58-7f219bc468de","name":"404 Thread Not Found","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[672,0]},{"parameters":{"jsCode":"const requestData = $('Extract Request Data').item.json;\nconst threadArray = $json;\n\nif (!Array.isArray(threadArray) || threadArray.length === 0) {\n  throw new Error('Thread not found');\n}\n\nconst thread = threadArray[0];\n\nreturn [{\n  ...requestData,\n  thread_id: thread.id,\n  thread_status: thread.status\n}];"},"id":"60659c3c-6a7f-4194-8152-40ba58df061f","name":"Extract Thread Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[672,192]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.file_url !== null }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"f531e25f-be91-47ab-9846-bd8324b1deed","name":"Has File?","type":"n8n-nodes-base.if","typeVersion":2,"position":[896,400]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/m4a'].includes($json.file_type) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"9e808b9b-e62c-4442-a25a-fe7761e71c09","name":"Is Audio?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1120,112]},{"parameters":{"workflowId":"QmcrF997iWDYX2I1","options":{}},"id":"7d978fe6-b05a-47f3-99f6-10edb38c5a49","name":"Process Audio (Whisper)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,16]},{"parameters":{"workflowId":"QbArI0hmMn8HNcft","options":{}},"id":"a17551b4-da00-41a4-acea-ab376a639aa7","name":"Process PDF","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,208]},{"parameters":{"mode":"combine","options":{}},"id":"8bce12d5-4b54-452e-88f0-26f780a5b813","name":"Merge File Results","type":"n8n-nodes-base.merge","typeVersion":3,"position":[1568,112]},{"parameters":{"jsCode":"const data = $json;\n\n// Build user message: original message + transcription if available\nlet userMessage = data.message || '';\n\nif (data.transcription) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Transkrypcja nagrania audio:\\n' + data.transcription;\n}\n\nif (data.pdf_text) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Treść dokumentu PDF:\\n' + data.pdf_text;\n}\n\nif (!userMessage) {\n  throw new Error('No message content available after file processing');\n}\n\nreturn [{\n  approach_id: data.approach_id,\n  tool_id: data.tool_id,\n  patient_id: data.patient_id,\n  thread_id: data.thread_id,\n  user_message: userMessage\n}];"},"id":"37cb7f4d-1b33-4e93-a9e7-a8712aca22cb","name":"Prepare Context Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[1776,416]},{"parameters":{"workflowId":"IFNsqMYJhHQwo5Iz","options":{}},"id":"083ec9a4-b6c1-4ace-a25c-4d2aac5a9f71","name":"Build Context (System Prompt)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[2016,192]},{"parameters":{"jsCode":"const contextResult = $json;\nconst preparedData = $('Prepare Context Inputs').item.json;\n\nreturn [{\n  thread_id: preparedData.thread_id,\n  approach_id: preparedData.approach_id,\n  system_prompt: contextResult.data.system_prompt,\n  user_message: preparedData.user_message\n}];"},"id":"322a948b-8689-46eb-aa60-c78e24b2b40f","name":"Prepare AI Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[2240,192]},{"parameters":{"promptType":"define","text":"={{ $json.user_message }}","options":{"systemMessage":"={{ $json.system_prompt }}"}},"id":"94f34ea7-515e-449b-9f24-63c18e976b46","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","typeVersion":1.7,"position":[2512,192],"onError":"continueErrorOutput"},{"parameters":{"model":"gpt-5-mini","options":{"maxTokens":2000,"temperature":0.7}},"id":"202f55ef-3036-4219-ae99-0c2f74d4c2ce","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1,"position":[2464,416]},{"parameters":{"sessionIdType":"customKey","sessionKey":"={{ $('Prepare AI Inputs').item.json.thread_id }}","tableName":"conversation_messages","contextWindowLength":10},"id":"b73dff16-7df3-4254-a0f3-878a777ac158","name":"Postgres Chat Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","typeVersion":1.1,"position":[2592,416]},{"parameters":{"model":"text-embedding-3-small","options":{}},"id":"44371076-72c7-4ad1-9b81-1699163bc87b","name":"Embeddings OpenAI","type":"@n8n/n8n-nodes-langchain.embeddingsOpenAi","typeVersion":1,"position":[2800,624]},{"parameters":{"jsCode":"const aiResponse = $json.output;\nconst threadData = $('Prepare AI Inputs').item.json;\n\nreturn [{\n  success: true,\n  data: {\n    message: aiResponse,\n    thread_id: threadData.thread_id\n  },\n  metadata: {\n    timestamp: new Date().toISOString(),\n    model: 'gpt-4o-mini',\n    has_rag: true\n  }\n}];"},"id":"aa183cd6-85ee-4131-8304-28fdb39096fd","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[3088,96]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{"responseCode":200}},"id":"65a32b1b-b6f1-4c2d-a5fd-f42852f9f1d0","name":"200 Success","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3312,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": $json.message || \"Invalid request data\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":400}},"id":"c369b5f9-8151-4c42-863d-9571fc99b18c","name":"400 Bad Request","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[224,288]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INTERNAL_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString(),\n    \"error_details\": $json.message\n  }\n} }}","options":{"responseCode":500}},"id":"b119071e-b9b8-493f-bfe8-097f5bddc123","name":"500 Internal Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3088,288]},{"parameters":{"mode":"retrieve-as-tool","toolDescription":"Vector store","tableName":{"__rl":true,"value":"documents","mode":"list","cachedResultName":"documents"},"options":{}},"type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","typeVersion":1.3,"position":[2720,416],"id":"c996d945-5019-4b42-ae43-6bce22b609bd","name":"Supabase Vector Store"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"d9a71db8-7fa0-4d51-b553-ee0af1b95cfa","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[-224,288]}],"connections":{"POST /chat/send":{"main":[[{"node":"JWT Validation","type":"main","index":0}]]},"JWT Validation":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Extract Request Data","type":"main","index":0}]]},"Extract Request Data":{"main":[[{"node":"Lookup Thread (HTTP)","type":"main","index":0}],[{"node":"400 Bad Request","type":"main","index":0}]]},"Lookup Thread (HTTP)":{"main":[[{"node":"Thread Not Found?","type":"main","index":0}]]},"Thread Not Found?":{"main":[[{"node":"404 Thread Not Found","type":"main","index":0}],[{"node":"Extract Thread Data","type":"main","index":0}]]},"Extract Thread Data":{"main":[[{"node":"Has File?","type":"main","index":0}]]},"Has File?":{"main":[[{"node":"Is Audio?","type":"main","index":0}],[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Is Audio?":{"main":[[{"node":"Process Audio (Whisper)","type":"main","index":0}],[{"node":"Process PDF","type":"main","index":0}]]},"Process Audio (Whisper)":{"main":[[{"node":"Merge File Results","type":"main","index":0}]]},"Process PDF":{"main":[[{"node":"Merge File Results","type":"main","index":1}]]},"Merge File Results":{"main":[[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Prepare Context Inputs":{"main":[[{"node":"Build Context (System Prompt)","type":"main","index":0}]]},"Build Context (System Prompt)":{"main":[[{"node":"Prepare AI Inputs","type":"main","index":0}]]},"Prepare AI Inputs":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Format Response","type":"main","index":0}],[{"node":"500 Internal Error","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Postgres Chat Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Embeddings OpenAI":{"ai_embedding":[[{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Format Response":{"main":[[{"node":"200 Success","type":"main","index":0}]]},"Supabase Vector Store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}},"settings":{"executionOrder":"v1"},"staticData":null,"meta":null,"pinData":{"POST /chat/send":[{"json":{"name":"First item","code":1}},{"json":{"name":"Second item","code":2}}]},"versionId":"d5df66f4-f029-4bb3-83c7-a12c7f41e8c5","versionCounter":1,"triggerCount":0,"shared":[{"updatedAt":"2025-10-23T21:31:02.363Z","createdAt":"2025-10-23T21:31:02.363Z","role":"workflow:owner","workflowId":"019Vrw56aROeEzVj","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Code","type":"n8n-nodes-base.code","typeVersion":2,"position":[500,300],"parameters":{"mode":"runOnceForAllItems","jsCode":"// Process data\nreturn items;"}}}]


workflow_id: 019Vrw56aROeEzVj
workflow_name: WOO_Workflow_21_POST_Chat_Send_AI_Agent
trigger: partial_update
created_at: 2025-11-13 09:02:58
workflow_snapshot:
  {"updatedAt":"2025-10-23T22:20:04.000Z","createdAt":"2025-10-23T21:31:02.360Z","id":"019Vrw56aROeEzVj","name":"WOO_Workflow_21_POST_Chat_Send_AI_Agent","active":false,"isArchived":true,"nodes":[{"parameters":{"httpMethod":"POST","path":"chat/send","responseMode":"responseNode","options":{}},"id":"2483395f-9d90-4105-8401-a64558f7a177","name":"POST /chat/send","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[-672,192],"webhookId":"b6bdd103-53ee-4fe3-b2c9-6bc89efcabf1","onError":"continueRegularOutput","notes":"Testing fix for issue #357 - AI node validation"},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"options":{}},"id":"e72c8395-ba13-4196-bc0b-e5b5bf31d874","name":"JWT Validation","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[-448,192],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"aeb8f7d7-4442-4adb-b707-5093f70e64af","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[-224,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"92454687-d663-47db-903f-5add6c3685fc","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[0,0]},{"parameters":{"jsCode":"const webhookData = $('POST /chat/send').item.json;\nconst jwtData = $('JWT Validation').item.json;\n\n// Extract request body\nconst body = webhookData.body || {};\n\n// Validate required fields\nif (!body.approach_id) {\n  throw new Error('Missing required field: approach_id');\n}\n\nif (!body.message && !body.file_url) {\n  throw new Error('Either message or file_url is required');\n}\n\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: body.patient_id || null,\n  approach_id: body.approach_id,\n  tool_id: body.tool_id || null,\n  message: body.message || '',\n  file_url: body.file_url || null,\n  file_type: body.file_type || null\n}];"},"id":"06c62c44-6679-4a44-8520-7b1417b43127","name":"Extract Request Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[0,192],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?user_id=eq.' + $json.user_id + '&approach_id=eq.' + $json.approach_id + '&status=eq.active' + ($json.patient_id ? '&patient_id=eq.' + $json.patient_id : '&patient_id=is.null') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('POST /chat/send').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjk1MTMwODgsImV4cCI6MjA0NTA4OTA4OH0.8DKKCeQxH6nJTFDNlOaP5yLMGufqMZ_4GNwwFgDY0xY"},{"name":"Prefer","value":"return=representation"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"7c506a85-08f1-46c8-ab0b-6325c7179a18","name":"Lookup Thread (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[224,96],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.length === 0 }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"b992f141-590d-4e45-82cd-b90030b1893a","name":"Thread Not Found?","type":"n8n-nodes-base.if","typeVersion":2,"position":[448,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Conversation thread not found. Please assign an approach to the patient first.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":404}},"id":"0bb72000-9ce2-4958-9a58-7f219bc468de","name":"404 Thread Not Found","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[672,0]},{"parameters":{"jsCode":"const requestData = $('Extract Request Data').item.json;\nconst threadArray = $json;\n\nif (!Array.isArray(threadArray) || threadArray.length === 0) {\n  throw new Error('Thread not found');\n}\n\nconst thread = threadArray[0];\n\nreturn [{\n  ...requestData,\n  thread_id: thread.id,\n  thread_status: thread.status\n}];"},"id":"60659c3c-6a7f-4194-8152-40ba58df061f","name":"Extract Thread Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[672,192]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.file_url !== null }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"f531e25f-be91-47ab-9846-bd8324b1deed","name":"Has File?","type":"n8n-nodes-base.if","typeVersion":2,"position":[896,400]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/m4a'].includes($json.file_type) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"9e808b9b-e62c-4442-a25a-fe7761e71c09","name":"Is Audio?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1120,112]},{"parameters":{"workflowId":"QmcrF997iWDYX2I1","options":{}},"id":"7d978fe6-b05a-47f3-99f6-10edb38c5a49","name":"Process Audio (Whisper)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,16]},{"parameters":{"workflowId":"QbArI0hmMn8HNcft","options":{}},"id":"a17551b4-da00-41a4-acea-ab376a639aa7","name":"Process PDF","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,208]},{"parameters":{"mode":"combine","options":{}},"id":"8bce12d5-4b54-452e-88f0-26f780a5b813","name":"Merge File Results","type":"n8n-nodes-base.merge","typeVersion":3,"position":[1568,112]},{"parameters":{"jsCode":"const data = $json;\n\n// Build user message: original message + transcription if available\nlet userMessage = data.message || '';\n\nif (data.transcription) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Transkrypcja nagrania audio:\\n' + data.transcription;\n}\n\nif (data.pdf_text) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Treść dokumentu PDF:\\n' + data.pdf_text;\n}\n\nif (!userMessage) {\n  throw new Error('No message content available after file processing');\n}\n\nreturn [{\n  approach_id: data.approach_id,\n  tool_id: data.tool_id,\n  patient_id: data.patient_id,\n  thread_id: data.thread_id,\n  user_message: userMessage\n}];"},"id":"37cb7f4d-1b33-4e93-a9e7-a8712aca22cb","name":"Prepare Context Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[1776,416]},{"parameters":{"workflowId":"IFNsqMYJhHQwo5Iz","options":{}},"id":"083ec9a4-b6c1-4ace-a25c-4d2aac5a9f71","name":"Build Context (System Prompt)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[2016,192]},{"parameters":{"jsCode":"const contextResult = $json;\nconst preparedData = $('Prepare Context Inputs').item.json;\n\nreturn [{\n  thread_id: preparedData.thread_id,\n  approach_id: preparedData.approach_id,\n  system_prompt: contextResult.data.system_prompt,\n  user_message: preparedData.user_message\n}];"},"id":"322a948b-8689-46eb-aa60-c78e24b2b40f","name":"Prepare AI Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[2240,192]},{"parameters":{"promptType":"define","text":"={{ $json.user_message }}","options":{"systemMessage":"={{ $json.system_prompt }}"}},"id":"94f34ea7-515e-449b-9f24-63c18e976b46","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","typeVersion":1.7,"position":[2512,192],"onError":"continueErrorOutput"},{"parameters":{"model":"gpt-5-mini","options":{"maxTokens":2000,"temperature":0.7}},"id":"202f55ef-3036-4219-ae99-0c2f74d4c2ce","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1,"position":[2464,416]},{"parameters":{"sessionIdType":"customKey","sessionKey":"={{ $('Prepare AI Inputs').item.json.thread_id }}","tableName":"conversation_messages","contextWindowLength":10},"id":"b73dff16-7df3-4254-a0f3-878a777ac158","name":"Postgres Chat Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","typeVersion":1.1,"position":[2592,416]},{"parameters":{"model":"text-embedding-3-small","options":{}},"id":"44371076-72c7-4ad1-9b81-1699163bc87b","name":"Embeddings OpenAI","type":"@n8n/n8n-nodes-langchain.embeddingsOpenAi","typeVersion":1,"position":[2800,624]},{"parameters":{"jsCode":"const aiResponse = $json.output;\nconst threadData = $('Prepare AI Inputs').item.json;\n\nreturn [{\n  success: true,\n  data: {\n    message: aiResponse,\n    thread_id: threadData.thread_id\n  },\n  metadata: {\n    timestamp: new Date().toISOString(),\n    model: 'gpt-4o-mini',\n    has_rag: true\n  }\n}];"},"id":"aa183cd6-85ee-4131-8304-28fdb39096fd","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[3088,96]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{"responseCode":200}},"id":"65a32b1b-b6f1-4c2d-a5fd-f42852f9f1d0","name":"200 Success","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3312,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": $json.message || \"Invalid request data\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":400}},"id":"c369b5f9-8151-4c42-863d-9571fc99b18c","name":"400 Bad Request","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[224,288]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INTERNAL_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString(),\n    \"error_details\": $json.message\n  }\n} }}","options":{"responseCode":500}},"id":"b119071e-b9b8-493f-bfe8-097f5bddc123","name":"500 Internal Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3088,288]},{"parameters":{"mode":"retrieve-as-tool","toolDescription":"Vector store","tableName":{"__rl":true,"value":"documents","mode":"list","cachedResultName":"documents"},"options":{}},"type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","typeVersion":1.3,"position":[2720,416],"id":"c996d945-5019-4b42-ae43-6bce22b609bd","name":"Supabase Vector Store"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"d9a71db8-7fa0-4d51-b553-ee0af1b95cfa","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[-224,288]}],"connections":{"POST /chat/send":{"main":[[{"node":"JWT Validation","type":"main","index":0}]]},"JWT Validation":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Extract Request Data","type":"main","index":0}]]},"Extract Request Data":{"main":[[{"node":"Lookup Thread (HTTP)","type":"main","index":0}],[{"node":"400 Bad Request","type":"main","index":0}]]},"Lookup Thread (HTTP)":{"main":[[{"node":"Thread Not Found?","type":"main","index":0}]]},"Thread Not Found?":{"main":[[{"node":"404 Thread Not Found","type":"main","index":0}],[{"node":"Extract Thread Data","type":"main","index":0}]]},"Extract Thread Data":{"main":[[{"node":"Has File?","type":"main","index":0}]]},"Has File?":{"main":[[{"node":"Is Audio?","type":"main","index":0}],[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Is Audio?":{"main":[[{"node":"Process Audio (Whisper)","type":"main","index":0}],[{"node":"Process PDF","type":"main","index":0}]]},"Process Audio (Whisper)":{"main":[[{"node":"Merge File Results","type":"main","index":0}]]},"Process PDF":{"main":[[{"node":"Merge File Results","type":"main","index":1}]]},"Merge File Results":{"main":[[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Prepare Context Inputs":{"main":[[{"node":"Build Context (System Prompt)","type":"main","index":0}]]},"Build Context (System Prompt)":{"main":[[{"node":"Prepare AI Inputs","type":"main","index":0}]]},"Prepare AI Inputs":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Format Response","type":"main","index":0}],[{"node":"500 Internal Error","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Postgres Chat Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Embeddings OpenAI":{"ai_embedding":[[{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Format Response":{"main":[[{"node":"200 Success","type":"main","index":0}]]},"Supabase Vector Store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}},"settings":{"executionOrder":"v1"},"staticData":null,"meta":null,"pinData":{"POST /chat/send":[{"json":{"name":"First item","code":1}},{"json":{"name":"Second item","code":2}}]},"versionId":"d5df66f4-f029-4bb3-83c7-a12c7f41e8c5","versionCounter":1,"triggerCount":0,"shared":[{"updatedAt":"2025-10-23T21:31:02.363Z","createdAt":"2025-10-23T21:31:02.363Z","role":"workflow:owner","workflowId":"019Vrw56aROeEzVj","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Code Processing","type":"n8n-nodes-base.code","typeVersion":2,"position":[500,300],"parameters":{"mode":"runOnceForAllItems","jsCode":"// Process data\nreturn items;"}}},{"type":"addConnection","source":"POST /chat/send","target":"Code Processing"}]


workflow_id: 019Vrw56aROeEzVj
workflow_name: WOO_Workflow_21_POST_Chat_Send_AI_Agent
trigger: partial_update
created_at: 2025-11-13 10:20:12
workflow_snapshot:
  {"updatedAt":"2025-11-13T09:02:58.970Z","createdAt":"2025-10-23T21:31:02.360Z","id":"019Vrw56aROeEzVj","name":"WOO_Workflow_21_POST_Chat_Send_AI_Agent","active":false,"isArchived":true,"nodes":[{"parameters":{"httpMethod":"POST","path":"chat/send","responseMode":"responseNode","options":{}},"id":"2483395f-9d90-4105-8401-a64558f7a177","name":"POST /chat/send","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[-672,192],"webhookId":"b6bdd103-53ee-4fe3-b2c9-6bc89efcabf1","onError":"continueRegularOutput","notes":"Testing fix for issue #357 - AI node validation"},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"options":{}},"id":"e72c8395-ba13-4196-bc0b-e5b5bf31d874","name":"JWT Validation","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[-448,192],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"aeb8f7d7-4442-4adb-b707-5093f70e64af","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[-224,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"92454687-d663-47db-903f-5add6c3685fc","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[0,0]},{"parameters":{"jsCode":"const webhookData = $('POST /chat/send').item.json;\nconst jwtData = $('JWT Validation').item.json;\n\n// Extract request body\nconst body = webhookData.body || {};\n\n// Validate required fields\nif (!body.approach_id) {\n  throw new Error('Missing required field: approach_id');\n}\n\nif (!body.message && !body.file_url) {\n  throw new Error('Either message or file_url is required');\n}\n\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: body.patient_id || null,\n  approach_id: body.approach_id,\n  tool_id: body.tool_id || null,\n  message: body.message || '',\n  file_url: body.file_url || null,\n  file_type: body.file_type || null\n}];"},"id":"06c62c44-6679-4a44-8520-7b1417b43127","name":"Extract Request Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[0,192],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?user_id=eq.' + $json.user_id + '&approach_id=eq.' + $json.approach_id + '&status=eq.active' + ($json.patient_id ? '&patient_id=eq.' + $json.patient_id : '&patient_id=is.null') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('POST /chat/send').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjk1MTMwODgsImV4cCI6MjA0NTA4OTA4OH0.8DKKCeQxH6nJTFDNlOaP5yLMGufqMZ_4GNwwFgDY0xY"},{"name":"Prefer","value":"return=representation"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"7c506a85-08f1-46c8-ab0b-6325c7179a18","name":"Lookup Thread (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[224,96],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.length === 0 }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"b992f141-590d-4e45-82cd-b90030b1893a","name":"Thread Not Found?","type":"n8n-nodes-base.if","typeVersion":2,"position":[448,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Conversation thread not found. Please assign an approach to the patient first.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":404}},"id":"0bb72000-9ce2-4958-9a58-7f219bc468de","name":"404 Thread Not Found","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[672,0]},{"parameters":{"jsCode":"const requestData = $('Extract Request Data').item.json;\nconst threadArray = $json;\n\nif (!Array.isArray(threadArray) || threadArray.length === 0) {\n  throw new Error('Thread not found');\n}\n\nconst thread = threadArray[0];\n\nreturn [{\n  ...requestData,\n  thread_id: thread.id,\n  thread_status: thread.status\n}];"},"id":"60659c3c-6a7f-4194-8152-40ba58df061f","name":"Extract Thread Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[672,192]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.file_url !== null }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"f531e25f-be91-47ab-9846-bd8324b1deed","name":"Has File?","type":"n8n-nodes-base.if","typeVersion":2,"position":[896,400]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/m4a'].includes($json.file_type) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"9e808b9b-e62c-4442-a25a-fe7761e71c09","name":"Is Audio?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1120,112]},{"parameters":{"workflowId":"QmcrF997iWDYX2I1","options":{}},"id":"7d978fe6-b05a-47f3-99f6-10edb38c5a49","name":"Process Audio (Whisper)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,16]},{"parameters":{"workflowId":"QbArI0hmMn8HNcft","options":{}},"id":"a17551b4-da00-41a4-acea-ab376a639aa7","name":"Process PDF","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,208]},{"parameters":{"mode":"combine","options":{}},"id":"8bce12d5-4b54-452e-88f0-26f780a5b813","name":"Merge File Results","type":"n8n-nodes-base.merge","typeVersion":3,"position":[1568,112]},{"parameters":{"jsCode":"const data = $json;\n\n// Build user message: original message + transcription if available\nlet userMessage = data.message || '';\n\nif (data.transcription) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Transkrypcja nagrania audio:\\n' + data.transcription;\n}\n\nif (data.pdf_text) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Treść dokumentu PDF:\\n' + data.pdf_text;\n}\n\nif (!userMessage) {\n  throw new Error('No message content available after file processing');\n}\n\nreturn [{\n  approach_id: data.approach_id,\n  tool_id: data.tool_id,\n  patient_id: data.patient_id,\n  thread_id: data.thread_id,\n  user_message: userMessage\n}];"},"id":"37cb7f4d-1b33-4e93-a9e7-a8712aca22cb","name":"Prepare Context Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[1776,416]},{"parameters":{"workflowId":"IFNsqMYJhHQwo5Iz","options":{}},"id":"083ec9a4-b6c1-4ace-a25c-4d2aac5a9f71","name":"Build Context (System Prompt)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[2016,192]},{"parameters":{"jsCode":"const contextResult = $json;\nconst preparedData = $('Prepare Context Inputs').item.json;\n\nreturn [{\n  thread_id: preparedData.thread_id,\n  approach_id: preparedData.approach_id,\n  system_prompt: contextResult.data.system_prompt,\n  user_message: preparedData.user_message\n}];"},"id":"322a948b-8689-46eb-aa60-c78e24b2b40f","name":"Prepare AI Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[2240,192]},{"parameters":{"promptType":"define","text":"={{ $json.user_message }}","options":{"systemMessage":"={{ $json.system_prompt }}"}},"id":"94f34ea7-515e-449b-9f24-63c18e976b46","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","typeVersion":1.7,"position":[2512,192],"onError":"continueErrorOutput"},{"parameters":{"model":"gpt-5-mini","options":{"maxTokens":2000,"temperature":0.7}},"id":"202f55ef-3036-4219-ae99-0c2f74d4c2ce","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1,"position":[2464,416]},{"parameters":{"sessionIdType":"customKey","sessionKey":"={{ $('Prepare AI Inputs').item.json.thread_id }}","tableName":"conversation_messages","contextWindowLength":10},"id":"b73dff16-7df3-4254-a0f3-878a777ac158","name":"Postgres Chat Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","typeVersion":1.1,"position":[2592,416]},{"parameters":{"model":"text-embedding-3-small","options":{}},"id":"44371076-72c7-4ad1-9b81-1699163bc87b","name":"Embeddings OpenAI","type":"@n8n/n8n-nodes-langchain.embeddingsOpenAi","typeVersion":1,"position":[2800,624]},{"parameters":{"jsCode":"const aiResponse = $json.output;\nconst threadData = $('Prepare AI Inputs').item.json;\n\nreturn [{\n  success: true,\n  data: {\n    message: aiResponse,\n    thread_id: threadData.thread_id\n  },\n  metadata: {\n    timestamp: new Date().toISOString(),\n    model: 'gpt-4o-mini',\n    has_rag: true\n  }\n}];"},"id":"aa183cd6-85ee-4131-8304-28fdb39096fd","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[3088,96]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{"responseCode":200}},"id":"65a32b1b-b6f1-4c2d-a5fd-f42852f9f1d0","name":"200 Success","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3312,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": $json.message || \"Invalid request data\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":400}},"id":"c369b5f9-8151-4c42-863d-9571fc99b18c","name":"400 Bad Request","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[224,288]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INTERNAL_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString(),\n    \"error_details\": $json.message\n  }\n} }}","options":{"responseCode":500}},"id":"b119071e-b9b8-493f-bfe8-097f5bddc123","name":"500 Internal Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3088,288]},{"parameters":{"mode":"retrieve-as-tool","toolDescription":"Vector store","tableName":{"__rl":true,"value":"documents","mode":"list","cachedResultName":"documents"},"options":{}},"type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","typeVersion":1.3,"position":[2720,416],"id":"c996d945-5019-4b42-ae43-6bce22b609bd","name":"Supabase Vector Store"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"d9a71db8-7fa0-4d51-b553-ee0af1b95cfa","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[-224,288]},{"id":"bf41ed42-4ac6-4bf1-b193-23ae7be74d4c","name":"Code Processing","type":"n8n-nodes-base.code","typeVersion":2,"position":[500,300],"parameters":{"mode":"runOnceForAllItems","jsCode":"// Process data\nreturn items;"}}],"connections":{"POST /chat/send":{"main":[[{"node":"JWT Validation","type":"main","index":0},{"node":"Code Processing","type":"main","index":0}]]},"JWT Validation":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Extract Request Data","type":"main","index":0}]]},"Extract Request Data":{"main":[[{"node":"Lookup Thread (HTTP)","type":"main","index":0}],[{"node":"400 Bad Request","type":"main","index":0}]]},"Lookup Thread (HTTP)":{"main":[[{"node":"Thread Not Found?","type":"main","index":0}]]},"Thread Not Found?":{"main":[[{"node":"404 Thread Not Found","type":"main","index":0}],[{"node":"Extract Thread Data","type":"main","index":0}]]},"Extract Thread Data":{"main":[[{"node":"Has File?","type":"main","index":0}]]},"Has File?":{"main":[[{"node":"Is Audio?","type":"main","index":0}],[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Is Audio?":{"main":[[{"node":"Process Audio (Whisper)","type":"main","index":0}],[{"node":"Process PDF","type":"main","index":0}]]},"Process Audio (Whisper)":{"main":[[{"node":"Merge File Results","type":"main","index":0}]]},"Process PDF":{"main":[[{"node":"Merge File Results","type":"main","index":1}]]},"Merge File Results":{"main":[[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Prepare Context Inputs":{"main":[[{"node":"Build Context (System Prompt)","type":"main","index":0}]]},"Build Context (System Prompt)":{"main":[[{"node":"Prepare AI Inputs","type":"main","index":0}]]},"Prepare AI Inputs":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Format Response","type":"main","index":0}],[{"node":"500 Internal Error","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Postgres Chat Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Embeddings OpenAI":{"ai_embedding":[[{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Format Response":{"main":[[{"node":"200 Success","type":"main","index":0}]]},"Supabase Vector Store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}},"settings":{"executionOrder":"v1"},"staticData":null,"meta":null,"pinData":{"POST /chat/send":[{"json":{"name":"First item","code":1}},{"json":{"name":"Second item","code":2}}]},"versionId":"79c25a0a-401c-4e60-a35e-2cf2b15208a1","versionCounter":2,"triggerCount":0,"shared":[{"updatedAt":"2025-10-23T21:31:02.363Z","createdAt":"2025-10-23T21:31:02.363Z","role":"workflow:owner","workflowId":"019Vrw56aROeEzVj","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"parameters":{},"id":"test-noop-node-123","name":"NoOp Test","type":"n8n-nodes-base.noOp","typeVersion":1,"position":[250,300]}}]


workflow_id: 019Vrw56aROeEzVj
workflow_name: WOO_Workflow_21_POST_Chat_Send_AI_Agent
trigger: partial_update
created_at: 2025-11-13 10:25:59
workflow_snapshot:
  {"updatedAt":"2025-11-13T09:02:58.970Z","createdAt":"2025-10-23T21:31:02.360Z","id":"019Vrw56aROeEzVj","name":"WOO_Workflow_21_POST_Chat_Send_AI_Agent","active":false,"isArchived":true,"nodes":[{"parameters":{"httpMethod":"POST","path":"chat/send","responseMode":"responseNode","options":{}},"id":"2483395f-9d90-4105-8401-a64558f7a177","name":"POST /chat/send","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[-672,192],"webhookId":"b6bdd103-53ee-4fe3-b2c9-6bc89efcabf1","onError":"continueRegularOutput","notes":"Testing fix for issue #357 - AI node validation"},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"options":{}},"id":"e72c8395-ba13-4196-bc0b-e5b5bf31d874","name":"JWT Validation","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[-448,192],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"aeb8f7d7-4442-4adb-b707-5093f70e64af","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[-224,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"92454687-d663-47db-903f-5add6c3685fc","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[0,0]},{"parameters":{"jsCode":"const webhookData = $('POST /chat/send').item.json;\nconst jwtData = $('JWT Validation').item.json;\n\n// Extract request body\nconst body = webhookData.body || {};\n\n// Validate required fields\nif (!body.approach_id) {\n  throw new Error('Missing required field: approach_id');\n}\n\nif (!body.message && !body.file_url) {\n  throw new Error('Either message or file_url is required');\n}\n\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: body.patient_id || null,\n  approach_id: body.approach_id,\n  tool_id: body.tool_id || null,\n  message: body.message || '',\n  file_url: body.file_url || null,\n  file_type: body.file_type || null\n}];"},"id":"06c62c44-6679-4a44-8520-7b1417b43127","name":"Extract Request Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[0,192],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?user_id=eq.' + $json.user_id + '&approach_id=eq.' + $json.approach_id + '&status=eq.active' + ($json.patient_id ? '&patient_id=eq.' + $json.patient_id : '&patient_id=is.null') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('POST /chat/send').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjk1MTMwODgsImV4cCI6MjA0NTA4OTA4OH0.8DKKCeQxH6nJTFDNlOaP5yLMGufqMZ_4GNwwFgDY0xY"},{"name":"Prefer","value":"return=representation"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"7c506a85-08f1-46c8-ab0b-6325c7179a18","name":"Lookup Thread (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[224,96],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.length === 0 }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"b992f141-590d-4e45-82cd-b90030b1893a","name":"Thread Not Found?","type":"n8n-nodes-base.if","typeVersion":2,"position":[448,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Conversation thread not found. Please assign an approach to the patient first.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":404}},"id":"0bb72000-9ce2-4958-9a58-7f219bc468de","name":"404 Thread Not Found","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[672,0]},{"parameters":{"jsCode":"const requestData = $('Extract Request Data').item.json;\nconst threadArray = $json;\n\nif (!Array.isArray(threadArray) || threadArray.length === 0) {\n  throw new Error('Thread not found');\n}\n\nconst thread = threadArray[0];\n\nreturn [{\n  ...requestData,\n  thread_id: thread.id,\n  thread_status: thread.status\n}];"},"id":"60659c3c-6a7f-4194-8152-40ba58df061f","name":"Extract Thread Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[672,192]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.file_url !== null }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"f531e25f-be91-47ab-9846-bd8324b1deed","name":"Has File?","type":"n8n-nodes-base.if","typeVersion":2,"position":[896,400]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/m4a'].includes($json.file_type) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"9e808b9b-e62c-4442-a25a-fe7761e71c09","name":"Is Audio?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1120,112]},{"parameters":{"workflowId":"QmcrF997iWDYX2I1","options":{}},"id":"7d978fe6-b05a-47f3-99f6-10edb38c5a49","name":"Process Audio (Whisper)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,16]},{"parameters":{"workflowId":"QbArI0hmMn8HNcft","options":{}},"id":"a17551b4-da00-41a4-acea-ab376a639aa7","name":"Process PDF","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,208]},{"parameters":{"mode":"combine","options":{}},"id":"8bce12d5-4b54-452e-88f0-26f780a5b813","name":"Merge File Results","type":"n8n-nodes-base.merge","typeVersion":3,"position":[1568,112]},{"parameters":{"jsCode":"const data = $json;\n\n// Build user message: original message + transcription if available\nlet userMessage = data.message || '';\n\nif (data.transcription) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Transkrypcja nagrania audio:\\n' + data.transcription;\n}\n\nif (data.pdf_text) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Treść dokumentu PDF:\\n' + data.pdf_text;\n}\n\nif (!userMessage) {\n  throw new Error('No message content available after file processing');\n}\n\nreturn [{\n  approach_id: data.approach_id,\n  tool_id: data.tool_id,\n  patient_id: data.patient_id,\n  thread_id: data.thread_id,\n  user_message: userMessage\n}];"},"id":"37cb7f4d-1b33-4e93-a9e7-a8712aca22cb","name":"Prepare Context Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[1776,416]},{"parameters":{"workflowId":"IFNsqMYJhHQwo5Iz","options":{}},"id":"083ec9a4-b6c1-4ace-a25c-4d2aac5a9f71","name":"Build Context (System Prompt)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[2016,192]},{"parameters":{"jsCode":"const contextResult = $json;\nconst preparedData = $('Prepare Context Inputs').item.json;\n\nreturn [{\n  thread_id: preparedData.thread_id,\n  approach_id: preparedData.approach_id,\n  system_prompt: contextResult.data.system_prompt,\n  user_message: preparedData.user_message\n}];"},"id":"322a948b-8689-46eb-aa60-c78e24b2b40f","name":"Prepare AI Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[2240,192]},{"parameters":{"promptType":"define","text":"={{ $json.user_message }}","options":{"systemMessage":"={{ $json.system_prompt }}"}},"id":"94f34ea7-515e-449b-9f24-63c18e976b46","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","typeVersion":1.7,"position":[2512,192],"onError":"continueErrorOutput"},{"parameters":{"model":"gpt-5-mini","options":{"maxTokens":2000,"temperature":0.7}},"id":"202f55ef-3036-4219-ae99-0c2f74d4c2ce","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1,"position":[2464,416]},{"parameters":{"sessionIdType":"customKey","sessionKey":"={{ $('Prepare AI Inputs').item.json.thread_id }}","tableName":"conversation_messages","contextWindowLength":10},"id":"b73dff16-7df3-4254-a0f3-878a777ac158","name":"Postgres Chat Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","typeVersion":1.1,"position":[2592,416]},{"parameters":{"model":"text-embedding-3-small","options":{}},"id":"44371076-72c7-4ad1-9b81-1699163bc87b","name":"Embeddings OpenAI","type":"@n8n/n8n-nodes-langchain.embeddingsOpenAi","typeVersion":1,"position":[2800,624]},{"parameters":{"jsCode":"const aiResponse = $json.output;\nconst threadData = $('Prepare AI Inputs').item.json;\n\nreturn [{\n  success: true,\n  data: {\n    message: aiResponse,\n    thread_id: threadData.thread_id\n  },\n  metadata: {\n    timestamp: new Date().toISOString(),\n    model: 'gpt-4o-mini',\n    has_rag: true\n  }\n}];"},"id":"aa183cd6-85ee-4131-8304-28fdb39096fd","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[3088,96]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{"responseCode":200}},"id":"65a32b1b-b6f1-4c2d-a5fd-f42852f9f1d0","name":"200 Success","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3312,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": $json.message || \"Invalid request data\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":400}},"id":"c369b5f9-8151-4c42-863d-9571fc99b18c","name":"400 Bad Request","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[224,288]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INTERNAL_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString(),\n    \"error_details\": $json.message\n  }\n} }}","options":{"responseCode":500}},"id":"b119071e-b9b8-493f-bfe8-097f5bddc123","name":"500 Internal Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3088,288]},{"parameters":{"mode":"retrieve-as-tool","toolDescription":"Vector store","tableName":{"__rl":true,"value":"documents","mode":"list","cachedResultName":"documents"},"options":{}},"type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","typeVersion":1.3,"position":[2720,416],"id":"c996d945-5019-4b42-ae43-6bce22b609bd","name":"Supabase Vector Store"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"d9a71db8-7fa0-4d51-b553-ee0af1b95cfa","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[-224,288]},{"id":"bf41ed42-4ac6-4bf1-b193-23ae7be74d4c","name":"Code Processing","type":"n8n-nodes-base.code","typeVersion":2,"position":[500,300],"parameters":{"mode":"runOnceForAllItems","jsCode":"// Process data\nreturn items;"}}],"connections":{"POST /chat/send":{"main":[[{"node":"JWT Validation","type":"main","index":0},{"node":"Code Processing","type":"main","index":0}]]},"JWT Validation":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Extract Request Data","type":"main","index":0}]]},"Extract Request Data":{"main":[[{"node":"Lookup Thread (HTTP)","type":"main","index":0}],[{"node":"400 Bad Request","type":"main","index":0}]]},"Lookup Thread (HTTP)":{"main":[[{"node":"Thread Not Found?","type":"main","index":0}]]},"Thread Not Found?":{"main":[[{"node":"404 Thread Not Found","type":"main","index":0}],[{"node":"Extract Thread Data","type":"main","index":0}]]},"Extract Thread Data":{"main":[[{"node":"Has File?","type":"main","index":0}]]},"Has File?":{"main":[[{"node":"Is Audio?","type":"main","index":0}],[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Is Audio?":{"main":[[{"node":"Process Audio (Whisper)","type":"main","index":0}],[{"node":"Process PDF","type":"main","index":0}]]},"Process Audio (Whisper)":{"main":[[{"node":"Merge File Results","type":"main","index":0}]]},"Process PDF":{"main":[[{"node":"Merge File Results","type":"main","index":1}]]},"Merge File Results":{"main":[[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Prepare Context Inputs":{"main":[[{"node":"Build Context (System Prompt)","type":"main","index":0}]]},"Build Context (System Prompt)":{"main":[[{"node":"Prepare AI Inputs","type":"main","index":0}]]},"Prepare AI Inputs":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Format Response","type":"main","index":0}],[{"node":"500 Internal Error","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Postgres Chat Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Embeddings OpenAI":{"ai_embedding":[[{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Format Response":{"main":[[{"node":"200 Success","type":"main","index":0}]]},"Supabase Vector Store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}},"settings":{"executionOrder":"v1"},"staticData":null,"meta":null,"pinData":{"POST /chat/send":[{"json":{"name":"First item","code":1}},{"json":{"name":"Second item","code":2}}]},"versionId":"79c25a0a-401c-4e60-a35e-2cf2b15208a1","versionCounter":2,"triggerCount":0,"shared":[{"updatedAt":"2025-10-23T21:31:02.363Z","createdAt":"2025-10-23T21:31:02.363Z","role":"workflow:owner","workflowId":"019Vrw56aROeEzVj","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Edit Fields","type":"n8n-nodes-base.set","typeVersion":3,"position":[500,300],"parameters":{"mode":"manual","duplicateItem":false,"assignments":{"assignments":[]},"options":{}}}}]


workflow_id: 019Vrw56aROeEzVj
workflow_name: WOO_Workflow_21_POST_Chat_Send_AI_Agent
trigger: partial_update
created_at: 2025-11-13 10:26:14
workflow_snapshot:
  {"updatedAt":"2025-11-13T09:02:58.970Z","createdAt":"2025-10-23T21:31:02.360Z","id":"019Vrw56aROeEzVj","name":"WOO_Workflow_21_POST_Chat_Send_AI_Agent","active":false,"isArchived":true,"nodes":[{"parameters":{"httpMethod":"POST","path":"chat/send","responseMode":"responseNode","options":{}},"id":"2483395f-9d90-4105-8401-a64558f7a177","name":"POST /chat/send","type":"n8n-nodes-base.webhook","typeVersion":2.1,"position":[-672,192],"webhookId":"b6bdd103-53ee-4fe3-b2c9-6bc89efcabf1","onError":"continueRegularOutput","notes":"Testing fix for issue #357 - AI node validation"},{"parameters":{"workflowId":{"__rl":true,"value":"dL4gKHrO0Mtdhpmo","mode":"list","cachedResultUrl":"/workflow/dL4gKHrO0Mtdhpmo","cachedResultName":"JWT Validation Middleware"},"options":{}},"id":"e72c8395-ba13-4196-bc0b-e5b5bf31d874","name":"JWT Validation","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[-448,192],"onError":"continueErrorOutput"},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.error }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"aeb8f7d7-4442-4adb-b707-5093f70e64af","name":"Auth Failed?","type":"n8n-nodes-base.if","typeVersion":2,"position":[-224,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"UNAUTHORIZED\",\n    \"message\": $json.error_message\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"92454687-d663-47db-903f-5add6c3685fc","name":"401 Unauthorized","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[0,0]},{"parameters":{"jsCode":"const webhookData = $('POST /chat/send').item.json;\nconst jwtData = $('JWT Validation').item.json;\n\n// Extract request body\nconst body = webhookData.body || {};\n\n// Validate required fields\nif (!body.approach_id) {\n  throw new Error('Missing required field: approach_id');\n}\n\nif (!body.message && !body.file_url) {\n  throw new Error('Either message or file_url is required');\n}\n\nreturn [{\n  user_id: jwtData.user_id,\n  patient_id: body.patient_id || null,\n  approach_id: body.approach_id,\n  tool_id: body.tool_id || null,\n  message: body.message || '',\n  file_url: body.file_url || null,\n  file_type: body.file_type || null\n}];"},"id":"06c62c44-6679-4a44-8520-7b1417b43127","name":"Extract Request Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[0,192],"onError":"continueErrorOutput"},{"parameters":{"url":"={{ 'https://xxciwnthnnywanbplqwg.supabase.co/rest/v1/conversation_threads?user_id=eq.' + $json.user_id + '&approach_id=eq.' + $json.approach_id + '&status=eq.active' + ($json.patient_id ? '&patient_id=eq.' + $json.patient_id : '&patient_id=is.null') }}","sendHeaders":true,"headerParameters":{"parameters":[{"name":"Authorization","value":"={{ $('POST /chat/send').item.json.headers.authorization }}"},{"name":"apikey","value":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh4Y2l3bnRobm55d2FuYnBscXdnIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjk1MTMwODgsImV4cCI6MjA0NTA4OTA4OH0.8DKKCeQxH6nJTFDNlOaP5yLMGufqMZ_4GNwwFgDY0xY"},{"name":"Prefer","value":"return=representation"}]},"options":{"response":{"response":{"responseFormat":"json"}}}},"id":"7c506a85-08f1-46c8-ab0b-6325c7179a18","name":"Lookup Thread (HTTP)","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[224,96],"alwaysOutputData":true},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.length === 0 }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"b992f141-590d-4e45-82cd-b90030b1893a","name":"Thread Not Found?","type":"n8n-nodes-base.if","typeVersion":2,"position":[448,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"NOT_FOUND\",\n    \"message\": \"Conversation thread not found. Please assign an approach to the patient first.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":404}},"id":"0bb72000-9ce2-4958-9a58-7f219bc468de","name":"404 Thread Not Found","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[672,0]},{"parameters":{"jsCode":"const requestData = $('Extract Request Data').item.json;\nconst threadArray = $json;\n\nif (!Array.isArray(threadArray) || threadArray.length === 0) {\n  throw new Error('Thread not found');\n}\n\nconst thread = threadArray[0];\n\nreturn [{\n  ...requestData,\n  thread_id: thread.id,\n  thread_status: thread.status\n}];"},"id":"60659c3c-6a7f-4194-8152-40ba58df061f","name":"Extract Thread Data","type":"n8n-nodes-base.code","typeVersion":2,"position":[672,192]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ $json.file_url !== null }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"f531e25f-be91-47ab-9846-bd8324b1deed","name":"Has File?","type":"n8n-nodes-base.if","typeVersion":2,"position":[896,400]},{"parameters":{"conditions":{"options":{"caseSensitive":true,"leftValue":"","typeValidation":"strict"},"conditions":[{"leftValue":"={{ ['audio/mpeg', 'audio/mp3', 'audio/wav', 'audio/m4a'].includes($json.file_type) }}","rightValue":true,"operator":{"type":"boolean","operation":"equals"}}],"combinator":"and"},"options":{}},"id":"9e808b9b-e62c-4442-a25a-fe7761e71c09","name":"Is Audio?","type":"n8n-nodes-base.if","typeVersion":2,"position":[1120,112]},{"parameters":{"workflowId":"QmcrF997iWDYX2I1","options":{}},"id":"7d978fe6-b05a-47f3-99f6-10edb38c5a49","name":"Process Audio (Whisper)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,16]},{"parameters":{"workflowId":"QbArI0hmMn8HNcft","options":{}},"id":"a17551b4-da00-41a4-acea-ab376a639aa7","name":"Process PDF","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[1344,208]},{"parameters":{"mode":"combine","options":{}},"id":"8bce12d5-4b54-452e-88f0-26f780a5b813","name":"Merge File Results","type":"n8n-nodes-base.merge","typeVersion":3,"position":[1568,112]},{"parameters":{"jsCode":"const data = $json;\n\n// Build user message: original message + transcription if available\nlet userMessage = data.message || '';\n\nif (data.transcription) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Transkrypcja nagrania audio:\\n' + data.transcription;\n}\n\nif (data.pdf_text) {\n  userMessage = (userMessage ? userMessage + '\\n\\n' : '') + \n                'Treść dokumentu PDF:\\n' + data.pdf_text;\n}\n\nif (!userMessage) {\n  throw new Error('No message content available after file processing');\n}\n\nreturn [{\n  approach_id: data.approach_id,\n  tool_id: data.tool_id,\n  patient_id: data.patient_id,\n  thread_id: data.thread_id,\n  user_message: userMessage\n}];"},"id":"37cb7f4d-1b33-4e93-a9e7-a8712aca22cb","name":"Prepare Context Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[1776,416]},{"parameters":{"workflowId":"IFNsqMYJhHQwo5Iz","options":{}},"id":"083ec9a4-b6c1-4ace-a25c-4d2aac5a9f71","name":"Build Context (System Prompt)","type":"n8n-nodes-base.executeWorkflow","typeVersion":1.1,"position":[2016,192]},{"parameters":{"jsCode":"const contextResult = $json;\nconst preparedData = $('Prepare Context Inputs').item.json;\n\nreturn [{\n  thread_id: preparedData.thread_id,\n  approach_id: preparedData.approach_id,\n  system_prompt: contextResult.data.system_prompt,\n  user_message: preparedData.user_message\n}];"},"id":"322a948b-8689-46eb-aa60-c78e24b2b40f","name":"Prepare AI Inputs","type":"n8n-nodes-base.code","typeVersion":2,"position":[2240,192]},{"parameters":{"promptType":"define","text":"={{ $json.user_message }}","options":{"systemMessage":"={{ $json.system_prompt }}"}},"id":"94f34ea7-515e-449b-9f24-63c18e976b46","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","typeVersion":1.7,"position":[2512,192],"onError":"continueErrorOutput"},{"parameters":{"model":"gpt-5-mini","options":{"maxTokens":2000,"temperature":0.7}},"id":"202f55ef-3036-4219-ae99-0c2f74d4c2ce","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","typeVersion":1,"position":[2464,416]},{"parameters":{"sessionIdType":"customKey","sessionKey":"={{ $('Prepare AI Inputs').item.json.thread_id }}","tableName":"conversation_messages","contextWindowLength":10},"id":"b73dff16-7df3-4254-a0f3-878a777ac158","name":"Postgres Chat Memory","type":"@n8n/n8n-nodes-langchain.memoryPostgresChat","typeVersion":1.1,"position":[2592,416]},{"parameters":{"model":"text-embedding-3-small","options":{}},"id":"44371076-72c7-4ad1-9b81-1699163bc87b","name":"Embeddings OpenAI","type":"@n8n/n8n-nodes-langchain.embeddingsOpenAi","typeVersion":1,"position":[2800,624]},{"parameters":{"jsCode":"const aiResponse = $json.output;\nconst threadData = $('Prepare AI Inputs').item.json;\n\nreturn [{\n  success: true,\n  data: {\n    message: aiResponse,\n    thread_id: threadData.thread_id\n  },\n  metadata: {\n    timestamp: new Date().toISOString(),\n    model: 'gpt-4o-mini',\n    has_rag: true\n  }\n}];"},"id":"aa183cd6-85ee-4131-8304-28fdb39096fd","name":"Format Response","type":"n8n-nodes-base.code","typeVersion":2,"position":[3088,96]},{"parameters":{"respondWith":"json","responseBody":"={{ $json }}","options":{"responseCode":200}},"id":"65a32b1b-b6f1-4c2d-a5fd-f42852f9f1d0","name":"200 Success","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3312,96]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": $json.message || \"Invalid request data\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString()\n  }\n} }}","options":{"responseCode":400}},"id":"c369b5f9-8151-4c42-863d-9571fc99b18c","name":"400 Bad Request","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[224,288]},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  \"success\": false,\n  \"error\": {\n    \"code\": \"INTERNAL_ERROR\",\n    \"message\": \"An unexpected error occurred. Please try again later.\"\n  },\n  \"metadata\": {\n    \"timestamp\": new Date().toISOString(),\n    \"error_details\": $json.message\n  }\n} }}","options":{"responseCode":500}},"id":"b119071e-b9b8-493f-bfe8-097f5bddc123","name":"500 Internal Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.1,"position":[3088,288]},{"parameters":{"mode":"retrieve-as-tool","toolDescription":"Vector store","tableName":{"__rl":true,"value":"documents","mode":"list","cachedResultName":"documents"},"options":{}},"type":"@n8n/n8n-nodes-langchain.vectorStoreSupabase","typeVersion":1.3,"position":[2720,416],"id":"c996d945-5019-4b42-ae43-6bce22b609bd","name":"Supabase Vector Store"},{"parameters":{"respondWith":"json","responseBody":"={{ {\n  success: false,\n  error: {\n    code: 'UNAUTHORIZED',\n    message: 'Invalid or expired access token'\n  },\n  metadata: {\n    timestamp: new Date().toISOString()\n  }\n} }}","options":{"responseCode":401}},"id":"d9a71db8-7fa0-4d51-b553-ee0af1b95cfa","name":"Return Error","type":"n8n-nodes-base.respondToWebhook","typeVersion":1.4,"position":[-224,288]},{"id":"bf41ed42-4ac6-4bf1-b193-23ae7be74d4c","name":"Code Processing","type":"n8n-nodes-base.code","typeVersion":2,"position":[500,300],"parameters":{"mode":"runOnceForAllItems","jsCode":"// Process data\nreturn items;"}}],"connections":{"POST /chat/send":{"main":[[{"node":"JWT Validation","type":"main","index":0},{"node":"Code Processing","type":"main","index":0}]]},"JWT Validation":{"main":[[{"node":"Auth Failed?","type":"main","index":0}],[{"node":"Return Error","type":"main","index":0}]]},"Auth Failed?":{"main":[[{"node":"401 Unauthorized","type":"main","index":0}],[{"node":"Extract Request Data","type":"main","index":0}]]},"Extract Request Data":{"main":[[{"node":"Lookup Thread (HTTP)","type":"main","index":0}],[{"node":"400 Bad Request","type":"main","index":0}]]},"Lookup Thread (HTTP)":{"main":[[{"node":"Thread Not Found?","type":"main","index":0}]]},"Thread Not Found?":{"main":[[{"node":"404 Thread Not Found","type":"main","index":0}],[{"node":"Extract Thread Data","type":"main","index":0}]]},"Extract Thread Data":{"main":[[{"node":"Has File?","type":"main","index":0}]]},"Has File?":{"main":[[{"node":"Is Audio?","type":"main","index":0}],[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Is Audio?":{"main":[[{"node":"Process Audio (Whisper)","type":"main","index":0}],[{"node":"Process PDF","type":"main","index":0}]]},"Process Audio (Whisper)":{"main":[[{"node":"Merge File Results","type":"main","index":0}]]},"Process PDF":{"main":[[{"node":"Merge File Results","type":"main","index":1}]]},"Merge File Results":{"main":[[{"node":"Prepare Context Inputs","type":"main","index":0}]]},"Prepare Context Inputs":{"main":[[{"node":"Build Context (System Prompt)","type":"main","index":0}]]},"Build Context (System Prompt)":{"main":[[{"node":"Prepare AI Inputs","type":"main","index":0}]]},"Prepare AI Inputs":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"AI Agent":{"main":[[{"node":"Format Response","type":"main","index":0}],[{"node":"500 Internal Error","type":"main","index":0}]]},"OpenAI Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Postgres Chat Memory":{"ai_memory":[[{"node":"AI Agent","type":"ai_memory","index":0}]]},"Embeddings OpenAI":{"ai_embedding":[[{"node":"Supabase Vector Store","type":"ai_embedding","index":0}]]},"Format Response":{"main":[[{"node":"200 Success","type":"main","index":0}]]},"Supabase Vector Store":{"ai_tool":[[{"node":"AI Agent","type":"ai_tool","index":0}]]}},"settings":{"executionOrder":"v1"},"staticData":null,"meta":null,"pinData":{"POST /chat/send":[{"json":{"name":"First item","code":1}},{"json":{"name":"Second item","code":2}}]},"versionId":"79c25a0a-401c-4e60-a35e-2cf2b15208a1","versionCounter":2,"triggerCount":0,"shared":[{"updatedAt":"2025-10-23T21:31:02.363Z","createdAt":"2025-10-23T21:31:02.363Z","role":"workflow:owner","workflowId":"019Vrw56aROeEzVj","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Edit Fields","type":"n8n-nodes-base.set","typeVersion":3,"position":[250,192],"parameters":{"mode":"manual","duplicateItem":false,"assignments":{"assignments":[]},"options":{}}}},{"type":"addConnection","source":"Extract Request Data","target":"Edit Fields","sourceIndex":0,"targetIndex":0}]


workflow_id: 2Hm7ZkoJgEsG9QBy
workflow_name: [TEST] Session Cleanup - Telemetry Test - Updated
trigger: partial_update
created_at: 2025-11-13 10:46:23
workflow_snapshot:
  {"updatedAt":"2025-11-13T10:46:16.847Z","createdAt":"2025-10-13T09:14:55.735Z","id":"2Hm7ZkoJgEsG9QBy","name":"[TEST] Session Cleanup - Telemetry Test - Updated","active":false,"isArchived":false,"nodes":[{"id":"start-node","name":"Manual Trigger","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[250,300],"parameters":{}},{"id":"set-node","name":"Test Data","type":"n8n-nodes-base.set","typeVersion":3,"position":[450,300],"parameters":{"assignments":{"assignments":[{"id":"test-1","name":"message","value":"Version 12","type":"string"},{"id":"test-2","name":"version","value":"2.19.2","type":"string"}]}}},{"id":"3c02efcd-54ce-480c-a8ee-01766da88743","name":"Fetch API Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[650,300],"parameters":{"url":"https://api.example.com/data","method":"GET","timeout":10000}},{"id":"28b6f41c-5af3-487a-a838-5236ba45b610","name":"Check Status","type":"n8n-nodes-base.if","typeVersion":1,"position":[850,300],"parameters":{"conditions":{"options":{"leftValue":"","caseSensitive":false,"typeValidation":"strict"}}}},{"id":"new-set-node-001","name":"Process Data","type":"n8n-nodes-base.set","typeVersion":1,"position":[550,400],"parameters":{"options":{}}},{"id":"ffc506c7-2c2d-4d3e-a258-6c946167872d","name":"Stop and Error","type":"n8n-nodes-base.stopAndError","typeVersion":1,"position":[500,400],"parameters":{"errorMessage":"An error occurred during processing"}},{"id":"bf39d663-213f-484b-8934-bb7747cb794c","name":"Merge Data","type":"n8n-nodes-base.merge","typeVersion":1,"position":[1050,300],"parameters":{}},{"id":"f94b7701-ee17-43eb-b721-033ebc2abf23","name":"Fetch User Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[900,400],"parameters":{"url":"https://api.example.com/users","method":"GET"}}],"connections":{"Manual Trigger":{"main":[[{"node":"Test Data","type":"main","index":0},{"node":"Fetch API Data","type":"main","index":0},{"node":"Stop and Error","type":"main","index":0}]]},"Fetch API Data":{"main":[[{"node":"Check Status","type":"main","index":0}]]},"Test Data":{"main":[[{"node":"Process Data","type":"main","index":0},{"node":"Fetch User Data","type":"main","index":0}]]},"Check Status":{"main":[[{"node":"Merge Data","type":"main","index":0}]]}},"settings":{"executionOrder":"v0"},"staticData":null,"meta":null,"pinData":null,"versionId":"0cac0c62-cfa8-441a-8cd6-b36824d18a9c","versionCounter":19,"triggerCount":0,"shared":[{"updatedAt":"2025-10-13T09:14:55.738Z","createdAt":"2025-10-13T09:14:55.738Z","role":"workflow:owner","workflowId":"2Hm7ZkoJgEsG9QBy","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"removeConnection","source":"Test Data","target":"Fetch User Data"},{"type":"addConnection","source":"Manual Trigger","target":"Fetch User Data"}]


workflow_id: 2Hm7ZkoJgEsG9QBy
workflow_name: [TEST] Session Cleanup - Telemetry Test - Updated
trigger: partial_update
created_at: 2025-11-13 11:16:33
workflow_snapshot:
  {"updatedAt":"2025-11-13T10:46:23.219Z","createdAt":"2025-10-13T09:14:55.735Z","id":"2Hm7ZkoJgEsG9QBy","name":"[TEST] Session Cleanup - Telemetry Test - Updated","active":false,"isArchived":false,"nodes":[{"id":"start-node","name":"Manual Trigger","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[250,300],"parameters":{}},{"id":"set-node","name":"Test Data","type":"n8n-nodes-base.set","typeVersion":3,"position":[450,300],"parameters":{"assignments":{"assignments":[{"id":"test-1","name":"message","value":"Version 12","type":"string"},{"id":"test-2","name":"version","value":"2.19.2","type":"string"}]}}},{"id":"3c02efcd-54ce-480c-a8ee-01766da88743","name":"Fetch API Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[650,300],"parameters":{"url":"https://api.example.com/data","method":"GET","timeout":10000}},{"id":"28b6f41c-5af3-487a-a838-5236ba45b610","name":"Check Status","type":"n8n-nodes-base.if","typeVersion":1,"position":[850,300],"parameters":{"conditions":{"options":{"leftValue":"","caseSensitive":false,"typeValidation":"strict"}}}},{"id":"new-set-node-001","name":"Process Data","type":"n8n-nodes-base.set","typeVersion":1,"position":[550,400],"parameters":{"options":{}}},{"id":"ffc506c7-2c2d-4d3e-a258-6c946167872d","name":"Stop and Error","type":"n8n-nodes-base.stopAndError","typeVersion":1,"position":[500,400],"parameters":{"errorMessage":"An error occurred during processing"}},{"id":"bf39d663-213f-484b-8934-bb7747cb794c","name":"Merge Data","type":"n8n-nodes-base.merge","typeVersion":1,"position":[1050,300],"parameters":{}},{"id":"f94b7701-ee17-43eb-b721-033ebc2abf23","name":"Fetch User Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[900,400],"parameters":{"url":"https://api.example.com/users","method":"GET"}}],"connections":{"Manual Trigger":{"main":[[{"node":"Test Data","type":"main","index":0},{"node":"Fetch API Data","type":"main","index":0},{"node":"Stop and Error","type":"main","index":0},{"node":"Fetch User Data","type":"main","index":0}]]},"Fetch API Data":{"main":[[{"node":"Check Status","type":"main","index":0}]]},"Test Data":{"main":[[{"node":"Process Data","type":"main","index":0}]]},"Check Status":{"main":[[{"node":"Merge Data","type":"main","index":0}]]}},"settings":{"executionOrder":"v0"},"staticData":null,"meta":null,"pinData":null,"versionId":"7a1a2396-40a9-4554-a79f-7a84f20cf52a","versionCounter":20,"triggerCount":0,"shared":[{"updatedAt":"2025-10-13T09:14:55.738Z","createdAt":"2025-10-13T09:14:55.738Z","role":"workflow:owner","workflowId":"2Hm7ZkoJgEsG9QBy","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"parameters":{"conditions":{"string":[{"value1":"={{ $json.status }}","operation":"equals","value2":"valid"}]}},"name":"Filter Valid Items","type":"n8n-nodes-base.if","typeVersion":2,"position":[400,300]}}]


workflow_id: 2Hm7ZkoJgEsG9QBy
workflow_name: [TEST] Session Cleanup - Telemetry Test - Updated
trigger: partial_update
created_at: 2025-11-13 11:16:45
workflow_snapshot:
  {"updatedAt":"2025-11-13T10:46:23.219Z","createdAt":"2025-10-13T09:14:55.735Z","id":"2Hm7ZkoJgEsG9QBy","name":"[TEST] Session Cleanup - Telemetry Test - Updated","active":false,"isArchived":false,"nodes":[{"id":"start-node","name":"Manual Trigger","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[250,300],"parameters":{}},{"id":"set-node","name":"Test Data","type":"n8n-nodes-base.set","typeVersion":3,"position":[450,300],"parameters":{"assignments":{"assignments":[{"id":"test-1","name":"message","value":"Version 12","type":"string"},{"id":"test-2","name":"version","value":"2.19.2","type":"string"}]}}},{"id":"3c02efcd-54ce-480c-a8ee-01766da88743","name":"Fetch API Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[650,300],"parameters":{"url":"https://api.example.com/data","method":"GET","timeout":10000}},{"id":"28b6f41c-5af3-487a-a838-5236ba45b610","name":"Check Status","type":"n8n-nodes-base.if","typeVersion":1,"position":[850,300],"parameters":{"conditions":{"options":{"leftValue":"","caseSensitive":false,"typeValidation":"strict"}}}},{"id":"new-set-node-001","name":"Process Data","type":"n8n-nodes-base.set","typeVersion":1,"position":[550,400],"parameters":{"options":{}}},{"id":"ffc506c7-2c2d-4d3e-a258-6c946167872d","name":"Stop and Error","type":"n8n-nodes-base.stopAndError","typeVersion":1,"position":[500,400],"parameters":{"errorMessage":"An error occurred during processing"}},{"id":"bf39d663-213f-484b-8934-bb7747cb794c","name":"Merge Data","type":"n8n-nodes-base.merge","typeVersion":1,"position":[1050,300],"parameters":{}},{"id":"f94b7701-ee17-43eb-b721-033ebc2abf23","name":"Fetch User Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[900,400],"parameters":{"url":"https://api.example.com/users","method":"GET"}}],"connections":{"Manual Trigger":{"main":[[{"node":"Test Data","type":"main","index":0},{"node":"Fetch API Data","type":"main","index":0},{"node":"Stop and Error","type":"main","index":0},{"node":"Fetch User Data","type":"main","index":0}]]},"Fetch API Data":{"main":[[{"node":"Check Status","type":"main","index":0}]]},"Test Data":{"main":[[{"node":"Process Data","type":"main","index":0}]]},"Check Status":{"main":[[{"node":"Merge Data","type":"main","index":0}]]}},"settings":{"executionOrder":"v0"},"staticData":null,"meta":null,"pinData":null,"versionId":"7a1a2396-40a9-4554-a79f-7a84f20cf52a","versionCounter":20,"triggerCount":0,"shared":[{"updatedAt":"2025-10-13T09:14:55.738Z","createdAt":"2025-10-13T09:14:55.738Z","role":"workflow:owner","workflowId":"2Hm7ZkoJgEsG9QBy","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"parameters":{"conditions":{"string":[{"value1":"={{ $json.status }}","operation":"equals","value2":"valid"}]}},"name":"Filter Valid Items","type":"n8n-nodes-base.if","typeVersion":2,"position":[1050,500]}},{"type":"addConnection","source":"Merge Data","target":"Filter Valid Items"}]


workflow_id: 2Hm7ZkoJgEsG9QBy
workflow_name: [TEST] Session Cleanup - Telemetry Test - Updated
trigger: partial_update
created_at: 2025-11-13 11:21:31
workflow_snapshot:
  {"updatedAt":"2025-11-13T11:16:45.914Z","createdAt":"2025-10-13T09:14:55.735Z","id":"2Hm7ZkoJgEsG9QBy","name":"[TEST] Session Cleanup - Telemetry Test - Updated","active":false,"isArchived":false,"nodes":[{"id":"start-node","name":"Manual Trigger","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[250,300],"parameters":{}},{"id":"set-node","name":"Test Data","type":"n8n-nodes-base.set","typeVersion":3,"position":[450,300],"parameters":{"assignments":{"assignments":[{"id":"test-1","name":"message","value":"Version 12","type":"string"},{"id":"test-2","name":"version","value":"2.19.2","type":"string"}]}}},{"id":"3c02efcd-54ce-480c-a8ee-01766da88743","name":"Fetch API Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[650,300],"parameters":{"url":"https://api.example.com/data","method":"GET","timeout":10000}},{"id":"28b6f41c-5af3-487a-a838-5236ba45b610","name":"Check Status","type":"n8n-nodes-base.if","typeVersion":1,"position":[850,300],"parameters":{"conditions":{"options":{"leftValue":"","caseSensitive":false,"typeValidation":"strict"}}}},{"id":"new-set-node-001","name":"Process Data","type":"n8n-nodes-base.set","typeVersion":1,"position":[550,400],"parameters":{"options":{}}},{"id":"ffc506c7-2c2d-4d3e-a258-6c946167872d","name":"Stop and Error","type":"n8n-nodes-base.stopAndError","typeVersion":1,"position":[500,400],"parameters":{"errorMessage":"An error occurred during processing"}},{"id":"bf39d663-213f-484b-8934-bb7747cb794c","name":"Merge Data","type":"n8n-nodes-base.merge","typeVersion":1,"position":[1050,300],"parameters":{}},{"id":"f94b7701-ee17-43eb-b721-033ebc2abf23","name":"Fetch User Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[900,400],"parameters":{"url":"https://api.example.com/users","method":"GET"}},{"id":"83ec1486-6d7a-4cbc-a3b8-6732eafb358a","name":"Filter Valid Items","type":"n8n-nodes-base.if","typeVersion":2,"position":[1050,500],"parameters":{"conditions":{"string":[{"value1":"={{ $json.status }}","operation":"equals","value2":"valid"}]}}}],"connections":{"Manual Trigger":{"main":[[{"node":"Test Data","type":"main","index":0},{"node":"Fetch API Data","type":"main","index":0},{"node":"Stop and Error","type":"main","index":0},{"node":"Fetch User Data","type":"main","index":0}]]},"Fetch API Data":{"main":[[{"node":"Check Status","type":"main","index":0}]]},"Test Data":{"main":[[{"node":"Process Data","type":"main","index":0}]]},"Check Status":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Merge Data":{"main":[[{"node":"Filter Valid Items","type":"main","index":0}]]}},"settings":{"executionOrder":"v0"},"staticData":null,"meta":null,"pinData":null,"versionId":"3b01c779-d244-46af-a5f0-c431eaa43116","versionCounter":21,"triggerCount":0,"shared":[{"updatedAt":"2025-10-13T09:14:55.738Z","createdAt":"2025-10-13T09:14:55.738Z","role":"workflow:owner","workflowId":"2Hm7ZkoJgEsG9QBy","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Webhook","type":"n8n-nodes-base.webhook","typeVersion":1,"position":[250,300],"parameters":{"httpMethod":"POST","path":"test-mutation","responseMode":"responseNode"}}}]


workflow_id: 2Hm7ZkoJgEsG9QBy
workflow_name: [TEST] Session Cleanup - Telemetry Test - Updated
trigger: partial_update
created_at: 2025-11-13 11:21:42
workflow_snapshot:
  {"updatedAt":"2025-11-13T11:16:45.914Z","createdAt":"2025-10-13T09:14:55.735Z","id":"2Hm7ZkoJgEsG9QBy","name":"[TEST] Session Cleanup - Telemetry Test - Updated","active":false,"isArchived":false,"nodes":[{"id":"start-node","name":"Manual Trigger","type":"n8n-nodes-base.manualTrigger","typeVersion":1,"position":[250,300],"parameters":{}},{"id":"set-node","name":"Test Data","type":"n8n-nodes-base.set","typeVersion":3,"position":[450,300],"parameters":{"assignments":{"assignments":[{"id":"test-1","name":"message","value":"Version 12","type":"string"},{"id":"test-2","name":"version","value":"2.19.2","type":"string"}]}}},{"id":"3c02efcd-54ce-480c-a8ee-01766da88743","name":"Fetch API Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[650,300],"parameters":{"url":"https://api.example.com/data","method":"GET","timeout":10000}},{"id":"28b6f41c-5af3-487a-a838-5236ba45b610","name":"Check Status","type":"n8n-nodes-base.if","typeVersion":1,"position":[850,300],"parameters":{"conditions":{"options":{"leftValue":"","caseSensitive":false,"typeValidation":"strict"}}}},{"id":"new-set-node-001","name":"Process Data","type":"n8n-nodes-base.set","typeVersion":1,"position":[550,400],"parameters":{"options":{}}},{"id":"ffc506c7-2c2d-4d3e-a258-6c946167872d","name":"Stop and Error","type":"n8n-nodes-base.stopAndError","typeVersion":1,"position":[500,400],"parameters":{"errorMessage":"An error occurred during processing"}},{"id":"bf39d663-213f-484b-8934-bb7747cb794c","name":"Merge Data","type":"n8n-nodes-base.merge","typeVersion":1,"position":[1050,300],"parameters":{}},{"id":"f94b7701-ee17-43eb-b721-033ebc2abf23","name":"Fetch User Data","type":"n8n-nodes-base.httpRequest","typeVersion":1,"position":[900,400],"parameters":{"url":"https://api.example.com/users","method":"GET"}},{"id":"83ec1486-6d7a-4cbc-a3b8-6732eafb358a","name":"Filter Valid Items","type":"n8n-nodes-base.if","typeVersion":2,"position":[1050,500],"parameters":{"conditions":{"string":[{"value1":"={{ $json.status }}","operation":"equals","value2":"valid"}]}}}],"connections":{"Manual Trigger":{"main":[[{"node":"Test Data","type":"main","index":0},{"node":"Fetch API Data","type":"main","index":0},{"node":"Stop and Error","type":"main","index":0},{"node":"Fetch User Data","type":"main","index":0}]]},"Fetch API Data":{"main":[[{"node":"Check Status","type":"main","index":0}]]},"Test Data":{"main":[[{"node":"Process Data","type":"main","index":0}]]},"Check Status":{"main":[[{"node":"Merge Data","type":"main","index":0}]]},"Merge Data":{"main":[[{"node":"Filter Valid Items","type":"main","index":0}]]}},"settings":{"executionOrder":"v0"},"staticData":null,"meta":null,"pinData":null,"versionId":"3b01c779-d244-46af-a5f0-c431eaa43116","versionCounter":21,"triggerCount":0,"shared":[{"updatedAt":"2025-10-13T09:14:55.738Z","createdAt":"2025-10-13T09:14:55.738Z","role":"workflow:owner","workflowId":"2Hm7ZkoJgEsG9QBy","projectId":"K0HtvGddtPI24Iyt","project":{"updatedAt":"2025-10-03T09:49:33.347Z","createdAt":"2025-10-03T09:48:05.062Z","id":"K0HtvGddtPI24Iyt","name":"Romuald Członkowski <romuald@n8n-mcp.com>","type":"personal","icon":null,"description":null,"projectRelations":[{"updatedAt":"2025-10-03T09:48:05.063Z","createdAt":"2025-10-03T09:48:05.063Z","userId":"ebb1bcfe-d103-4506-a994-9b18ef25e289","projectId":"K0HtvGddtPI24Iyt","user":{"updatedAt":"2025-11-13T08:20:04.000Z","createdAt":"2025-10-03T09:48:04.462Z","id":"ebb1bcfe-d103-4506-a994-9b18ef25e289","email":"romuald@n8n-mcp.com","firstName":"Romuald","lastName":"Członkowski","personalizationAnswers":{"version":"v4","personalization_survey_submitted_at":"2025-10-03T09:49:36.595Z","personalization_survey_n8n_version":"1.113.3"},"settings":{"userActivated":true,"firstSuccessfulWorkflowId":"DWyvSVPDFSSo9jwN","userActivatedAt":1759494619257,"easyAIWorkflowOnboarded":true,"npsSurvey":{"waitingForResponse":true,"ignoredCount":1,"lastShownAt":1762117185907}},"disabled":false,"mfaEnabled":false,"lastActiveAt":"2025-11-12","isPending":false}}]}}],"tags":[]}

operations:
  [{"type":"addNode","node":{"name":"Incoming Webhook","type":"n8n-nodes-base.webhook","typeVersion":2,"position":[250,500],"parameters":{"httpMethod":"POST","path":"test-mutation","responseMode":"responseNode"}}},{"type":"addConnection","source":"Incoming Webhook","target":"Process Data"}]


