n8n AI agent memory is a sub-node on the AI Agent's Memory port. Simple Memory keeps the last few exchanges in the n8n process and is lost on restart. Postgres Chat Memory stores every message in a table, which works on Supabase. A pgvector store attached as a tool adds recall across sessions. Start with the first, move to Postgres for production, and add pgvector only when users expect to be remembered. The template below has all three in one workflow.
n8n AI agent memory is a sub-node you connect to the Memory port of the AI Agent node: Simple Memory keeps the last few exchanges in the n8n process, Postgres Chat Memory stores them in a database table, and a pgvector store attached as a tool lets the agent search older conversations by meaning. Start with Simple Memory, switch to Postgres when chats have to survive a restart, and add pgvector only when the agent must recall something from a past session.
Checked October 2026 against n8n's docs for Simple Memory, Postgres Chat Memory, the PGVector Vector Store and the Chat Trigger, the source of n8n 2.42.6, Supabase's connection and pricing pages, and the Anthropic and OpenAI price lists.
This tutorial is part of our n8n hub. It is the hands-on companion to AI agent memory options compared, which covers window, summary, vector and entity memory in general. Here you build the three that n8n supports out of the box, in one workflow you can import.
- 01Message arrives
The Chat Trigger outputs chatInput and a sessionId.
- 02Memory loads the window
The last N exchanges for that sessionId go into the prompt.
- 03Agent may search
If the user refers to an older chat, it calls long_term_memory.
- 04Model answers
With the window, plus any saved turns the search returned.
- 05Memory saves the exchange
The user message and the reply are appended to the session.
- 06Turn is embedded
The same exchange is stored as a vector for future sessions.
How does n8n AI agent memory work?
Every memory node in n8n is a window. On each turn it loads the stored messages for one session, hands the model the most recent ones, and saves the new exchange afterwards. Three settings decide the behaviour:
- Session ID. The key that separates one conversation from another. By default the node reads
sessionIdfrom a directly connected Chat Trigger. For any other trigger, choose Define below and pass your own key, such as a phone number or chat ID. - Context Window Length. How many past interactions the model receives. The default is 5, and one interaction is a user message plus the reply.
- Where it is stored. This is the only real difference between the memory nodes.
Two limits from the docs: only agents can use memory (chains cannot), and sub-node expressions always resolve against the first input item. Here are the three tiers side by side:
| Tier 1 | Tier 2 | Tier 3 | |
|---|---|---|---|
| Node | Simple Memory | Postgres Chat Memory | Postgres PGVector Store, as a tool |
| Where the data lives | The running n8n process | Table n8n_chat_histories | Table n8n_vectors |
| Survives a restart | No | Yes | Yes |
| Works in queue mode | No | Yes | Yes |
| Remembers across sessions | No | No, each session ID is separate | Yes |
| What the model receives | The last N exchanges | The last N exchanges | The 4 closest saved turns, when the agent searches |
| Credentials | None | Postgres | Postgres and an embeddings model |
Tier 1: the n8n chat memory node (Simple Memory)
Simple Memory is the node to start with: connect it to the agent and conversations work, with no credential. Its own description says why: "Stores in n8n memory, so no credentials required". That is also its limit.
The one-hour rule is in MemoryBufferWindow.node.ts (cleanupStaleBuffers); the n8n docs do not mention it. None of this matters while you are building. Use tier 1 to get the prompt, the tools and the tone right, then swap the memory node. Nothing else in the workflow has to change.
Tier 2: n8n AI agent memory on Supabase (Postgres Chat Memory)
Postgres Chat Memory is the same window, backed by a table. Any Postgres works; Supabase is a convenient host because the same database can hold the vectors in tier 3.
- 1Copy the Session pooler details
In Supabase, open Connect and choose Session pooler. Expected result: a host ending in pooler.supabase.com, port 5432, and a user in the form postgres.your-project-ref.
- 2Create a Postgres credential in n8n
Enter the host, database postgres, the user, your database password and port 5432, then set SSL as described below. Expected result: the connection test passes.
- 3Swap the memory node
Delete the line from Simple Memory to the agent and drag from Postgres Chat Memory to the Memory port. Expected result: one memory node attached.
- 4Send a chat message
Open the chat and say hello. Expected result: a reply, and a new table named n8n_chat_histories with two rows.
- 5Restart and continue
Restart n8n and send another message in the same session. Expected result: the agent still knows what you said before.
Why the Session pooler: Supabase's docs say the direct connection uses IPv6 unless you buy the IPv4 add-on, while the shared pooler works over IPv4 on every plan, and session mode is the one they recommend for persistent servers on IPv4 networks. n8n is a persistent server.
On SSL: Supabase's docs say to connect with SSL whenever possible, and the n8n Postgres credential defaults to Disable, so change it to Require. If the test then fails on the certificate, the credential's Ignore SSL Issues (Insecure) switch keeps the connection encrypted but skips verifying the server, which is the trade-off its name states.
What n8n writes is simple. The node creates the table if it does not exist, with three columns: id, session_id and message as JSON. Every turn adds two rows. On each call the node reads the whole session and sends the model the last N interactions, so the table keeps growing while the prompt does not. This is your n8n agent conversation history, and you can query it directly:
-- read one conversation
select id, message->>'type' as role, message->>'content' as content
from n8n_chat_histories
where session_id = 'YOUR-SESSION-ID'
order by id;
-- speed up lookups once the table grows (the node only creates a primary key)
create index if not exists n8n_chat_histories_session_idx
on n8n_chat_histories (session_id);
-- optional: add a timestamp so you can prune old chats
alter table n8n_chat_histories
add column if not exists created_at timestamptz default now();
delete from n8n_chat_histories
where created_at < now() - interval '90 days';One Supabase detail to plan around: the pricing page says Free plan projects are paused after one week of inactivity, and paid projects are not. A paused database means an agent with no memory and a failed execution, so give the workflow an error workflow; our Error Trigger guide has one ready to import. If you are new to Supabase itself, start with the Supabase tutorial.
Tier 3: pgvector semantic recall across sessions
A window forgets everything outside it, and a new session starts empty. Tier 3 fixes that with two additions: every finished turn is embedded and saved, and the agent gets a tool that searches those saved turns by meaning.
- "What did I just ask you?"
- "Change the second option you gave me"
- Anything said in the last N exchanges
- Nothing from last week's chat
- "What was the supplier I mentioned last month?"
- "Use the tone I asked for before"
- Any saved turn that is close in meaning
- Not reliable for exact order, dates or counts
In n8n this is the Postgres PGVector Store node, used twice with the same Postgres credential as tier 2:
- As a tool. Operation Mode set to Retrieve Documents (As Tool for AI Agent), connected to the agent's Tool port. The Description tells the model when to search, and Limit (default 4) sets how many saved turns come back.
- As a writer. Operation Mode set to Insert Documents, placed after the agent, with a Default Data Loader that turns the exchange into one document.
- With an embeddings model. Both need one. The template uses OpenAI's
text-embedding-3-small.
You do not need to prepare the database. The node creates the n8n_vectors table on first use, and the store it is built on runs create extension if not exists vector first. On Supabase you can also switch the extension on yourself under Database, Extensions.
The n8n docs list Metadata Filter under Get Many mode only. In the 2.42.6 source the same option is wired into the tool mode as well (VectorStorePGVector.node.ts), which is what the template relies on.
The template: three tiers in one importable n8n workflow
Copy the JSON, open a new workflow and paste it onto the canvas. Tier 1 is connected and works with one Anthropic credential. Tier 2 sits beside it, ready to swap in. Tier 3 is fully wired but deactivated, so it does nothing until you turn it on. Node types, versions and parameter names come from the n8n 2.42.6 source, and the versions chosen also exist in n8n 2.0.
{
"name": "AI agent memory: window, Postgres, pgvector",
"nodes": [
{
"parameters": {
"options": {}
},
"id": "d4b9f5c2-0001-4f3e-8b4d-000000000001",
"name": "When chat message received",
"type": "@n8n/n8n-nodes-langchain.chatTrigger",
"typeVersion": 1.3,
"position": [
0,
0
],
"webhookId": "d4b9f5c2-1000-4f3e-8b4d-000000000010"
},
{
"parameters": {
"options": {
"systemMessage": "You are a helpful assistant. If you have a long_term_memory tool and the user refers to something from an earlier conversation that is not in this chat, search it before you answer."
}
},
"id": "d4b9f5c2-0002-4f3e-8b4d-000000000002",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3,
"position": [
240,
0
]
},
{
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "claude-haiku-5-5"
},
"options": {}
},
"id": "d4b9f5c2-0003-4f3e-8b4d-000000000003",
"name": "Anthropic Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatAnthropic",
"typeVersion": 1.3,
"position": [
160,
220
]
},
{
"parameters": {
"contextWindowLength": 5
},
"id": "d4b9f5c2-0004-4f3e-8b4d-000000000004",
"name": "Simple Memory",
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [
320,
220
]
},
{
"parameters": {
"tableName": "n8n_chat_histories",
"contextWindowLength": 5
},
"id": "d4b9f5c2-0005-4f3e-8b4d-000000000005",
"name": "Postgres Chat Memory",
"type": "@n8n/n8n-nodes-langchain.memoryPostgresChat",
"typeVersion": 1.3,
"position": [
320,
400
]
},
{
"parameters": {
"mode": "retrieve-as-tool",
"toolDescription": "Search turns saved from this user's earlier conversations. Use it when the user mentions something that is not in the current chat.",
"tableName": "n8n_vectors",
"topK": 4,
"options": {
"metadata": {
"metadataValues": [
{
"name": "userId",
"value": "={{ $json.sessionId }}"
}
]
}
}
},
"id": "d4b9f5c2-0006-4f3e-8b4d-000000000006",
"name": "long_term_memory",
"type": "@n8n/n8n-nodes-langchain.vectorStorePGVector",
"typeVersion": 1.3,
"position": [
500,
220
],
"disabled": true
},
{
"parameters": {
"model": "text-embedding-3-small",
"options": {}
},
"id": "d4b9f5c2-0007-4f3e-8b4d-000000000007",
"name": "Embeddings OpenAI",
"type": "@n8n/n8n-nodes-langchain.embeddingsOpenAi",
"typeVersion": 1.2,
"position": [
600,
420
],
"disabled": true
},
{
"parameters": {
"mode": "insert",
"tableName": "n8n_vectors",
"options": {}
},
"id": "d4b9f5c2-0008-4f3e-8b4d-000000000008",
"name": "Save turn",
"type": "@n8n/n8n-nodes-langchain.vectorStorePGVector",
"typeVersion": 1.3,
"position": [
640,
0
],
"disabled": true
},
{
"parameters": {
"jsonMode": "expressionData",
"jsonData": "=User: {{ $('When chat message received').first().json.chatInput }}\nAssistant: {{ $json.output }}",
"options": {
"metadata": {
"metadataValues": [
{
"name": "userId",
"value": "={{ $('When chat message received').first().json.sessionId }}"
}
]
}
}
},
"id": "d4b9f5c2-0009-4f3e-8b4d-000000000009",
"name": "Turn as document",
"type": "@n8n/n8n-nodes-langchain.documentDefaultDataLoader",
"typeVersion": 1.1,
"position": [
760,
220
],
"disabled": true
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "d4b9f5c2-2000-4f3e-8b4d-000000000020",
"name": "output",
"value": "={{ $('AI Agent').first().json.output }}",
"type": "string"
}
]
},
"options": {}
},
"id": "d4b9f5c2-0010-4f3e-8b4d-000000000011",
"name": "Reply",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
880,
0
]
}
],
"connections": {
"When chat message received": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Save turn",
"type": "main",
"index": 0
}
]
]
},
"Save turn": {
"main": [
[
{
"node": "Reply",
"type": "main",
"index": 0
}
]
]
},
"Anthropic Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"long_term_memory": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"Embeddings OpenAI": {
"ai_embedding": [
[
{
"node": "long_term_memory",
"type": "ai_embedding",
"index": 0
},
{
"node": "Save turn",
"type": "ai_embedding",
"index": 0
}
]
]
},
"Turn as document": {
"ai_document": [
[
{
"node": "Save turn",
"type": "ai_document",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}- Tier 1: select your Anthropic credential on the chat model, open the chat and hold a three-message conversation
- The model is set to claude-haiku-5-5; change it to any model your key can use
- Tier 2: add the Postgres credential, then move the Memory connection from Simple Memory to Postgres Chat Memory
- Check that n8n_chat_histories has rows, restart n8n and confirm the agent still remembers
- Tier 3: activate long_term_memory, Embeddings OpenAI, Save turn and Turn as document
- Select the Postgres credential on both vector nodes and an OpenAI credential on the embeddings node
- Replace the two userId expressions with a stable user ID
- Start a new chat session and ask about something from the old one
Two design notes. The last node, Reply, exists because the Chat Trigger answers with the output of the last node that ran; the n8n docs say a manual reply must be a field named output or text, so Reply copies the agent's answer back into output after the save step. And Save turn stores every exchange, including "thanks". Once the basics work, put an If node in front of it and skip short turns, or your searches fill up with noise.
What does each tier cost per 1,000 conversations?
Mostly input tokens, because the window is sent again on every turn. Storage barely registers. The table below is arithmetic on published prices, not a measurement, and every input is an assumption you should replace with your own.
| Tier | Input tokens | Output tokens | Embedding tokens | Claude Haiku 5.5 | Claude Sonnet 5.5 |
|---|---|---|---|---|---|
| 1. Simple Memory, window of 5 | 11.0M | 1.0M | 0 | $1.60 | $32.00 |
| 2. Postgres Chat Memory, window of 5 | 11.0M | 1.0M | 0 | $1.60 plus the database plan | $32.00 plus the database plan |
| 3. Window plus pgvector recall | 17.7M | 1.1M | 2.1M | $2.36 plus the database plan | $46.32 plus the database plan |
- Tiers 1 and 2 cost the same in tokens. Postgres adds a database bill, which is a plan price and not a per-conversation fee. On Supabase that is the Free plan with 500 MB per project, or Pro from $25 a month with 8 GB of disk (checked October 2026).
- The window is the dial. With these inputs, raising Context Window Length from 5 to 10 lifts input from 11.0M to 13.0M tokens. In longer conversations the gap widens, because the window stays full for more turns.
- Embeddings are nearly free. Saving and searching 1,000 conversations costs about four cents. The price of recall is the extra model call and the saved turns it reads, about 71 cents of the Haiku total.
- The model outweighs the memory. Every row is about 20 times higher on Sonnet 5.5. Pick the memory tier for what the agent must remember and the model for how hard the task is.
Our Claude API pricing breakdown covers caching and batch discounts, which change these numbers further. When memory, retrieval and model choice turn into product decisions, the AI SaaS Builder program goes deeper: it covers Supabase from schema to Row Level Security, and adding RAG with embeddings and pgvector to an app you ship.
Troubleshooting n8n agent memory
- "No sessionId". The memory node expects a
sessionIdfrom a connected Chat Trigger. With a Webhook, WhatsApp or Telegram trigger, set Session ID to Define below and pass your own key. - The agent mixes up two conversations. Both are using the same session key. The docs warn that several memory nodes in one workflow share one memory instance unless you give them different session IDs.
- The agent forgets mid-conversation. Either Context Window Length is too low for the task, or you are on Simple Memory and the process restarted.
- Memory works in the editor and fails in production. Simple Memory with queue mode. Move to Postgres or Redis memory.
- Supabase connection times out. Check whether you used the direct connection host from a network without IPv6. Use the Session pooler host and the user with the project reference.
- The search tool is never called. Sharpen its Description and name the tool in the system message, as the template does.
If you would rather run the whole stack on your own machine first, n8n's self-hosted kit bundles a PostgreSQL database you can point tier 2 at, and ships Qdrant as its vector store; our AI starter kit guide explains what is in it.
n8n AI agent memory: FAQ
How do I add memory to an n8n AI agent?
Connect a memory sub-node to the Memory port under the AI Agent node. Simple Memory needs no setup and keeps the last five exchanges by default. For history that survives a restart, connect Postgres Chat Memory with a Postgres credential instead. An agent takes one memory node at a time, and the session ID decides which conversation it loads.
Does n8n Simple Memory persist?
No. Simple Memory keeps chat history inside the running n8n process. It is lost when n8n restarts, the node's own notice says it is not compatible with queue mode or multi-main setups, and the n8n 2.42.6 source clears a session that has not been used for an hour. Use it for testing and single-instance prototypes. Checked October 2026.
How do I use Supabase for n8n AI agent memory?
Create a Postgres credential in n8n with the Session pooler details from the Connect dialog in Supabase: the pooler host, port 5432, database postgres and the user postgres followed by a dot and your project reference. Select that credential in a Postgres Chat Memory node and connect it to the agent. n8n creates the n8n_chat_histories table on the first message.
Where does n8n store agent conversation history?
It depends on the memory node. Simple Memory holds it in the n8n process. Postgres Chat Memory writes one row per message to a table named n8n_chat_histories, with an id, a session_id and the message as JSON. Redis and MongoDB memory nodes write to those systems. The model only receives the last few exchanges set by Context Window Length.
What does Context Window Length mean in n8n memory nodes?
It is the number of past interactions sent to the model on each turn, where one interaction is a user message plus the reply. The default is 5. A higher number lets the agent refer further back and raises the input tokens you pay for on every turn. The Postgres node still stores the full history; the setting only limits what is loaded into the prompt.
Do I need pgvector for n8n agent memory?
Only when the agent must recall things from earlier conversations that are no longer in the window. A window covers a single chat. A pgvector store attached to the agent as a tool lets it search saved turns by meaning. That costs an embedding call per saved turn and an extra model call each time the agent searches, so add it when users expect to be remembered.
Your agent now remembers its users. Next, give it a product to live in.
AI SaaS Builder, included in All Access, covers the same building blocks in application code: a Supabase schema with Row Level Security, Claude API features, and RAG with embeddings and pgvector. All Access adds the other three programs, live coaching and the private community.
Start with the free Creator Starter Kit
Templates and checklists for your first automations, free.