{
  "id": "aUafqkSlH9nzcQuM",
  "name": "All in One Chat Flow",
  "tags": [],
  "nodes": [
    {
      "id": "5e1a4126-749d-4c6d-9856-12ea3fa925cf",
      "name": "WhatsApp Trigger",
      "type": "n8n-nodes-base.whatsAppTrigger",
      "position": [
        304,
        352
      ],
      "parameters": {
        "options": {
          "messageStatusUpdates": [
            "sent",
            "delivered",
            "read"
          ]
        },
        "updates": [
          "messages"
        ]
      },
      "credentials": {
        "whatsAppTriggerApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "6e910d80-c55e-4ae7-b7a3-ce2ff9b3319c",
      "name": "Route by Event",
      "type": "n8n-nodes-base.switch",
      "position": [
        528,
        320
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Read",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c8cb4e77-5f59-488d-ac19-aefc74dd39fe",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.statuses[0].status }}",
                    "rightValue": "read"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Sent",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "6603939d-d8a5-4b08-aed5-21cd0add04c8",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.statuses[0].status }}",
                    "rightValue": "sent"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Delivered",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "7b1b0a07-76f1-4a6a-9f47-6c27a1121bf3",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.statuses[0].status }}",
                    "rightValue": "delivered"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "message Rcvd"
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0d41280e-9d87-40b7-9ca7-a20320e5cf59",
      "name": "Store Read",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        752,
        128
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/status/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "wa_message_id",
              "value": "={{ $json.statuses[0].id }}"
            },
            {
              "name": "status",
              "value": "read"
            },
            {
              "name": "timestamp",
              "value": "={{ $json.statuses[0].timestamp }}"
            },
            {
              "name": "recipient_phone",
              "value": "={{ $json.statuses[0].recipient_id }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "00f5c83f-4b26-4446-aac5-b92058d787b5",
      "name": "Store Sent",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        752,
        320
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/status/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "wa_message_id",
              "value": "={{ $json.statuses[0].id }}"
            },
            {
              "name": "status",
              "value": "sent"
            },
            {
              "name": "timestamp",
              "value": "={{ $json.statuses[0].timestamp }}"
            },
            {
              "name": "recipient_phone",
              "value": "={{ $json.statuses[0].recipient_id }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "afbf465e-a996-414f-8a40-cbf8daeb6ea5",
      "name": "Store Delivered",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        752,
        512
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/status/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "wa_message_id",
              "value": "={{ $json.statuses[0].id }}"
            },
            {
              "name": "status",
              "value": "delivered"
            },
            {
              "name": "timestamp",
              "value": "={{ $json.statuses[0].timestamp }}"
            },
            {
              "name": "recipient_phone",
              "value": "={{ $json.statuses[0].recipient_id }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "8f9a0bb9-7870-4208-abc3-26c8bf9838ce",
      "name": "Normalise Message",
      "type": "n8n-nodes-base.set",
      "position": [
        976,
        704
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "phone",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.contacts[0].wa_id }}"
            },
            {
              "id": "a2",
              "name": "name",
              "type": "string",
              "value": "={{ $('Contact Lookup').item.json.found ? $('Contact Lookup').item.json.contact.name : $('WhatsApp Trigger').item.json.contacts[0].profile.name }}"
            },
            {
              "id": "a3",
              "name": "wa_message_id",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].id }}"
            },
            {
              "id": "a4",
              "name": "message_type",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].type }}"
            },
            {
              "id": "a5",
              "name": "timestamp",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].timestamp }}"
            },
            {
              "id": "a6",
              "name": "reaction_message_id",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].reaction?.message_id ?? '' }}"
            },
            {
              "id": "a7",
              "name": "reaction_emoji",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].reaction?.emoji ?? '' }}"
            },
            {
              "id": "a8",
              "name": "content",
              "type": "string",
              "value": "={{ $('WhatsApp Trigger').item.json.messages[0].text?.body ?? $('WhatsApp Trigger').item.json.messages[0][$('WhatsApp Trigger').item.json.messages[0].type]?.caption ?? ($('WhatsApp Trigger').item.json.messages[0].type === 'location' ? '\ud83d\udccd Location shared' : ($('WhatsApp Trigger').item.json.messages[0].type === 'reaction' ? $('WhatsApp Trigger').item.json.messages[0].reaction.emoji : '[' + $('WhatsApp Trigger').item.json.messages[0].type + ' message]')) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "3ebb25ec-7542-4451-84af-9793e3ec1826",
      "name": "Route by Message Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        1184,
        336
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Reaction",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "reaction-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "reaction"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Text",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "text-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "text"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Image",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "image-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "image"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Video",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "video-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "video"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Audio",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "audio-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "audio"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Voice",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "voice-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "voice"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Document",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "document-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "document"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Sticker",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "sticker-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "sticker"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Location",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "location-route",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.message_type }}",
                    "rightValue": "location"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "unknown"
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f04a688d-5f9e-40bf-944d-ed4bee36e505",
      "name": "Store Reaction",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1472,
        176
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/status/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "wa_message_id",
              "value": "={{ $('Normalise Message').item.json.reaction_message_id }}"
            },
            {
              "name": "status",
              "value": "reaction"
            },
            {
              "name": "timestamp",
              "value": "={{ $('Normalise Message').item.json.timestamp }}"
            },
            {
              "name": "recipient_phone",
              "value": "={{ $('Normalise Message').item.json.phone }}"
            },
            {
              "name": "reaction_emoji",
              "value": "={{ $('Normalise Message').item.json.reaction_emoji }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "150095a7-2899-4875-a663-0836889f0b00",
      "name": "Store Inbound Msg",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2704,
        288
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/messages/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $('Normalise Message').item.json.phone }}"
            },
            {
              "name": "direction",
              "value": "inbound"
            },
            {
              "name": "name",
              "value": "={{ $('Normalise Message').item.json.name }}"
            },
            {
              "name": "wa_message_id",
              "value": "={{ $('Normalise Message').item.json.wa_message_id }}"
            },
            {
              "name": "message_type",
              "value": "={{ $('Normalise Message').item.json.message_type }}"
            },
            {
              "name": "content",
              "value": "={{ $('Normalise Message').item.json.content }}"
            },
            {
              "name": "media_url",
              "value": "={{\n$if(\n  $('Prepare Media Message').isExecuted,\n  $('Prepare Media Message').item.json.media_url,\n  ''\n)\n}}"
            },
            {
              "name": "mime_type",
              "value": "={{\n$if(\n  $('Prepare Media Message').isExecuted,\n  $('Prepare Media Message').item.json.mime_type,\n  ''\n)\n}}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "1017cc63-ed37-4b71-9f22-4bb222f9bd00",
      "name": "Case Open Check",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2960,
        240
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/cases/lookup",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $('Normalise Message').item.json.phone }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "f629575c-1064-4d85-ba5c-a3210754422a",
      "name": "Case Open?",
      "type": "n8n-nodes-base.if",
      "position": [
        3216,
        240
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "case-open-check",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              },
              "leftValue": "={{ $json.case.status }}"
            },
            {
              "id": "31367b8c-218c-4807-904e-59cffd5b03df",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.case.status }}",
              "rightValue": "open"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "20c6d5b5-9d4d-4718-b40f-da4a4555782f",
      "name": "Prepare Agent Reply",
      "type": "n8n-nodes-base.set",
      "position": [
        4528,
        608
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "phone",
              "name": "phone",
              "type": "string",
              "value": "={{ $('Normalise Message').item.json.phone }}"
            },
            {
              "id": "reply",
              "name": "reply",
              "type": "string",
              "value": "={{ $json.output }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "153b1272-3e82-4c05-b9d1-d702bae70294",
      "name": "Send message",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        4752,
        608
      ],
      "parameters": {
        "textBody": "={{ $('Prepare Agent Reply').item.json.reply }}",
        "operation": "send",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Prepare Agent Reply').item.json.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "7f7c1fa6-9f80-498b-80c1-02b5d5e15a49",
      "name": "Store Outbound Msg",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4976,
        608
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/messages/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $('Prepare Agent Reply').item.json.phone }}"
            },
            {
              "name": "direction",
              "value": "outbound"
            },
            {
              "name": "wa_message_id",
              "value": "={{ $json.messages[0].id }}"
            },
            {
              "name": "message_type",
              "value": "text"
            },
            {
              "name": "content",
              "value": "={{ $('Prepare Agent Reply').item.json.reply }}"
            },
            {
              "name": "sent_by",
              "value": "ai"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "b9a415a7-da50-40d9-9703-23d0a214d883",
      "name": "OpenRouter Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3904,
        848
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c09a53e7-a8ab-49b9-8e50-2a57e87452d4",
      "name": "AI Agent1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        4128,
        608
      ],
      "parameters": {
        "text": "={{ $('Normalise Message').item.json.content }}",
        "options": {
          "maxIterations": 10,
          "systemMessage": "=You are a friendly, professional WhatsApp customer support assistant for a business.\n\nKeep your responses warm, natural, and concise, just like a real WhatsApp conversation. Avoid long explanations unless the customer specifically asks for more detail.\n\n**Customer Information**\n\n* Customer name: `{{ $('Normalise Message').first().json.name }}`\n* Customer phone: `{{ $('Normalise Message').first().json.phone }}`\n\n## Your Responsibilities\n\nYour primary goal is to resolve the customer's issue yourself whenever possible.\n\nHowever, if the issue requires human assistance, you **must** use the **Create Case** tool immediately.\n\n### Always Create a Case if:\n\n* The customer requests to speak with a human, agent, representative, manager, or support team.\n* The customer insists on talking to a person, even if you could answer the question.\n* The issue involves refunds, billing, payments, invoices, charges, or financial disputes.\n* The customer has a complaint or is dissatisfied.\n* The customer is upset, angry, or frustrated.\n* The issue requires investigation or access that you do not have.\n* The problem is complex, unclear, or cannot be fully resolved by you.\n* You are uncertain about the correct answer.\n* The customer asks you to create a support ticket or case.\n\n## Important Context\n\nYou will **only receive customer messages when there is NO open case** for that customer.\n\nIf there is already an open case, the system routes messages elsewhere and you will not receive them.\n\nTherefore:\n\n* Never assume a case already exists.\n* If the conversation requires human assistance according to the rules above, always create a new case immediately.\n* Do not hesitate or ask for permission first if the situation clearly requires escalation.\n\n## When Creating a Case\n\nUse the **Create Case** tool.\n\nWrite the case so that a human support agent who has **never seen this conversation** can immediately understand the situation.\n\nInclude:\n\n**Title**\n\n* A short, specific summary of the issue.\n\n**Briefing**\n\n* What the customer wants.\n* Relevant background and important details.\n* Any troubleshooting already attempted (if applicable).\n* What action the customer is expecting.\n\n**Suggested Replies**\nProvide **1\u20133 short, ready-to-send WhatsApp replies** that the human agent can send directly to continue the conversation.\n\n## After Creating a Case\n\nDo **not** continue troubleshooting.\n\nSimply reassure the customer with a short message such as:\n\n\"Thanks! I've forwarded your request to our support team. A human agent will get back to you as soon as possible.\"\n\n## If No Case Is Needed\n\nAnswer the customer's question directly.\n\nKeep responses:\n\n* Friendly\n* Professional\n* Accurate\n* Short and conversational\n* Appropriate for WhatsApp\n\nOnly ask follow-up questions when they are genuinely needed to resolve the customer's request."
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "6350af28-05b5-4f85-b467-b152e4b608a7",
      "name": "Contact Lookup",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        752,
        704
      ],
      "parameters": {
        "url": "=http://host.docker.internal:3001/api/contacts/lookup?phone={{ $json.contacts[0].wa_id }}",
        "options": {
          "response": {}
        },
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "405b56d0-ca37-460b-8c2a-c5df6618a2a6",
      "name": "Get Media URL",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1856,
        592
      ],
      "parameters": {
        "resource": "media",
        "operation": "mediaUrlGet",
        "mediaGetId": "={{ $('WhatsApp Trigger').item.json.messages[0][$('WhatsApp Trigger').item.json.messages[0].type].id }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "e5d860f8-dcc1-4634-b7cf-ce5fee5ebcec",
      "name": "Download Media",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2048,
        592
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "whatsAppApi"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "a0546bff-8584-4daf-9157-914523f2a0cb",
      "name": "Upload Media",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2240,
        592
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/media/upload",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "contentType": "multipart-form-data",
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "file",
              "parameterType": "formBinaryData",
              "inputDataFieldName": "data"
            },
            {
              "name": "message_type",
              "value": "={{ $('Normalise Message').item.json.message_type }}"
            },
            {
              "name": "wa_message_id",
              "value": "={{ $('Normalise Message').item.json.wa_message_id }}"
            },
            {
              "name": "mime_type",
              "value": "={{ $('Get Media URL').item.json.mime_type }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "3c1fa383-ddbb-486a-aa47-83d8e82c1f0d",
      "name": "Prepare Media Message",
      "type": "n8n-nodes-base.set",
      "position": [
        2432,
        592
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "m1",
              "name": "media_url",
              "type": "string",
              "value": "={{ $('Upload Media').item.json.media_url }}"
            },
            {
              "id": "m2",
              "name": "mime_type",
              "type": "string",
              "value": "={{ $('Upload Media').item.json.mime_type }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "46f1e8c0-eed9-4f60-8871-6682e9f6eb72",
      "name": "Prepare Static Reply",
      "type": "n8n-nodes-base.set",
      "position": [
        3872,
        256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "sr1",
              "name": "phone",
              "type": "string",
              "value": "={{ $('Normalise Message').item.json.phone }}"
            },
            {
              "id": "sr2",
              "name": "reply",
              "type": "string",
              "value": "Thanks for your message! I can only read text messages right now. Please type your question and I'll help you right away."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "01c40997-efbd-4164-b7a7-c234edfebb63",
      "name": "Send Static Reply",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        4112,
        256
      ],
      "parameters": {
        "textBody": "={{ $('Prepare Static Reply').item.json.reply }}",
        "operation": "send",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $('Prepare Static Reply').item.json.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "8df856ad-a7dd-4a9e-9220-7fe355b0aa22",
      "name": "Store Static Outbound",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        4336,
        256
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/messages/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $('Prepare Static Reply').item.json.phone }}"
            },
            {
              "name": "direction",
              "value": "outbound"
            },
            {
              "name": "wa_message_id",
              "value": "={{ $json.messages[0].id }}"
            },
            {
              "name": "message_type",
              "value": "text"
            },
            {
              "name": "content",
              "value": "={{ $('Prepare Static Reply').item.json.reply }}"
            },
            {
              "name": "sent_by",
              "value": "ai"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "c1652241-b9ed-45f2-9d73-e907377c6588",
      "name": "Simple Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        4192,
        832
      ],
      "parameters": {
        "sessionKey": "={{ $('Normalise Message').first().json.phone }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.4
    },
    {
      "id": "69249c00-0195-42b5-abf1-d445c3e74e65",
      "name": "Create Case",
      "type": "n8n-nodes-base.httpRequestTool",
      "position": [
        4416,
        992
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/cases/create",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $('Normalise Message').first().json.phone }}"
            },
            {
              "name": "title",
              "value": "={{ $fromAI('title', 'A short specific case title naming the customer actual problem (for example: Refund request for damaged order, or Cannot log into account). Never generic like Customer inquiry.', 'string') }}"
            },
            {
              "name": "summary",
              "value": "={{ $fromAI('summary', 'A briefing for the human agent who will take over: what the customer wants, the key details and context they gave, and anything already told to them. Written so the agent understands the problem without reading the whole conversation.', 'string') }}"
            },
            {
              "name": "urgency",
              "value": "={{ $fromAI('urgency', 'Urgency: low for a general question, medium for an issue needing attention, high for an upset customer or a refund/billing/payment or a time-sensitive matter or an explicit request for a human.', 'string') }}"
            },
            {
              "name": "ai_suggestion_1",
              "value": "={{ $fromAI('suggestion1', 'A complete ready-to-send reply the human agent can send to the customer to address their problem. An actual professional message, not a description of what to say.', 'string') }}"
            },
            {
              "name": "ai_suggestion_2",
              "value": "={{ $fromAI('suggestion2', 'A second complete ready-to-send reply option for the human agent, taking a slightly different angle from the first.', 'string') }}"
            },
            {
              "name": "ai_suggestion_3",
              "value": "={{ $fromAI('suggestion3', 'A third complete ready-to-send reply option for the human agent.', 'string') }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "toolDescription": "Create a support case when the customer has an issue that needs human agent attention or escalation (a complaint, a complex issue AI cannot resolve, an explicit request for a human, or refund/billing/urgent matters). Provide a clear title, a detailed summary, an urgency level (low/medium/high), and three suggested replies for the human agent."
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "60a58c2b-50c8-4490-89e4-90340e87f864",
      "name": "Reply Router",
      "type": "n8n-nodes-base.if",
      "position": [
        3568,
        288
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "reply-text-check",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Normalise Message').item.json.message_type }}",
              "rightValue": "text"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "73919237-9fa2-4054-b870-a368f3813818",
      "name": "Knowledge Base",
      "type": "@n8n/n8n-nodes-langchain.vectorStorePinecone",
      "position": [
        4544,
        976
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "options": {
          "pineconeNamespace": "company-kb"
        },
        "pineconeIndex": {
          "__rl": true,
          "mode": "list",
          "value": "company-knowledge-base",
          "cachedResultName": "company-knowledge-base"
        },
        "toolDescription": "Search the company knowledge base for facts about products, pricing, features, and policies before answering a customer question.",
        "includeDocumentMetadata": false
      },
      "credentials": {
        "pineconeApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "55617bc9-c55e-4433-8965-72881bee6c7d",
      "name": "Gemini",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        4576,
        1232
      ],
      "parameters": {
        "modelName": "models/gemini-embedding-2"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0ee62081-9f35-4b31-b76b-6c42c88a7bb1",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        -32
      ],
      "parameters": {
        "color": 7,
        "width": 1500,
        "height": 1020,
        "content": "## Inbound webhook, status events & message routing\n\nWhatsApp trigger. Routes status events (read / sent / delivered) to the API, and messages through contact lookup, normalise, message-type switch and reaction logging."
      },
      "typeVersion": 1
    },
    {
      "id": "16796773-40ca-487a-a39b-3998d8ea90eb",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        432
      ],
      "parameters": {
        "color": 7,
        "width": 960,
        "height": 330,
        "content": "## Media: fetch, download & upload\n\nFor image / audio / video / document messages: resolve the WhatsApp media URL, download it, upload to storage, and build the media message."
      },
      "typeVersion": 1
    },
    {
      "id": "6511fd25-de66-476c-be06-d9332f911ee9",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2624,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 824,
        "height": 320,
        "content": "## Persist message & open-case check\n\nStore the inbound message, then check the dashboard API for an already-open case on this conversation."
      },
      "typeVersion": 1
    },
    {
      "id": "a76e9c01-0e53-47ce-bb46-881e89db0199",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3456,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 360,
        "height": 340,
        "content": "## Reply routing\n\nOpen case \u2192 hand to the AI agent. No open case \u2192 send the static auto-reply."
      },
      "typeVersion": 1
    },
    {
      "id": "09ba3fb6-6fa7-4d02-9251-e9760cc2f4fb",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3824,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 708,
        "height": 336,
        "content": "## Auto-reply branch (no open case)\n\nPrepare and send a static WhatsApp reply, then log it to the dashboard."
      },
      "typeVersion": 1
    },
    {
      "id": "48e0504b-f669-4a10-a7cc-a243924d54bc",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3824,
        464
      ],
      "parameters": {
        "color": 7,
        "width": 1440,
        "height": 940,
        "content": "## AI agent \u2014 RAG, memory & reply send\n\nAI Agent (OpenRouter model, buffer memory, Pinecone Knowledge Base with Gemini embeddings, Create-Case tool) drafts a reply, which is sent on WhatsApp and stored."
      },
      "typeVersion": 1
    },
    {
      "id": "b0a18294-79ba-4864-9f54-5ee651ed17da",
      "name": "Outbound Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        928,
        2304
      ],
      "parameters": {
        "path": "ps-outbound-send",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "586a0b44-e4a6-4323-a2da-b6ebbec61f88",
      "name": "Has Media?",
      "type": "n8n-nodes-base.if",
      "position": [
        1152,
        2304
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "media-check",
              "operator": {
                "type": "string",
                "operation": "notEmpty"
              },
              "leftValue": "={{ $json.body.media_url }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "5a615f98-0554-4372-a060-0373c11a428e",
      "name": "Log Outbound",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        2272,
        2256
      ],
      "parameters": {
        "url": "http://host.docker.internal:3001/api/messages/store",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "phone",
              "value": "={{ $(\"Outbound Webhook\").item.json.body.phone }}"
            },
            {
              "name": "direction",
              "value": "outbound"
            },
            {
              "name": "wa_message_id",
              "value": "={{ $json.messages[0].id }}"
            },
            {
              "name": "message_type",
              "value": "={{ $(\"Outbound Webhook\").item.json.body.message_type }}"
            },
            {
              "name": "content",
              "value": "={{ $(\"Outbound Webhook\").item.json.body.content }}"
            },
            {
              "name": "media_url",
              "value": "={{ $(\"Outbound Webhook\").item.json.body.media_url }}"
            },
            {
              "name": "mime_type",
              "value": "={{ $(\"Outbound Webhook\").item.json.body.mime_type }}"
            },
            {
              "name": "sent_by",
              "value": "human"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "fe41c657-bc2b-4caa-a115-174ecacf6ab7",
      "name": "Send Text",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1760,
        2784
      ],
      "parameters": {
        "textBody": "={{ $json.body.content }}",
        "operation": "send",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {},
        "recipientPhoneNumber": "={{ $json.body.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "c11ea501-b7d3-4890-86c8-adf232402652",
      "name": "Respond OK",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        2464,
        2256
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { \"ok\": true, \"message_id\": $json.message_id } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "55e71f56-ea71-4e5d-aa83-10c8eb41cc15",
      "name": "Route By Media Type",
      "type": "n8n-nodes-base.switch",
      "position": [
        1520,
        2096
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "Image",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "5ceb26f5-b6b3-4704-bb86-65db6d58aae3",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.message_type }}",
                    "rightValue": "image"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "video",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9959ff8b-513b-4ceb-b090-1f6dd9873f96",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.message_type }}",
                    "rightValue": "video"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Document",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1e33bfe7-ff4d-40a1-9403-189c4d20c05e",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.message_type }}",
                    "rightValue": "document"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "Audio",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e897d5ae-636c-42a1-b8cd-57ccea72b107",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.message_type }}",
                    "rightValue": "audio"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "3bfc155c-64c1-410a-804c-d36bc684c426",
      "name": "Send Media - Image",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1744,
        1840
      ],
      "parameters": {
        "mediaLink": "={{ $json.body.media_url }}",
        "operation": "send",
        "messageType": "image",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {
          "mediaCaption": "={{ $json.body.content ?? '' }}"
        },
        "recipientPhoneNumber": "={{ $json.body.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "afcfa9f9-50dd-4fd5-8e79-87b16f3d8a6f",
      "name": "Send Media - Video",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1744,
        2032
      ],
      "parameters": {
        "mediaLink": "={{ $json.body.media_url }}",
        "operation": "send",
        "messageType": "video",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {
          "mediaCaption": "={{ $json.body.content ?? '' }}"
        },
        "recipientPhoneNumber": "={{ $json.body.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "251636c8-5d03-4e58-a67f-5f5450214d8a",
      "name": "Send Media - Document",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1744,
        2224
      ],
      "parameters": {
        "mediaLink": "={{ $json.body.media_url }}",
        "operation": "send",
        "messageType": "document",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {
          "mediaCaption": "={{ $json.body.content ?? '' }}"
        },
        "recipientPhoneNumber": "={{ $json.body.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "ad689e34-09dd-4477-a874-a834b3560282",
      "name": "Send Media - Audio",
      "type": "n8n-nodes-base.whatsApp",
      "position": [
        1744,
        2416
      ],
      "parameters": {
        "mediaLink": "={{ $json.body.media_url }}",
        "operation": "send",
        "messageType": "audio",
        "phoneNumberId": "1183690101495838",
        "additionalFields": {
          "mediaCaption": "={{ $json.body.content ?? '' }}"
        },
        "recipientPhoneNumber": "={{ $json.body.phone }}"
      },
      "credentials": {
        "whatsAppApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "87b548c6-57da-47d2-a77e-386a9f25d018",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        832,
        2176
      ],
      "parameters": {
        "color": 7,
        "width": 600,
        "height": 340,
        "content": "## Outbound webhook & media check\n\nDashboard POSTs here to send a message. Branch on whether a media URL is present."
      },
      "typeVersion": 1
    },
    {
      "id": "9316e355-7e20-430f-b75c-291690427732",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1456,
        1680
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 880,
        "content": "## Route & send media\n\nSwitch on message type and send the image / video / document / audio via WhatsApp."
      },
      "typeVersion": 1
    },
    {
      "id": "1fc937c8-68da-40c1-9558-b0fce61d0533",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1616,
        2608
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 332,
        "content": "## Send text\n\nNo media \u2192 send a plain WhatsApp text message."
      },
      "typeVersion": 1
    },
    {
      "id": "c26ffea9-61d8-4d44-80eb-ee30fd7f16ec",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2208,
        2128
      ],
      "parameters": {
        "color": 7,
        "width": 620,
        "height": 300,
        "content": "## Log outbound & respond\n\nLog the sent message to the dashboard API and return OK to the caller."
      },
      "typeVersion": 1
    },
    {
      "id": "082f6db1-d3f1-469e-b788-7ff1a7742d93",
      "name": "Summarize Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        3216,
        2160
      ],
      "parameters": {
        "path": "ps-summarize",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "d6e0d7f0-4f99-4b5b-84db-fe9abff4d165",
      "name": "Fetch Messages",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3440,
        2160
      ],
      "parameters": {
        "url": "=http://host.docker.internal:3001/api/messages/recent?phone={{ $json.body.phone }}&limit={{ $json.body.limit }}",
        "options": {},
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.4
    },
    {
      "id": "af50c825-f5c0-43f5-bb11-504a8d46b47c",
      "name": "Summarize",
      "type": "@n8n/n8n-nodes-langchain.chainLlm",
      "position": [
        3728,
        2160
      ],
      "parameters": {
        "text": "=Summarize this WhatsApp customer-support conversation for a human agent who is about to take over. Be concise: cover the customer's main issue, the key details, what has already been done or said, and what still needs action.\n\nConversation ({{ $json.count }} messages):\n{{ $json.messages.map(m => m.from.toUpperCase() + ': ' + m.text).join('\\n') }}",
        "batching": {},
        "messages": {
          "messageValues": [
            {
              "message": "You write short, clear handoff summaries of customer-support chats for human agents. Plain language, brief, no preamble."
            }
          ]
        },
        "promptType": "define"
      },
      "typeVersion": 1.9
    },
    {
      "id": "adff1895-a3c2-4cff-a989-c2efe5938372",
      "name": "OpenRouter Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3808,
        2384
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {}
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "eb52a5d2-4862-4ac1-ba8f-2d57f117e6e8",
      "name": "Respond Summary",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        4080,
        2160
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { \"summary\": $json.text } }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "45cb2bcf-cb0b-49e2-8271-ccec6f856bc9",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -656,
        528
      ],
      "parameters": {
        "width": 480,
        "height": 1984,
        "content": "## Chat Flow \u2014 WhatsApp AI Support Suite\n\nA 3-workflow module powering the **Chat Flow** WhatsApp support dashboard: an AI agent that answers customers with RAG, a sender that pushes messages out, and a summarizer that briefs human agents on handoff.\n\n\ud83d\udd17 **Dashboard repo & full setup guide:** https://github.com/salmannn4321/chat-flow\n\ud83d\udcc4 **Database schema:** `SUPABASE_SCHEMA.md` in the repo\n\n---\n\n### 1. WhatsApp AI Support Agent with RAG (Chat Flow)\n\n**What it does:**\nHandles every inbound WhatsApp event. Logs status updates (read/sent/delivered), looks up the contact, normalises text and media messages, and stores them. Checks whether the conversation has an open case \u2014 if not, sends a static auto-reply; if yes, hands off to an AI Agent that answers using a Pinecone knowledge base (RAG), conversation memory, and a tool to open new support cases automatically. Sends the AI's reply back over WhatsApp and logs it.\n\n**Setup:**\n- [ ] WhatsApp Cloud API credential (phone number ID) on every WhatsApp node\n- [ ] Pinecone credential + index, and Google Gemini embeddings credential (Knowledge Base node)\n- [ ] OpenRouter credential on the AI Agent's model node\n- [ ] Dashboard API base URL + bearer token on the HTTP Request nodes\n- [ ] Point your WhatsApp Business webhook at this workflow's trigger\n\n---\n\n### 2. WhatsApp Message Sender (Chat Flow)\n\n**What it does:**\nOutbound leg \u2014 the dashboard calls this webhook whenever an agent sends a reply. Checks if the message includes media; if so, routes by type (image/video/document/audio) and sends via WhatsApp, otherwise sends plain text. Logs the sent message back to the dashboard and responds with the WhatsApp message ID.\n\n**Setup:**\n- [ ] WhatsApp Cloud API credential (phone number ID) on every WhatsApp node\n- [ ] Dashboard API base URL + bearer token on \"Log Outbound\"\n- [ ] Point the dashboard's outbound-send env var at this workflow's production webhook URL\n\n---\n\n### 3. WhatsApp Chat Summarizer for Agent Handoff (Chat Flow)\n\n**What it does:**\nRuns when a human agent is about to take over a conversation. The dashboard sends a phone number and a message limit; this workflow fetches the last N messages from the dashboard API and asks an LLM to write a short handoff summary \u2014 the customer's issue, what's been done, what's still needed \u2014 then returns it as JSON.\n\n**Setup:**\n- [ ] Dashboard API base URL + bearer token on \"Fetch Messages\"\n- [ ] OpenRouter credential on the summarizer's model node\n- [ ] Point the dashboard's summarize env var at this workflow's production webhook URL\n\n---\n\n### Customization\n- Swap OpenRouter for OpenAI/Anthropic on either AI model node\n- Edit the AI Agent's system prompt, handoff keywords, and RAG index\n- Adjust the summary window (`limit`) from the dashboard"
      },
      "typeVersion": 1
    },
    {
      "id": "9031ad8b-68fe-48a4-9db6-95a6cbcf88a7",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3136,
        2000
      ],
      "parameters": {
        "color": 7,
        "width": 510,
        "height": 300,
        "content": "## Summarize request & fetch messages\n\nDashboard POSTs phone + limit; fetch the last N messages from the dashboard API."
      },
      "typeVersion": 1
    },
    {
      "id": "74cc9332-269a-4131-8e97-1fb4909defb4",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3664,
        2000
      ],
      "parameters": {
        "color": 7,
        "width": 376,
        "height": 560,
        "content": "## AI summary (LLM chain)\n\nSummarise the conversation for a human agent taking over, using the OpenRouter model."
      },
      "typeVersion": 1
    },
    {
      "id": "6a03ef69-8845-4ee3-833e-2ade666c2588",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4048,
        2000
      ],
      "parameters": {
        "color": 7,
        "width": 296,
        "height": 320,
        "content": "## Return summary\n\nRespond to the dashboard with the generated summary JSON."
      },
      "typeVersion": 1
    },
    {
      "id": "1d5c9aae-2880-4872-aac9-3caf9fcfb5f5",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        -256
      ],
      "parameters": {
        "color": 2,
        "width": 5200,
        "height": 1760,
        "content": "# 1. WhatsApp AI Support Agent with RAG"
      },
      "typeVersion": 1
    },
    {
      "id": "a43a0cf6-8594-4e0e-8b26-7e6da6288a90",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        144,
        1568
      ],
      "parameters": {
        "color": 2,
        "width": 2832,
        "height": 1440,
        "content": "# 2. WhatsApp Message Sender"
      },
      "typeVersion": 1
    },
    {
      "id": "993c6a5e-1f14-49f3-9d04-e9f83cba72a8",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3072,
        1808
      ],
      "parameters": {
        "color": 2,
        "width": 1296,
        "height": 768,
        "content": "# 3. WhatsApp Chat Summarizer for Agent Handoff"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "665ba072-1aed-413c-a1ff-78a30f5008fd",
  "nodeGroups": [],
  "connections": {
    "Gemini": {
      "ai_embedding": [
        [
          {
            "node": "Knowledge Base",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent1": {
      "main": [
        [
          {
            "node": "Prepare Agent Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Text": {
      "main": [
        [
          {
            "node": "Log Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize": {
      "main": [
        [
          {
            "node": "Respond Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Case Open?": {
      "main": [
        [],
        [
          {
            "node": "Reply Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Media?": {
      "main": [
        [
          {
            "node": "Route By Media Type",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Case": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Log Outbound": {
      "main": [
        [
          {
            "node": "Respond OK",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reply Router": {
      "main": [
        [
          {
            "node": "AI Agent1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Static Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send message": {
      "main": [
        [
          {
            "node": "Store Outbound Msg",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload Media": {
      "main": [
        [
          {
            "node": "Prepare Media Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Media URL": {
      "main": [
        [
          {
            "node": "Download Media",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Contact Lookup": {
      "main": [
        [
          {
            "node": "Normalise Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Media": {
      "main": [
        [
          {
            "node": "Upload Media",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Messages": {
      "main": [
        [
          {
            "node": "Summarize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Knowledge Base": {
      "ai_tool": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Route by Event": {
      "main": [
        [
          {
            "node": "Store Read",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Sent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Delivered",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Contact Lookup",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Case Open Check": {
      "main": [
        [
          {
            "node": "Case Open?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Model": {
      "ai_languageModel": [
        [
          {
            "node": "Summarize",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Outbound Webhook": {
      "main": [
        [
          {
            "node": "Has Media?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WhatsApp Trigger": {
      "main": [
        [
          {
            "node": "Route by Event",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalise Message": {
      "main": [
        [
          {
            "node": "Route by Message Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Model1": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Send Static Reply": {
      "main": [
        [
          {
            "node": "Store Static Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Store Inbound Msg": {
      "main": [
        [
          {
            "node": "Case Open Check",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Summarize Webhook": {
      "main": [
        [
          {
            "node": "Fetch Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Media - Audio": {
      "main": [
        [
          {
            "node": "Log Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Media - Image": {
      "main": [
        [
          {
            "node": "Log Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Media - Video": {
      "main": [
        [
          {
            "node": "Log Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Agent Reply": {
      "main": [
        [
          {
            "node": "Send message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route By Media Type": {
      "main": [
        [
          {
            "node": "Send Media - Image",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Media - Video",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Media - Document",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Media - Audio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Static Reply": {
      "main": [
        [
          {
            "node": "Send Static Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Media Message": {
      "main": [
        [
          {
            "node": "Store Inbound Msg",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by Message Type": {
      "main": [
        [
          {
            "node": "Store Reaction",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Inbound Msg",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get Media URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Inbound Msg",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Store Inbound Msg",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Media - Document": {
      "main": [
        [
          {
            "node": "Log Outbound",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}