Module 6: Real Channels: Web Chat, WhatsApp, Telegram, and Voice
2. Chat Trigger and embedded web widget
Description
By the end of this lesson you'll be able to open your agent's first real channel: configure the Chat Trigger node in its two modes — the chat hosted by n8n and the chat embedded in your own site — choose wisely among its three response modes, protect access, and paste the @n8n/chat widget into a TuTienda web page with two lines of HTML. You'll also resolve, on this channel, the identity question lesson 1 planted: how to make the conversation history belong to the customer instead of the browser tab.
This matters because it's the first time in the whole guide that someone other than you can talk to your agent. Up to now everything happened inside n8n: the canvas's chat panel, test executions, the trace. By the end of this lesson there's going to be a URL you can send another person, or a widget on a page, and Module 5's system — triage_agent, order_specialist, billing_specialist — is going to answer on the other end. It's also the cheapest channel to make mistakes on: it's free, it comes included with n8n, it doesn't depend on anyone's approval, and if something breaks it gets fixed by reloading the page.
Connection to the module: this is the lesson where lesson 1's channel layer and adapter layer stop being a diagram. Chat Trigger is the friendliest channel layer you're going to meet, precisely because n8n controls all of it — there's no third party imposing rules on you. That's why it's worth learning the structural decisions here (response mode, authentication, session identity) while nothing else is failing; on WhatsApp, in lesson 3, those same decisions come back mixed in with Meta's paperwork and it's much harder to isolate what's broken. And everything you're bringing from Module 3 about sessionId and persistent memory comes into play in the final section.
The intercom and the front desk
Think of two ways to let people into a building.
The first is a complete front desk: a counter, someone staffing it, chairs, and a sign with the building's name. All of that already exists, you didn't design it, and it works well from day one. The only thing you can do is change the sign, put in different chairs, and decide who's allowed through. It's convenient and it's fast.
The second is an intercom on your own office's door, inside a building you built yourself. Nobody sees the original building's front desk: people come in through your door, with your sign, with your colors. You handle the facade; the intercom only handles getting the message to whoever needs to get it.
n8n's Chat Trigger node is exactly those two things, chosen with a selector. In Hosted Chat mode, n8n gives you the complete front desk: a chat page, hosted by n8n, with its own URL, ready to use. In Embedded Chat mode, n8n gives you only the intercom: a webhook URL your own web page talks to, and you build the facade.
The distinction matters because almost everyone starts in Hosted — it's the right thing for testing — and almost everyone ends up in Embedded, because a real business wants the chat inside its own site and not on an n8n domain. You're going to do both things in this lesson, in that order.
Anatomy of the Chat Trigger
Before touching anything, let's see what this node is. Chat Trigger — which shows up in the node list as "When chat message received" — is a trigger node doing three things at once, and that combination is what makes it different from a regular Webhook:
- It publishes an HTTP endpoint that receives the user's messages.
- It generates and carries a
sessionId, so the agent's memory can group a single conversation's turns without you doing anything. - Optionally, it serves a complete chat interface at that same URL (that's Hosted mode).
You already used it in Module 1 without configuring anything: you dragged it onto the canvas, the Chat panel appeared at the bottom, and with that you tested your first agent. That canvas panel is the local testing version. What this lesson does is turn it into a channel accessible from outside.
When the trigger receives a message, the output it hands the next node has this shape:
{
"sessionId": "a2f0c8b1e4d7...",
"action": "sendMessage",
"chatInput": "Hi, how's my order #4521 doing?"
}
Three fields and none of them is extra. chatInput is the text the person wrote — it's the field the AI Agent node reads by default when its prompt source is set to "Connected Chat Trigger Node." sessionId is the conversation's identity, the one memory uses as its key. And action tells apart a normal message from a load of previous history, which you'll see below.
The exact field and option names can vary between n8n minor versions. Every time this lesson names a parameter, open it in the node's panel and confirm the label on your version before taking it for granted. It's a cheap habit that saves half an hour of confusion.
Step by step: Module 5's agent in Hosted mode
Let's connect the system you already have. If your triage_agent with its two specialists is in a workflow, open it; if you'd rather start with a simple agent so you don't risk the one that works, that's fine too — everything in this lesson applies just the same.
Step 1 — The trigger. If the workflow already starts with a Chat Trigger (it comes that way from Module 5), don't add another one. Just open it: up to now it's had its default values.
Step 2 — The mode. The node's first parameter is Make Chat Publicly Available or its equivalent, a switch deciding whether the conversation lives only in the canvas panel or whether there's also a public URL. Turn it on. As soon as you do, two URLs show up, and the difference between them is the source of this lesson's most common error:
Test Chat URL → only works WHILE you have the canvas open and
you clicked "Execute workflow". It's for testing.
Production Chat URL → always works, but ONLY if the workflow is
ACTIVE (the toggle at the top right).
What to expect. Copy the production URL, activate it by pasting it into another browser tab, and you'll see a chat page with a text box. If instead you see a 404 error or a message saying the webhook isn't registered, it's almost always one of two things: you used the test URL without the canvas running, or you used the production one with the workflow inactive. It's fine, it happens to everyone the first time.
Step 3 — The response mode. Here's the first real design decision. The Response Mode parameter has three values and each one changes how the response reaches the screen:
- When Last Node Finishes. The chat waits for the whole workflow to finish and then shows the last node's output. It's the default behavior and the simplest one: a message comes in, the agent reasons, a message comes out. For Module 5's system it works perfectly.
- Using Response Nodes. The chat doesn't wait for the last node: it waits for a
Chatnode (or aRespond to Webhook, in embedded mode) to explicitly say what to respond. Useful when you want to send more than one message, or send something before finishing the work, or pause waiting for user confirmation. - Streaming response. The response shows up word by word as the model generates it, instead of appearing all at once at the end. It's what makes a chat feel fast even if it takes the same amount of time.
The practical decision is this: start with When Last Node Finishes because it has the fewest moving parts. Switch to Using Response Nodes when you need to talk to the user midway through the process — the typical case is an agent that's going to take fifteen seconds and you want to give a heads-up before it does. And consider Streaming when the agent writes long responses and the silence becomes uncomfortable. An honest warning about streaming: not every model or configuration supports it the same way, and its behavior with multi-agent systems — where there's intermediate reasoning that shouldn't be shown — is worth testing before promising it. Verify on your version which combination works for you.
Step 4 — Authentication. The Authentication parameter defines who can open that chat:
- None. Anyone with the URL gets in. It's the right choice for a customer-support chat on a public website, and it's what you're going to use. But keep in mind what that means: anyone with the URL can consume your model account's tokens. Yes, anyone.
- Basic Auth. Asks for a username and password, the same for everyone. Useful for an internal demo or a chat only your team should see.
- n8n User Auth. Only users logged into your n8n instance get in. It's the most restrictive one and works for internal tools.
Here's a paragraph of honesty. A chat with Authentication: None and a shareable URL is, literally, a public endpoint connected to a model that charges per token. If someone finds that URL, they can write to it all day. That's not a reason not to publish it — it's the business model of any support chat — but it is a reason for two things: keep the production URL out of public repositories, and don't leave a forgotten open chat on a test instance. Spend control and real defenses are Module 7; for now, just have the awareness planted.
Step 5 — Presentation options. Under Options there's a set of fields that only affect Hosted mode, and that turn a generic page into something that looks like TuTienda's:
# Node: Chat Trigger — Options (Hosted Chat mode)
Title: TuTienda Support
Subtitle: We're here to help with your orders and charges
Input Placeholder: Type your message…
Initial Message(s): Hi! I'm TuTienda's assistant.
I can help with an order's status, a return,
or a charge you don't recognize.
Require Button Click to Start Chat: on
Allowed Origin (CORS): https://tutienda.example
Load Previous Session: Memory Connected to Agent
It's worth understanding what each one does, because three of them aren't cosmetic.
Initial Message(s) is the message the person sees before typing anything. The model doesn't generate it: it's fixed text you write, so it costs no tokens and is always identical. This matters more than it looks. A chat that opens blank gets asked all kinds of questions, many outside the agent's scope; a chat that opens saying "I can help with orders, returns, and charges" gets far fewer out-of-scope questions, because you just told the person what to expect. It's the cheapest way to bound a conversation: bounding it before it starts.
Require Button Click to Start Chat makes the widget show a "New conversation" button instead of opening straight into the text box. It sounds trivial and has a real effect: it prevents accidental triggers, and in the embedded window mode it gives the session a clear starting point.
Allowed Origin (CORS) is the field causing the most confusion, so it's worth explaining calmly. CORS is a browser rule: when a web page on domain A tries to make a request to domain B, the browser asks B whether that's allowed, and if B doesn't say yes, the browser blocks the request. It's not a defense against a determined attacker — anyone can call your webhook from outside a browser — but it does stop your chat from being embedded on someone else's site. The default value is usually *, meaning "any origin." Setting it to https://tutienda.example means "only TuTienda's page can embed me." During local development you're going to need to include your test origin too, something like http://localhost:8080, comma-separated.
Load Previous Session decides whether, on reopening the chat, the person sees the previous messages again. It requires a memory node connected to the agent, because that's where those messages come from. Without it, every page reload looks like a blank conversation — even though the agent does remember, because memory and the screen are different things. That nuance is subtle and confusing: the agent can remember perfectly well and the screen can still look empty, because Load Previous Session controls what gets painted, not what the model has in context.
Step 6 — Test it. Save, activate the workflow, open the production URL, and write: "Hi, how's my order #4521 doing?"
What to expect. After a few seconds — and yes, it's going to be several seconds, because triage_agent is delegating — the composed response shows up. If you open n8n's executions tab you're going to find that complete run, with its delegation trace, just like in Module 5. The only thing that changed is which door the message came through.
Perfect. You now have a channel. Anyone you send that URL to can talk to your agent.
Responding with the Chat node
When you choose Response Mode: Using Response Nodes, Chat Trigger stops responding on its own and waits for someone to tell it what to send. That someone is the Chat node (which in earlier versions was called "Respond to Chat" — another name worth verifying in your panel).
The node has two operations, and the second one is more interesting than it sounds:
- Send Message. Sends a message to the chat and the workflow keeps running. It's the one you use to say "give me a second, I'm checking the system" before starting the slow part.
- Send and Wait for Response. Sends a message and pauses execution until the person answers. When they answer, the workflow continues from there with that answer.
That second operation is a human-in-the-loop mechanism inside the channel itself, and it comes with a Response Type parameter with these options:
- Free Text: the person types whatever they want.
- Approval: the person sees buttons and clicks. You can configure whether only "approve" shows up or "approve and reject" too, customize the button text, and turn on Block User Input so they can't type free text while deciding.
Let's look at TuTienda's clearest use case:
# Flow with confirmation before a sensitive action
Chat Trigger (Response Mode: Using Response Nodes)
└─► AI Agent: triage_agent
└─► (the agent determines the order needs cancelling)
└─► Chat ── Operation: Send and Wait for Response
Message: "Confirming: I'm going to cancel order
#4521. The refund takes 5 to 7
business days. Should I cancel it?"
Response Type: Approval
Approve Button Label: Yes, cancel it
Disapprove Button Label: No, leave it
Block User Input: on
│
├── approved → execute the cancellation
└── rejected → respond that nothing was done
Notice what this pattern solves. Without it, the confirmation would have to be conversational: the agent asks "do you confirm?", the customer types "yes," and the agent has to interpret that "yes" — which could also be "yep," "sure," "yes but wait," or "yes, and cancel the other one too while you're at it." With buttons, the confirmation is a piece of data, not an interpretation. When the action is destructive, a piece of data is worth much more than an interpretation.
This same pattern, generalized and with criteria for when to apply it, is Module 7's human-in-the-loop lesson. Here you see it as a channel capability; there you're going to see it as a security policy.
One limitation worth knowing now: the Chat node works with Hosted mode. In Embedded mode, the response gets handled with the Respond to Webhook node, the generic equivalent. Verify the behavior on your version before designing a flow that depends on this.
Embedded mode: the chat inside tutienda.example
Now, the intercom. Switch Chat Trigger to Embedded Chat and watch what happens: the presentation options disappear. No Title, no Subtitle, no Initial Messages. It makes sense — those things belong to the interface, and in embedded mode the interface is yours. What's left is the webhook URL, the response mode, Allowed Origin (CORS), and Load Previous Session.
n8n publishes a package that builds that interface for you: @n8n/chat. It's a JavaScript library that draws the chat widget and talks to your webhook. It can be installed with npm in a project, but for a regular page the simplest route is loading it from a CDN. Two tags:
<!-- TuTienda's chat widget — paste before </body> -->
<!-- 1) The widget's styles -->
<link
href="https://cdn.jsdelivr.net/npm/@n8n/chat/dist/style.css"
rel="stylesheet"
/>
<!-- 2) The widget, with its configuration -->
<script type="module">
import { createChat } from 'https://cdn.jsdelivr.net/npm/@n8n/chat/dist/chat.bundle.es.js';
createChat({
// The Chat Trigger's PRODUCTION URL in embedded mode.
// If you put the test one, it works for a while and then stops
// working with no apparent explanation: the test one expires when
// you close the canvas.
webhookUrl: 'https://YOUR-N8N-INSTANCE/webhook/xxxxxxxx/chat',
// 'window' puts a floating bubble in the corner.
// 'fullscreen' fills the container you point it at with target.
mode: 'window',
// Welcome messages. They go here, not on the node: in embedded
// mode the interface is yours, so the text is too.
initialMessages: [
'Hi! I\'m TuTienda\'s assistant.',
'I can help with orders, returns, and charges.'
],
// Interface text.
i18n: {
en: {
title: 'TuTienda Support',
subtitle: 'We answer instantly, every day.',
inputPlaceholder: 'Type your message…',
getStarted: 'New conversation'
}
}
});
</script>
Two details in that block worth not overlooking.
The first is that the i18n object's key is en even though the text is in Spanish in the original. It's not an oversight: en is the widget's default language, and if you define your text there it works without configuring anything else. You can add more languages and pick which one to use, but for a single-language site this is the shortest way. It's exactly the kind of detail that wastes twenty minutes if nobody mentions it.
The second is webhookUrl. It has to be the production URL, and the workflow has to be active. If the widget shows up but every message fails silently, that's the first place to look — open the browser console and you'll see the real error, which is usually a 404 (inactive workflow) or a CORS error (your page's origin isn't in Allowed Origin).
Worked example: passing the customer's identity to the agent
Here we resolve the question lesson 1 left planted. The widget generates a random sessionId per browser, so the conversation belongs to the tab, not the person. If the customer comes in from their phone, that's a different sessionId and a different conversation.
But notice TuTienda's real situation: the chat is on a page where the customer has already logged in. Your server knows perfectly well who they are. It would be absurd for the agent not to know.
createChat accepts a metadata object whose content travels with every message to the workflow. That's where you pass the identity:
<script type="module">
import { createChat } from 'https://cdn.jsdelivr.net/npm/@n8n/chat/dist/chat.bundle.es.js';
createChat({
webhookUrl: 'https://YOUR-N8N-INSTANCE/webhook/xxxxxxxx/chat',
mode: 'window',
// Everything you put here reaches the workflow along with the message.
// YOUR server fills it in when rendering the page, with the data
// from the customer's authenticated session.
metadata: {
customer_id: 'C-9931',
plan: 'premium',
locale: 'en-US'
}
});
</script>
On n8n's side, that object arrives inside the Chat Trigger's payload. The exact path to it is one of the things worth verifying on your version — open a real execution and look at the node's output JSON before writing the expression — but it typically reads like this:
# Node: Postgres Chat Memory (connected to triage_agent)
Session ID: Define below
Key: {{ $('When chat message received').item.json.metadata.customer_id }}
What to expect. With this, memory stops grouping by tab and starts grouping by customer. The same C-9931 who chatted yesterday from their computer picks the thread back up today from their phone, because sessionId is no longer invented by the browser — your server sets it. It's Module 3's employee credential, applied to the web channel.
And there's a second, subtler use of the same mechanism. With customer_id available from the first turn, triage_agent no longer needs to ask the customer who they are. You can inject that data into the message the agent receives, so it starts the conversation already knowing who it's talking to. Compare the first two turns:
# WITHOUT metadata
Customer: how's my order doing?
Agent: Hi there! Sure, could you share your order number or the
email you used to buy it?
# WITH metadata (customer_id injected)
Customer: how's my order doing?
Agent: Hi there! Your most recent order, #4521, is on its way and
arrives Thursday. Is that the one you meant?
The second one feels ten times better and doesn't require a better model. It requires the adaptation layer to hand the brain a piece of data that was already available on the page. That's, in miniature, this whole layer's craft.
A trust warning that does matter. metadata travels from the browser, and anything traveling from the browser can be modified by whoever controls that browser. If your chat is public and you pass a customer_id through metadata, someone can swap it for another one and see a third party's conversation. For a chat behind an authenticated session where your server writes the data into the HTML, the risk is low. For anything touching sensitive data, identity must be validated server-side — typically by passing a signed token instead of a plain ID, and verifying it in n8n before using it. Trust boundaries are Module 7's topic; for now, just be clear this mechanism is convenient but it isn't authentication.
Common mistakes
Using the test URL as if it were permanent (practical). What happens: you set up the widget, test it, it works beautifully. You close n8n's canvas to go to lunch, come back, and the chat no longer responds — every message fails and there's no visible error on the page. Why it happens: the test URL is only registered while the editor is open and running; the production one requires the workflow to be active. The two look nearly identical and it's extremely easy to copy the wrong one. How to spot it: open the browser console (F12) and look at the failed request; a 404 with a message about an unregistered webhook is exactly this. How to fix it: always use the production URL for anything embedded, and confirm the workflow's "Active" toggle is on — the same toggle, top right, that sometimes turns itself off when you duplicate a workflow.
Confusing "the agent doesn't remember" with "the screen doesn't show" (conceptual). What happens: someone reloads the chat page, sees an empty thread, and concludes memory isn't working. They change the memory node, adjust the sessionId, try another store — and nothing changes, because memory was never broken. Why it happens: Load Previous Session controls whether the widget paints previous messages, and it's a different option from the memory node feeding the model's context. They're two separate things intuition lumps together. How to spot it: after reloading, write something that depends on previous context — "and what happened with what I asked you before?" — and see if the agent answers correctly. If it answers correctly with an empty screen, memory is perfectly fine and what's missing is the presentation option. How to fix it: turn on Load Previous Session and confirm there's a memory node connected to the agent, because without it the option has nowhere to read from.
Leaving Allowed Origin (CORS) at * and finding out too late (practical). What happens: the widget works everywhere during development because the default value accepts any origin. It goes to production that way, and now any page on the internet can embed your chat and consume your tokens. Why it happens: * is the value that makes everything work on the first try, and nothing warns you it's still there. How to spot it: check the field on the node; if it says *, this is it. How to fix it: put the real origins, comma-separated — your production domain and, if you need it, your local development origin — and test from both. Keep in mind the measure's real scope: CORS is enforced by the browser, so it protects against embedding on another site, not against someone calling your webhook directly with a command-line tool. For that you need real authentication.
Putting the welcome messages on the node when the mode is embedded (practical). What happens: someone configures Initial Message(s), Title, and Subtitle on the Chat Trigger, embeds the widget, and none of the three shows up. They check the node three times and everything's written correctly. Why it happens: those options belong to Hosted mode, where n8n serves the interface. In embedded mode @n8n/chat builds the interface, so the text goes into createChat's options. How to spot it: if the trigger's mode is Embedded and you expected to see text configured on the node, this is it. How to fix it: move the text into initialMessages and i18n in the page's snippet.
Forgetting a public chat is a public endpoint (conceptual). What happens: someone publishes the chat with Authentication: None, shares the URL in a demo, and weeks later notices token consumption that doesn't match their site's real traffic. Why it happens: the URL is easy to share, it doesn't expire, and nothing in n8n reminds you there's a model that charges on the other end. How to spot it: compare the workflow's execution count against the conversations you expected; if they don't match, someone else is writing to it. How to fix it: for demos and internal tools use Basic Auth or n8n User Auth; for a genuinely public chat, keep the URL out of repositories and shared documents, and treat spend control and guardrails as what they are — their own topic, which is Module 7.
Exercises
Exercise 1 — Choose the response mode. For each of these three TuTienda scenarios, say which Response Mode you'd use and why. (a) An agent answering frequently asked questions in two seconds. (b) An agent that checks three systems and takes between ten and twenty seconds, and you want to let the customer know to wait. (c) An agent that drafts long, multi-paragraph responses and you want it to feel agile.
See solution
(a) When Last Node Finishes. Two seconds need nothing more. Adding response nodes or streaming here is complexity with no benefit: nobody perceives the difference between "it showed up in two seconds" and "it showed up letter by letter over two seconds."
(b) Using Response Nodes. It's the case this exists for. You put a Chat node with the Send Message operation right after the trigger, saying something like "Give me a moment, I'm checking your order," and the workflow keeps working. When it finishes, a second Chat node sends the real response. Without this, the customer sees fifteen seconds of silence, and fifteen seconds of silence in a chat is a long time — half of people type "hello?" before the response arrives, which also triggers another execution.
(c) Streaming response. It's exactly its use case: the text starts showing up almost immediately even though the full response takes a while. Perceived speed changes completely without real latency changing at all. The honest caveat: verify your model and your agent's configuration support it on your n8n version, and pay attention to what shows up when there's intermediate reasoning or delegation between agents — you don't want the customer seeing billing_specialist's JSON appearing word by word.
Why it works: the three scenarios aren't told apart by aesthetic preference but by a number — how long it takes — and whether there's something to say midway through. That's the real criterion.
Exercise 2 — Diagnose the silent widget. A colleague pasted the snippet on TuTienda's page. The widget shows up, looks fine, and typing a message does absolutely nothing: no response, no visible error. Write the five places you'd check, in order, and what you'd expect to see at each one.
See solution
In this order, from most to least likely:
- The browser console (F12). It's always first, because the real error is there and it doesn't show on the page. A
404points to the wrong URL or an inactive workflow; a message mentioningCORSorAccess-Control-Allow-Originpoints to the origins field; a401points to authentication. - The workflow's "Active" toggle. If it's off, the production URL doesn't exist. It's cause number one and takes two seconds to rule out.
- The snippet's
webhookUrlagainst the node's. That it's the production one and not the test one, and that it's copied in full — it's easy to truncate when copy-pasting. Allowed Origin (CORS)on the node. That it includes the exact origin the page is served from, with its scheme and its port.https://tutienda.exampleandhttp://localhost:8080are different origins and you need to list both if you use both.- The
Chat Trigger's mode. If it's left on Hosted instead of Embedded, the URL responds with a chat page instead of accepting the widget's request. The symptom is odd and quite confusing.
And a sixth place, if the first five check out: n8n's executions tab. If there's an execution there for every message you typed, the channel works and the problem is on the way back — typically a Response Mode set to Using Response Nodes with no node responding. If no execution shows up at all, the message never arrived and the problem is in the first five.
Why it works: that last tiebreaker — is there an execution or not? — splits the problem in half and is the step that saves the most time. Without it, you check ten possible causes; with it, you check five.
Exercise 3 — Design TuTienda's chat identity. TuTienda has two situations on its site: anonymous visitors browsing the catalog without having logged in, and customers logged into their account panel. You want a single widget that serves both. Write what metadata you'd send in each case and what expression you'd put in the memory node's Session ID so it works for both.
See solution
The cleanest way is for the server to fill metadata differently based on session state, and for n8n to use an expression with a fallback.
For an authenticated customer, your page renders:
metadata: {
customer_id: 'C-9931', // your server writes this
authenticated: true
}
For an anonymous visitor, your page doesn't send customer_id at all:
metadata: {
authenticated: false
}
And on the memory node, an expression that uses customer_id if it exists and falls back to the widget's sessionId if not:
Session ID: Define below
Key: {{ $('When chat message received').item.json.metadata.customer_id
|| $('When chat message received').item.json.sessionId }}
JavaScript's || operator returns the first value that isn't empty. If there's a customer_id, the conversation groups by customer and persists across devices. If there isn't one, it groups by browser session — which for an anonymous visitor is exactly right, because there's no better identity available.
One nuance worth noting: the anonymous visitor who later logs in switches keys halfway through, and their previous conversation stays orphaned under the old key. There are two reasonable stances. Accept it — an anonymous conversation is rarely worth migrating — or, if the case justifies it, have the agent summarize the conversation on detecting authentication and write it into the new session. The first one is right 90% of the time, and saying so in an interview, naming the second option as a deliberately discarded alternative, conveys considerably more judgment than implementing the second one without needing to.
And always confirm the exact path to metadata in a real execution's JSON on your version before treating the expression as correct.
Why it works: the exercise forces you to solve identity with a single configuration for two different populations, which is exactly the problem that comes back in lesson 7 when there are four channels instead of two.
Summary and next step
You now have your agent's first real channel, and with it the structural decisions that repeat across all the others. Chat Trigger in Hosted mode gives you a ready-to-use page with its own URL; in Embedded mode it gives you a webhook your site talks to with @n8n/chat and two lines of HTML. You picked the response mode based on how long your agent takes and whether there's something to say midway through, protected access with judgment, understood that Allowed Origin protects against embedding but isn't authentication, and solved the customer's identity by passing metadata from an authenticated page instead of settling for the tab's sessionId.
Before moving on you should be able to: explain the difference between the test URL and the production one and why the widget stops working with the first one; name the three response modes and in which case you'd use each; and say where the welcome messages get configured in each of the two modes, which isn't the same place.
What's next is the channel that genuinely matters in LATAM and also the most uncomfortable one to set up. Lesson 3 goes to WhatsApp Business API: what pieces you need to create in Meta's panel, what the two distinct credentials n8n needs are — one for receiving and one for sending — why you can only have one webhook per app and what that means for your testing, and the rule that changes the design of everything: the 24-hour window, with its corresponding bill.
Resources
- Chat Trigger node — n8n Docs — the node's complete reference: the two modes, the three authentication options, the three response modes, and the list of presentation options. Verify your version's exact names there.
- Chat node — n8n Docs — the
Send Message/Send and Wait for Responsenode, with its response types and approval button parameters. - @n8n/chat — npm — the embedded widget's package; the complete, up-to-date list of
createChat's options is there, including the ones this lesson didn't use. - Respond to Webhook node — n8n Docs — the node you respond with in embedded mode when the response mode waits for response nodes.
- Memory in n8n — n8n Docs — worth re-reading how
sessionIdgroups the history, which is the piecemetadatalets you replace with a real identity. - Workflow activation — n8n Docs — what exactly activating a workflow means and why the production URL depends on it; the source of this lesson's most common error.