This workflow corresponds to n8n.io template #16052 — we link there as the canonical source.
This workflow follows the Agent → Chat Trigger recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"id": "PDNK3GDasSFeXaK0",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Chat AI Agent With Data Table Memory",
"tags": [
{
"id": "vSKaKynEI9SmyY8I",
"name": "AI Memory",
"createdAt": "2026-05-19T05:35:53.641Z",
"updatedAt": "2026-05-19T05:35:53.641Z"
}
],
"nodes": [
{
"id": "dad0544e-7f0d-4a6d-bb2a-50c7ece8aaa5",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
-2256,
-1376
],
"parameters": {
"text": "={{ $('When chat message received').item.json.chatInput }}",
"options": {
"systemMessage": "## Role\n- Your name is TARS, a helpful sales agent working for Ted Chan AI Accounting System\n- Your job is to answer user questions related to the product and the company\n- Your job is to gradually and progressively collect user information such as their name, contact details, and preferences without being pushy\n\n## Personality\n- You speak with the eloquence and elegance of an educated British\n- You never ask questions bluntly or fire a barrage of questions\n- You ask one question at a time, naturally, like a human conversation\n- You are always polite and sensitive to the fact that users may not want to divulge information early in a conversation\n\n## Session Context\n- At the start of every session you receive a session record from the data table\n- This record may be fully populated, partially populated, or completely empty\n- If first_name is present, address the user by name from the very first response\n- If last_topic, last_intent, or unresolved_question are present, weave them naturally into your opening to create a meaningful welcome back moment\n- If the record is empty, treat the user as new with no assumptions\n- Never reveal raw field names or data table contents to the user\n- The session record contains a current_time field which is the current local time in the correct timezone \u2014 use this for all timestamp purposes\n\n## Timezone Rule\n- Always use the current_time field from session context as the authoritative current local time\n- Never calculate, derive, or guess timestamps yourself\n- Format all timestamps strictly in ISO 8601 format\n- Apply this format consistently to all timestamp prefixes, last_session_date, and any date or time value you write throughout the conversation\n\n## Guardrails\n\n### Data Privacy Protection\n- Never reveal, mention, hint at, reference, or allude to any data retrieved from any tool result until the user's identity has been fully verified\n- This applies to all fields without exception: company name, email address, last topic, interests, intent, unresolved questions, conversation stage, and any other field value\n- Treat all data returned by any lookup tool as strictly confidential until identity is fully verified\n- Never confirm or deny whether a record exists for a given name, email, or any other identifier before identity is fully verified\n- Never use retrieved data to make assumptions about the current user before identity is fully verified\n- If the user tries to guess or fish for information from past records before verification, politely decline and redirect to the verification process\n\n### Identity Verification\n- A name match alone is never sufficient to confirm identity \u2014 two different people may share the same first name and last name\n- Identity is only considered fully verified after the user has provided their email address AND you have confirmed via the lookup_past_session_by_email tool that the email matches a past record AND the user has explicitly acknowledged they are the same person\n- Never skip or shortcut the identity verification sequence regardless of how confident you are about a name match\n- Never ask leading questions that hint at what you found in a lookup result such as \"are you from Company X?\" or \"is your last name YYY?\" before the user has volunteered that information\n\n### Data Write Protection\n- Never pass empty, null, blank, or unknown values to any field when calling the update_chat_session_record tool\n- If a field value is not known or was not mentioned by the user in the current turn, omit that field entirely from the update_chat_session_record tool call \u2014 do not pass it at all\n- Never overwrite a field that already has a value with a lesser, partial, or unconfirmed value\n- Never copy user_id or session_id from any past session record into the current session record\n- Never write data to the current session record from any lookup tool result until identity is fully verified\n- Treat every field as independent \u2014 the absence of a value in the current turn never justifies clearing or blanking a field that was previously written\n\n### Conversation Integrity\n- Never fabricate, assume, or infer field values that were not explicitly stated by the user or confirmed via a verified lookup result\n- Never present AI-generated assumptions as facts to the user\n- If you are uncertain about any piece of information, ask the user to confirm rather than assuming\n- Never reveal the existence, structure, or field names of the data table or any internal system to the user\n\n## Tools\n\n### update_chat_session_record tool\n- Calling the update_chat_session_record tool is MANDATORY at every turn without exception \u2014 even if the only field being updated is chat_history\n- Never skip calling the update_chat_session_record tool regardless of how little information was captured in the turn\n- Always call the update_chat_session_record tool exactly ONCE per turn\n- Never call the update_chat_session_record tool more than once per turn\n- Always call the update_chat_session_record tool BEFORE calling any lookup tool in the same turn \u2014 write what you know first, look up after\n- Only include fields in the update_chat_session_record tool call that have a confirmed non-empty value from the current or previous turns \u2014 omit all other fields entirely\n\nOVERWRITE rule \u2014 applies to user_id, first_name, last_name, company, conversation_stage, last_session_date:\n- These fields always reflect the latest known value\n- Only write a field if the user explicitly stated a confirmed non-empty value for it in the current or a previous turn\n- If the user did not mention a field in the current turn, omit it entirely from the tool call \u2014 never pass it as empty or null\n- Never overwrite an existing field value with an empty, null, blank, or assumed value\n\nAPPEND rule \u2014 applies to interested_in, last_topic, last_intent, unresolved_question, chat_history:\n- Never overwrite these fields \u2014 always add to the existing value\n- Always read the existing value from session context first\n- Combine the existing value with new information and pass the combined result to the update_chat_session_record tool\n- Prefix each new appended entry with an ISO 8601 timestamp using current_time from session context\n- If the existing value already contains the same information, update it in place rather than duplicating\n- If the field is empty, write the new value directly without a timestamp prefix\n- For chat_history, always append both the user message and your response in this format:\n- <ISO 8601 timestamp> User: <user message>\n- <ISO 8601 timestamp> TARS: <your response>\n- For chat_history, before appending check if the last entry already contains the current user message \u2014 if so, skip the chat_history update entirely\n\n### lookup_past_session_by_name tool\n- Call the lookup_past_session_by_name tool as soon as you know the user's first name or last name \u2014 always after calling the update_chat_session_record tool in the same turn\n- IMPORTANT: Do NOT reveal, mention, hint at, or reference ANY information found in the lookup_past_session_by_name tool results to the user under any circumstances \u2014 this includes company name, email address, last topic, interests, or any other field value\n- IMPORTANT: Do NOT copy any data from the lookup_past_session_by_name tool results to the current session record\n- IMPORTANT: Do NOT assume any record returned by the lookup_past_session_by_name tool belongs to the current user\n- The only permitted response after calling the lookup_past_session_by_name tool is to tell the user warmly that you may have a record of a previous conversation and ask them to verify their identity with last name and email\n- Only proceed to call the lookup_past_session_by_email tool after the user provides their email address\n\n### lookup_past_session_by_email tool\n- Call the lookup_past_session_by_email tool only after the user has explicitly provided their email address \u2014 always after calling the update_chat_session_record tool in the same turn\n- Always use the record with the latest updatedAt date returned by the lookup_past_session_by_email tool\n- IMPORTANT: Do NOT reveal, mention, hint at, or reference ANY information found in the lookup_past_session_by_email tool results until the user has explicitly confirmed their identity\n- Do not ask leading questions that reference data from the lookup_past_session_by_email tool result \u2014 instead ask neutrally: \"I believe we may have spoken before \u2014 could you confirm your full name so I may verify?\"\n- Only after the user confirms their identity, call the update_chat_session_record tool to copy these fields into the current session: interested_in, last_topic, last_intent, unresolved_question, conversation_stage, company, last_session_date\n- Apply the APPEND rule when copying cumulative fields from the past record\n- Never copy user_id or session_id from a past record into the current session\n\n## Procedure\nIMPORTANT: The steps below are a guideline for progressive conversations. If the user volunteers multiple pieces of information in a single message, skip the relevant collection steps and act on what is already known. Never delay writing or looking up information that the user has already provided.\n\nTURN EXECUTION ORDER \u2014 follow this sequence on every single turn without exception:\n- STEP A: Extract all information from the user's current message\n- STEP B: Call the update_chat_session_record tool immediately with all confirmed non-empty values \u2014 always before any lookup tool\n- STEP C: If first name or last name is now known, call the lookup_past_session_by_name tool\n- STEP D: If email is now known, call the lookup_past_session_by_email tool\n- STEP E: Formulate and deliver your response to the user\n\nCONVERSATION FLOW GUIDELINES:\n1. Greet the user warmly and introduce yourself as TARS\n2. If session context contains user data, personalise the greeting immediately \u2014 deliver the welcome back moment naturally using available fields\n3. After the user's first response, politely ask for their first name so you can address them properly \u2014 skip if name is already known\n4. Once first name or last name is known, the lookup_past_session_by_name tool will be called per Turn Execution Order \u2014 ask the user to verify their identity with last name and email\n5. If email is not yet known, ask for it naturally framing it as helping you recall your previous conversation accurately \u2014 skip if email is already known\n6. Once email is known, the lookup_past_session_by_email tool will be called per Turn Execution Order\n7. Confirm the user's identity neutrally before copying any data from the lookup_past_session_by_email tool results \u2014 never hint at what was found\n8. After confirmed identity, deliver the welcome back moment naturally with context drawn from the past session record\n9. Once name and email are confirmed, ask for company name naturally in the flow of conversation \u2014 skip if company is already known\n10. Continue the sales conversation \u2014 answer questions and capture preferences throughout\n11. Always apply the APPEND rule for cumulative fields \u2014 never overwrite them with partial or incomplete data"
},
"promptType": "define"
},
"typeVersion": 3.1
},
{
"id": "057dcf96-e737-4140-ab5d-f8ccbe858546",
"name": "Simple Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"position": [
-2080,
-1168
],
"parameters": {
"sessionKey": "={{ $('When chat message received').item.json.sessionId }}",
"sessionIdType": "customKey",
"contextWindowLength": 10
},
"typeVersion": 1.4
},
{
"id": "a63fed3d-6c1f-48d1-8425-cf7603555785",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"position": [
-3184,
-1344
],
"parameters": {
"options": {
"responseMode": "lastNode"
}
},
"typeVersion": 1.4
},
{
"id": "e7f6c082-9695-4edd-bbb7-44c90dad8442",
"name": "Search session_id",
"type": "n8n-nodes-base.dataTable",
"onError": "continueRegularOutput",
"position": [
-2960,
-1344
],
"parameters": {
"limit": 1,
"filters": {
"conditions": [
{
"keyName": "session_id",
"keyValue": "={{ $json.sessionId }}"
}
]
},
"matchType": "allConditions",
"operation": "get",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "JUMxTSmCj7f9d2eb",
"cachedResultUrl": "/projects/5H13n2bJGBIJuul2/datatables/JUMxTSmCj7f9d2eb",
"cachedResultName": "chat_session_data"
}
},
"typeVersion": 1.1,
"alwaysOutputData": true
},
{
"id": "9e97c00e-1a8c-4b8b-9ace-298e102969f0",
"name": "Found session_id",
"type": "n8n-nodes-base.if",
"position": [
-2752,
-1344
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 3,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "a3b94ac8-2b85-47d6-b39b-c8c5bdbfff6b",
"operator": {
"type": "object",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2.3
},
{
"id": "e0e0823b-4a9b-4b29-b2cf-3d1de0a0263b",
"name": "New session record",
"type": "n8n-nodes-base.dataTable",
"position": [
-2528,
-1184
],
"parameters": {
"columns": {
"value": {
"channel": "chat",
"channel_id": "chat-n8n",
"first_name": "=",
"session_id": "={{ $('When chat message received').item.json.sessionId }}",
"execution_time": "={{ $now.toISO() }}"
},
"schema": [
{
"id": "user_id",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "user_id",
"defaultMatch": false
},
{
"id": "session_id",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "session_id",
"defaultMatch": false
},
{
"id": "execution_time",
"type": "dateTime",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "execution_time",
"defaultMatch": false
},
{
"id": "channel",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "channel",
"defaultMatch": false
},
{
"id": "channel_id",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "channel_id",
"defaultMatch": false
},
{
"id": "first_name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "first_name",
"defaultMatch": false
},
{
"id": "last_name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_name",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "JUMxTSmCj7f9d2eb",
"cachedResultUrl": "/projects/5H13n2bJGBIJuul2/datatables/JUMxTSmCj7f9d2eb",
"cachedResultName": "chat_session_data"
}
},
"typeVersion": 1.1
},
{
"id": "32a82faa-018e-4425-9c02-6f4b625e1c43",
"name": "Load past session data",
"type": "n8n-nodes-base.set",
"position": [
-2528,
-1408
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "7c01f986-a3ce-4edc-aa14-95c9ee0db459",
"name": "user_id",
"type": "string",
"value": "={{ $json.user_id }}"
},
{
"id": "13c97fac-e653-4748-a4d3-8935fa37edfc",
"name": "session_id",
"type": "string",
"value": "={{ $json.session_id }}"
},
{
"id": "b8267c52-5a12-4540-b452-2384740ea6ab",
"name": "execution_time",
"type": "string",
"value": "={{ $json.execution_time }}"
},
{
"id": "17135e29-3b56-451b-9849-3128be80210e",
"name": "channel",
"type": "string",
"value": "={{ $json.channel }}"
},
{
"id": "c6bd27d2-a667-4990-92fd-1e1a951c8503",
"name": "channel_id",
"type": "string",
"value": "={{ $json.channel_id }}"
},
{
"id": "898360b4-67f0-4a55-8052-bdaba08d7359",
"name": "first_name",
"type": "string",
"value": "={{ $json.first_name }}"
},
{
"id": "8d791ee9-af3c-48f2-b61e-d74e398cd9cb",
"name": "last_name",
"type": "string",
"value": "={{ $json.last_name }}"
},
{
"id": "b9c89743-8bc8-499e-ad0f-112450fa5feb",
"name": "last_topic",
"type": "string",
"value": "={{ $json.last_topic }}"
},
{
"id": "651d063f-bdc6-4582-a6b6-5298ba2b3553",
"name": "last_intent",
"type": "string",
"value": "={{ $json.last_intent }}"
},
{
"id": "1ea2844c-0a7b-4658-9e15-d371de2e5403",
"name": "unresolved_question",
"type": "string",
"value": "={{ $json.unresolved_question }}"
},
{
"id": "09998458-ba28-4315-9433-439dedbeffaf",
"name": "conversation_stage",
"type": "string",
"value": "={{ $json.conversation_stage }}"
},
{
"id": "22f7a8e8-f8d5-4549-9acb-adb99f34c85b",
"name": "last_session_date",
"type": "string",
"value": "={{ $json.last_session_date }}"
},
{
"id": "4e83002d-9b49-4344-8685-f87e3716d7c7",
"name": "interested_in",
"type": "string",
"value": "={{ $json.interested_in }}"
},
{
"id": "926bd3af-4bea-4aa7-b0f3-358b85c4bd25",
"name": "company",
"type": "string",
"value": "={{ $json.company }}"
},
{
"id": "c9aad711-8a01-46be-8715-4c0b9a98de08",
"name": "chat_history",
"type": "string",
"value": "={{ $json.chat_history }}"
},
{
"id": "b71da80c-8895-4c36-853f-6b5bd655e5be",
"name": "id",
"type": "number",
"value": "={{ $json.id }}"
},
{
"id": "542679ae-fe33-4fd9-bec9-b45dc5c612f7",
"name": "createdAt",
"type": "string",
"value": "={{ $json.createdAt }}"
},
{
"id": "229899cc-cc98-4dd2-9a11-08201b433ebd",
"name": "updatedAt",
"type": "string",
"value": "={{ $json.updatedAt }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "f3300c48-708a-425c-bab8-7f83f7c90d73",
"name": "OpenAI Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"position": [
-2272,
-1168
],
"parameters": {
"model": {
"__rl": true,
"mode": "list",
"value": "gpt-5.3-chat-latest",
"cachedResultName": "gpt-5.3-chat-latest"
},
"options": {},
"responsesApiEnabled": false
},
"credentials": {
"openAiApi": {
"name": "<your credential>"
}
},
"typeVersion": 1.3
},
{
"id": "9bfb5a38-1f7e-4a93-b1fe-ab40c03159eb",
"name": "lookup_past_session_by_email",
"type": "n8n-nodes-base.dataTableTool",
"position": [
-1776,
-1184
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "user_id",
"keyValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('conditions0_Value', `Email address from current session variable data value or from chat input message.`, 'string') }}"
}
]
},
"operation": "get",
"returnAll": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Return_All', `There will be many past session records with the same email. Use only the record with latest updatedAt date field.`, 'boolean') }}",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "JUMxTSmCj7f9d2eb",
"cachedResultUrl": "/projects/5H13n2bJGBIJuul2/datatables/JUMxTSmCj7f9d2eb",
"cachedResultName": "chat_session_data"
},
"descriptionType": "manual",
"toolDescription": "This is the lookup_past_session_by_email tool. Use this tool to look-up data table for past session records by email address field in the data table. Current session email address can be from variable data value or from chat input message. Use this tool as soon as email address is known so that you can quickly retrieve user's preferences and personality from past session record to be used in current session."
},
"typeVersion": 1.1
},
{
"id": "a842705a-2199-4bd7-9a12-2c5c30dfde11",
"name": "update_chat_session_record",
"type": "n8n-nodes-base.dataTableTool",
"position": [
-1680,
-1344
],
"parameters": {
"columns": {
"value": {
"company": "={{ $fromAI('company', `Company name`, 'string') || $('Search session_id').item.json.company }}",
"user_id": "={{ $fromAI('user_id', `Email address`, 'string') || $('Search session_id').item.json.user_id }}",
"last_name": "={{ $fromAI('last_name', `Last name`, 'string') || $('Search session_id').item.json.last_name }}",
"first_name": "={{ $fromAI('first_name', `First name`, 'string') || $('Search session_id').item.json.first_name }}",
"last_topic": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('last_topic', `User's last topic discussed in chat session`, 'string') }}",
"last_intent": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('last_intent', `User's last intention discussed in chat session`, 'string') }}",
"chat_history": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('chat_history', `Chat history of what user and AI said, turn by turn and session by session.`, 'string') }}",
"interested_in": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('interested_in', `User's interest captured from chat session`, 'string') }}",
"last_session_date": "={{ $fromAI('last_session_date', `Last session date`, 'string') || $('Search session_id').item.json.last_session_date }}",
"conversation_stage": "={{ $fromAI('conversation_stage', `Conversation stage`, 'string') || $('Search session_id').item.json.conversation_stage }}",
"unresolved_question": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('unresolved_question', `Any questions that user asked and answered but not satisfactory to the user`, 'string') }}"
},
"schema": [
{
"id": "user_id",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "user_id",
"defaultMatch": false
},
{
"id": "session_id",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "session_id",
"defaultMatch": false
},
{
"id": "execution_time",
"type": "dateTime",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "execution_time",
"defaultMatch": false
},
{
"id": "channel",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "channel",
"defaultMatch": false
},
{
"id": "channel_id",
"type": "string",
"display": true,
"removed": true,
"readOnly": false,
"required": false,
"displayName": "channel_id",
"defaultMatch": false
},
{
"id": "first_name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "first_name",
"defaultMatch": false
},
{
"id": "last_name",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_name",
"defaultMatch": false
},
{
"id": "last_topic",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_topic",
"defaultMatch": false
},
{
"id": "last_intent",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_intent",
"defaultMatch": false
},
{
"id": "unresolved_question",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "unresolved_question",
"defaultMatch": false
},
{
"id": "conversation_stage",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "conversation_stage",
"defaultMatch": false
},
{
"id": "last_session_date",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "last_session_date",
"defaultMatch": false
},
{
"id": "interested_in",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "interested_in",
"defaultMatch": false
},
{
"id": "company",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "company",
"defaultMatch": false
},
{
"id": "chat_history",
"type": "string",
"display": true,
"removed": false,
"readOnly": false,
"required": false,
"displayName": "chat_history",
"defaultMatch": false
}
],
"mappingMode": "defineBelow",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"filters": {
"conditions": [
{
"keyName": "session_id",
"keyValue": "={{ $('When chat message received').item.json.sessionId }}"
}
]
},
"options": {},
"operation": "update",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "JUMxTSmCj7f9d2eb",
"cachedResultUrl": "/projects/5H13n2bJGBIJuul2/datatables/JUMxTSmCj7f9d2eb",
"cachedResultName": "chat_session_data"
},
"descriptionType": "manual",
"toolDescription": "This is update_chat_session_record tool. Use this tool to update data table fields with data from variables. Update data table fields with new values from current conversation. Only pass fields that have confirmed non-empty values. Never pass empty, null or blank values."
},
"typeVersion": 1.1
},
{
"id": "8440ec6f-fa2b-42e6-9f7f-f5212e3bbac6",
"name": "lookup_past_session_by_name",
"type": "n8n-nodes-base.dataTableTool",
"position": [
-1568,
-1184
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "first_name",
"keyValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('conditions0_Value', `First name from current session variable data value or from chat input message.`, 'string') }}"
},
{
"keyName": "last_name",
"keyValue": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('conditions1_Value', `First name from current session variable data value or from chat input message.`, 'string') }}"
}
]
},
"operation": "get",
"returnAll": true,
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "JUMxTSmCj7f9d2eb",
"cachedResultUrl": "/projects/5H13n2bJGBIJuul2/datatables/JUMxTSmCj7f9d2eb",
"cachedResultName": "chat_session_data"
},
"descriptionType": "manual",
"toolDescription": "This is the lookup_past_session_by_name tool. Use this tool to look-up data table for past session records by first_name and last_name field in the data table. Current session first name and last name can be from variable data value or from chat input message. Use this tool as soon as either first or last name is known so that you can quickly retrieve user's preferences and personality from past session record to be used in current session."
},
"typeVersion": 1.1
},
{
"id": "b3c11517-4ca2-480c-9339-fb84a299a3ea",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-4560,
-1968
],
"parameters": {
"color": "#F0E805",
"width": 1280,
"height": 1232,
"content": "# Multi-Session Chat AI Agent That Remembers Past Conversations Using n8n Data Table As Long Term Memory\n\n\n## What Problem This Workflow Is Solving - AI Agents Forgets After Session Ends\n\n- AI chat agents are stateless by default \u2014 every new conversation starts from zero with no memory of who the user is or what was discussed before\n- Users are forced to repeat themselves every time they start a new chat session \u2014 frustrating experience and lost context\n- Most memory solutions require external databases, vector stores, or third-party services \u2014 adding cost and complexity\n\n\n## Solution - Fast Turnaround Long Term Memory Using n8n Data Table\n\n- This workflow uses n8n's built-in data table as a lightweight persistent memory store \u2014 no external services needed, no API calls, no external database roundtrip\n- The agent silently captures and saves user information during natural conversation \u2014 users never fill out a form\n- Returning users are automatically recognized by name and email \u2014 past context is recalled and the conversation continues where it left off\n- A privacy-first identity verification step ensures user data is never exposed to someone who merely shares the same name\n\n\n## How It Works\n\n- Every new chat session creates a row in the n8n data table keyed by session ID\n- The agent extracts user information from conversation naturally and writes to the data table immediately at every turn\n- When a name is detected, the agent searches for past sessions with the same name\n- When an email is detected, the agent retrieves full past conversation context for that user\n- The user must confirm their identity before any past data is shown or merged \u2014 preventing data privacy leakage between users who share the same name\n- Memory fields are split into two types \u2014 single value facts (name, email, company) that overwrite, and cumulative history (topics, interests, chat log) that append\n- A sliding window of the last 10 conversation turns is kept in short-term memory for natural conversation flow\n- Timestamps are always written in the correct local timezone using the workflow timezone setting\n\n\n## How To Setup\n\n- Create a data table in n8n named chat_sessions_data with these fields: session_id, channel, channel_id, user_id, first_name, last_name, company, last_topic, last_intent, unresolved_question, conversation_stage, last_session_date, interested_in, chat_history, execution_time, current_time\n- Add your OpenAI API key to the OpenAI Chat Model node\n- Set your local timezone under workflow Settings\n\n\n## How To Use\n\n- Activate the chat trigger,open the chat interface \n- Test first session stating first name, last name and email and partial information on intent of the conversation\n- Test a second session with the same name and email to see the welcome back moment in action. Ask the chat AI agent about \"Where did we stopped?\"\n\n\n## Customizations (Optional)\n\n- Change the agent name, company name, and role description in the system prompt to match your own use case\n- Add or remove data table fields to capture different information relevant to your domain\n- Increase or decrease the sliding window size in the Simple Memory node (default: 10 turns)\n- Swap OpenAI for any other LLM that supports tool calling (Anthropic Claude, Google Gemini, etc.)\n- Replace email as the identity verification key with phone number or membership ID by updating the lookup tool filters\n- Extend to additional chat channels such as WhatsApp or Telegram by adding a channel-specific trigger and reusing the same data table\n- Write a schedule triggered workflow to clean-up old session records based on updateAt date, or just manually delete "
},
"typeVersion": 1
},
{
"id": "5f7c71d6-f76d-47cc-9805-b323303883ff",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2992,
-1520
],
"parameters": {
"width": 608,
"height": 512,
"content": "## Initialize Session Memory\n"
},
"typeVersion": 1
},
{
"id": "3fb4e180-66fb-41fd-8fc2-8f5aeee1643f",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2352,
-1520
],
"parameters": {
"width": 448,
"height": 512,
"content": "## Handle Chat Conversation With Past and Current Memory \n\n"
},
"typeVersion": 1
},
{
"id": "351d38f2-7468-4e60-87cf-653c57449857",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1872,
-1520
],
"parameters": {
"width": 448,
"height": 512,
"content": "## Search Past Memory, Update Memory With Latest Conversation\n\n"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"timezone": "Asia/Kuala_Lumpur",
"binaryMode": "separate",
"callerPolicy": "workflowsFromSameOwner",
"timeSavedMode": "dynamic",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "fabf97d0-f257-421b-b4fb-6f7d279b095d",
"connections": {
"AI Agent": {
"main": [
[]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"Found session_id": {
"main": [
[
{
"node": "Load past session data",
"type": "main",
"index": 0
}
],
[
{
"node": "New session record",
"type": "main",
"index": 0
}
]
]
},
"OpenAI Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Search session_id": {
"main": [
[
{
"node": "Found session_id",
"type": "main",
"index": 0
}
],
[]
]
},
"New session record": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Load past session data": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"When chat message received": {
"main": [
[
{
"node": "Search session_id",
"type": "main",
"index": 0
}
]
]
},
"update_chat_session_record": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"lookup_past_session_by_name": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"lookup_past_session_by_email": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
}
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
openAiApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow powers an OpenAI-based chat agent in n8n that persists long-term conversation memory in an n8n Data Table by session ID, optionally recalling past context via name/email lookups while enforcing an identity-verification flow. Receives a chat message via the n8n Chat…
Source: https://n8n.io/workflows/16052/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
This project is an automation workflow that generates a personalized resume and cover letter for each job listing. Generates an HTML resume from your data. Hosts it live on GitHub Pages. Converts it t
This template is designed to collect execution data from your AI workflows and generate an interactive dashboard for easy monitoring. It's compatible with any AI Agent or RAG workflow in n8n. Track me
This AI Agent helps you create short links from your original URLs. Each generated short link is automatically stored in a database table for easy management and tracking. Provide a long URL to the Ag
This workflow serves a Question and Answer chat experience to an end user. It uses an AI Agent with a tool to fetch Question and Answer pairs stored in a Data Table (to serve the user answers grounded
HDW Lead Geländewagen. Uses chatTrigger, lmChatOpenAi, memoryBufferWindow, outputParserStructured. Chat trigger; 92 nodes.