Beginner · about 2-4 hours
Build an n8n AI agent workflow with memory, a tool and error handling
Turn website enquiries into drafted replies and team alerts with an n8n AI Agent node that looks up your price sheet, remembers earlier messages from the same person, and fails safely when a service is down.
You'll end up with: Every website enquiry gets a drafted, price-accurate reply waiting in your inbox, and anything unusual or broken is flagged to a person within a minute.
Before you start
- □An n8n instance on a current 2.x release, Cloud or self-hosted
- □An Anthropic API key saved as an n8n credential
- □A Google Sheet with your services and prices (columns: service, price, notes)
- □Gmail and Slack accounts you can connect as n8n credentials
Architecture
Webhook trigger
Your website form POSTs name, email and message to an n8n production webhook URL protected by header auth.
Input check (If node)
Stops the run early when email or message is missing, so bad input never reaches the model.
AI Agent node
The Tools Agent: reads the enquiry with a system message and decides whether to call its tool.
Sub-nodes: model, memory, tool
Anthropic Chat Model for reasoning, Simple Memory keyed by email, and a Google Sheets tool that reads the price sheet.
Structured Output Parser
Forces the agent's answer into fixed fields: reply draft, category and a needs_human flag.
Route and act
An If node creates a Gmail draft for routine enquiries and posts to Slack when needs_human is true.
Error path and error workflow
The agent's error output posts a manual-follow-up alert; a separate Error Trigger workflow catches anything else.
Build it, step by step
Tick steps off as you build.
Create the webhook trigger and check the input
Add a Webhook node with method POST, a path such as enquiry, and Respond set to Immediately so your form doesn't wait on the AI. Add Header Auth under authentication so random traffic can't trigger paid model calls.
Add an If node straight after it that continues only when both email and message are non-empty. Without it, an empty submission still costs a model call and may poison the memory for an empty session key.
text Webhook node HTTP Method: POST Path: enquiry Authentication: Header Auth (credential holds the shared secret) Respond: Immediately If node (continue only when true) {{ $json.body.email }} is not empty {{ $json.body.message }} is not emptyAdd the AI Agent node
Add an AI Agent node. Since n8n 1.82.0 every AI Agent works as a Tools Agent, so there is no agent type to choose. Set Source for Prompt to Define below and build the prompt from the webhook fields. Put your rules in the System Message option, and lower Max Iterations from its default of 10 to about 4: a confused agent that keeps calling tools burns executions and tokens.
text Source for Prompt: Define below Prompt (User Message): Enquiry from {{ $('Webhook').item.json.body.name }}: {{ $('Webhook').item.json.body.message }} Options > System Message: You draft replies for Brightline Renovations. Look up every price with the price_sheet tool. Never quote a price the tool did not return. If the enquiry is a complaint, a legal issue, or asks for something not on the sheet, set needs_human to true. Options > Max Iterations: 4Connect the chat model
On the agent's Chat Model connector, add the Anthropic Chat Model sub-node and select your Anthropic credential. Choose a fast, low-cost model for this kind of short drafting work. Keep the API key in n8n's credential store, never in an expression or a Set node, so it isn't copied into execution data or exported workflows.
Add memory keyed by sender
On the Memory connector, add Simple Memory. Set the Session Key to the sender's email so a second message from the same person is answered with the first one in mind, and set Context Window Length to around 5 previous interactions.
Simple Memory doesn't work in an active production workflow if your instance runs in queue mode, because n8n can't guarantee each call reaches the same worker. In that case use Postgres Chat Memory or Redis Chat Memory with the same session key.
text Simple Memory Session Key: {{ $('Webhook').item.json.body.email.toLowerCase() }} Context Window Length: 5Give the agent one tool
On the Tool connector, add a Google Sheets tool that reads rows from your price sheet. Name it price_sheet and write a description that tells the agent when to use it. The agent chooses tools by their descriptions, so 'Gets data' is too vague; say what the data is and when to look it up.
text Tool: Google Sheets Name: price_sheet Description: Returns our services with price and notes. Call it whenever the enquiry mentions a service, a price or a budget. Operation: Get Row(s) from sheet "Prices"Require a structured output
Turn on Require Specific Output Format on the agent and connect a Structured Output Parser. Give it an example of the exact shape you want. Downstream nodes can then read fields such as needs_human directly instead of searching free text, which breaks the first time the wording changes.
json { "reply_draft": "Hi Sam, thanks for getting in touch...", "category": "quote_request", "needs_human": false }Route the result to an action
Add an If node on the agent's output. When needs_human is false, use the Gmail node to create a draft (not send) addressed to the sender with reply_draft as the body. When it is true, post the enquiry and the draft to a Slack channel. Drafts keep a person in the loop while the workflow is new.
text If: {{ $json.output.needs_human }} is true true -> Slack: post to #enquiries "Needs a person: {{ $('Webhook').item.json.body.email }}" false -> Gmail: Create draft To: {{ $('Webhook').item.json.body.email }} Message: {{ $json.output.reply_draft }}Add error handling at two levels
In the AI Agent node's settings, turn on Retry On Fail (for example 3 tries, 5 seconds apart) and set On Error to Continue (using error output). Wire that error output to a Slack message saying the enquiry needs a manual reply, so a model outage never silently drops a lead.
Then create a second workflow that starts with an Error Trigger node and posts the workflow name and execution URL to Slack. In the main workflow's Settings, select it as the Error Workflow. It runs when any execution fails, including failures outside the agent.
Test, then publish
Run the workflow with the test URL and send sample enquiries from a tool such as curl or your form's preview. When every test in the Testing section passes, publish the workflow and point your form at the production URL. The test and production URLs are different, and only the production URL works once the editor is closed.
Example run
- The form POSTs {"name": "Sam Ortiz", "email": "sam@example.com", "message": "How much to repaint a 3-bedroom interior? Budget around 4k."}.
- The If node passes it: email and message are present.
- The AI Agent calls price_sheet, which returns the row 'Interior repaint, 3 bed | 3,600 | excludes ceilings'.
- The parser returns {"reply_draft": "Hi Sam, our 3-bedroom interior repaint is 3,600, which excludes ceilings...", "category": "quote_request", "needs_human": false}.
- Gmail creates a draft to sam@example.com. Your team reviews and sends it.
- Two days later Sam writes 'Can you add ceilings?'. Simple Memory supplies the earlier exchange, the agent looks up the ceiling price, and a new draft refers back to the original quote.
Test it
- ✓Send a normal quote request and confirm the price in the draft matches the sheet exactly.
- ✓Failure case: send a payload with no email field. The If node should stop the run and no model call should appear in the execution.
- ✓Failure case: temporarily break the Anthropic credential (for example, point it at a revoked key). After the retries, the error output should post a manual-follow-up message to Slack.
- ✓Failure case: rename the price sheet tab. The tool call fails; confirm the agent doesn't invent prices and that the run is flagged for a person.
- ✓Send an enquiry about a service you don't offer and check that needs_human comes back true.
- ✓Send two messages from the same email and confirm the second draft uses context from the first.
Watch out for
- !Using Simple Memory on a queue-mode instance, where calls can land on different workers and memory goes missing. Use Postgres or Redis Chat Memory there.
- !A session key that can be empty or differently cased, which merges unrelated people into one conversation. Validate and lower-case the email first.
- !Leaving Max Iterations at 10 on a simple task, so a looping agent multiplies your cost per enquiry.
- !Vague tool descriptions. The agent picks tools from the description, so an unclear one means the tool is skipped or misused.
- !Sending replies automatically from day one. Create drafts until you have reviewed enough real outputs to trust the workflow.
- !Testing only with the test URL and forgetting to switch the form to the production URL after publishing.
