Your Website Visitors Are Leaving Without Talking to Anyone
The average B2B website converts 2-3% of its traffic. The other 97% browse your pricing page, read a case study, hover over the "Contact Us" button, and leave. They had intent. They had questions. But filling out a form and waiting 24 hours for a reply was too much friction — so they went to a competitor who answered instantly.
Odoo 19's Live Chat module eliminates that friction. A visitor lands on your site, a chat widget appears after a configurable delay, and within seconds they're talking to a human operator or an automated chatbot. That conversation can automatically create a CRM lead, a helpdesk ticket, or both — without the visitor ever filling out a form.
This guide walks through the complete setup: installing and configuring the Live Chat module, building chatbot flows with conditional logic, assigning operators intelligently, creating canned responses for speed, tracking visitors across pages, and wiring conversations directly into your CRM pipeline and helpdesk queue. Every step includes the exact configuration or code you need.
Installing and Configuring Live Chat in Odoo 19
The Live Chat module ships with Odoo 19 Enterprise and Community. Installation takes under two minutes, but the configuration choices you make here determine whether the widget feels helpful or intrusive to visitors.
Step 1: Install the Module
Navigate to Apps, search for "Live Chat", and click Install. This installs the im_livechat module and its dependencies. If you are working from a custom Docker image or source install, ensure the module is available:
# From your Odoo addons path, confirm im_livechat exists
ls -la /opt/odoo/odoo/addons/im_livechat/
# If using a custom addons path, also check:
ls -la /opt/odoo/custom-addons/im_livechat/ 2>/dev/null
# Install via CLI (useful for automated deployments)
python3 odoo-bin -d your_database -i im_livechat --stop-after-initStep 2: Create a Live Chat Channel
Go to Live Chat → Channels and click New. Each channel represents a separate chat widget configuration. Most companies need two: one for sales (pre-purchase questions) and one for support (post-purchase issues).
import xmlrpc.client
url = 'https://erp.yourcompany.com'
db = 'production'
uid = 2 # admin user ID
password = 'your_api_key'
models = xmlrpc.client.ServerProxy(f'{{url}}/xmlrpc/2/object')
# Create a Sales live chat channel
channel_id = models.execute_kw(db, uid, password,
'im_livechat.channel', 'create', [{
'name': 'Sales - Website',
'button_text': 'Chat with Sales',
'default_message': 'Hi! How can we help you today?',
'input_placeholder': 'Type your question...',
'header_background_color': '#1B5E3B',
'title_color': '#FFFFFF',
'button_background_color': '#1B5E3B',
'button_text_color': '#FFFFFF',
}])
print(f'Created channel ID: {{channel_id}}')Step 3: Embed the Widget on Your Website
If you are using Odoo Website, the chat widget activates automatically once you publish the channel. For external websites (headless front-ends, WordPress, static sites), embed the script tag that Odoo generates:
<!-- Odoo 19 Live Chat Widget -->
<script type="text/javascript">
(function () {
var script = document.createElement('script');
script.type = 'text/javascript';
script.async = true;
script.src = 'https://erp.yourcompany.com/im_livechat/loader/1';
// The number at the end is your channel ID
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(script, s);
})();
</script>
<!-- CORS: Ensure your Odoo server allows the external domain -->
<!-- In odoo.conf, add: -->
<!-- http_allowed_origins = https://www.yourwebsite.com --> The /im_livechat/loader/1 URL uses the channel's database ID. If you create multiple channels, each gets a different ID. Use /im_livechat/loader/2 for your support channel, etc. You can find the ID in the URL when editing the channel in the Odoo backend.
Building Chatbot Flows with Conditional Logic in Odoo 19
Odoo 19's chatbot isn't a simple auto-responder — it supports multi-step flows with conditional branching, allowing you to qualify leads, route conversations, and collect structured data before a human operator ever joins. The chatbot handles the repetitive first 60 seconds of every conversation so your team can focus on high-value interactions.
Creating a Chatbot Script
Navigate to Live Chat → Chatbots and click New. Each chatbot consists of ordered steps. Here is a lead-qualification flow that we deploy for SaaS companies:
# Create the chatbot script
chatbot_id = models.execute_kw(db, uid, password,
'chatbot.script', 'create', [{
'title': 'Sales Qualifier Bot',
}])
# Step 1: Greeting + ask intent
models.execute_kw(db, uid, password,
'chatbot.script.step', 'create', [{
'chatbot_script_id': chatbot_id,
'step_type': 'question_selection',
'message': "Welcome! I'm here to help. What brings you here today?",
'sequence': 10,
}])
# Step 1 answer options (these create conditional branches)
step1_id = models.execute_kw(db, uid, password,
'chatbot.script.step', 'search', [[
('chatbot_script_id', '=', chatbot_id),
('sequence', '=', 10),
]])[0]
for answer in ['Exploring pricing', 'Technical question', 'Need support']:
models.execute_kw(db, uid, password,
'chatbot.script.answer', 'create', [{
'script_step_id': step1_id,
'name': answer,
}])
# Step 2: Collect email (free text input)
models.execute_kw(db, uid, password,
'chatbot.script.step', 'create', [{
'chatbot_script_id': chatbot_id,
'step_type': 'question_email',
'message': 'Great! What is your email address so we can follow up?',
'sequence': 20,
}])
# Step 3: Create a lead automatically
models.execute_kw(db, uid, password,
'chatbot.script.step', 'create', [{
'chatbot_script_id': chatbot_id,
'step_type': 'create_lead',
'message': 'Thanks! A team member will reach out shortly.',
'sequence': 30,
'crm_team_id': 1, # Sales Team ID
}])
# Step 4: Forward to human operator
models.execute_kw(db, uid, password,
'chatbot.script.step', 'create', [{
'chatbot_script_id': chatbot_id,
'step_type': 'forward_operator',
'message': 'Let me connect you with a team member now...',
'sequence': 40,
}])Chatbot Step Types Reference
| Step Type | Purpose | When to Use |
|---|---|---|
text | Display a message (no input expected) | Greetings, confirmations, informational messages |
question_selection | Multiple-choice buttons | Intent routing, qualification questions, menu navigation |
question_email | Free text with email validation | Lead capture — validates format before proceeding |
question_phone | Free text with phone validation | Callback requests, high-intent lead capture |
free_input_single | Single-line free text | Name, company name, order number lookup |
free_input_multi | Multi-line free text | Detailed issue description, feature requests |
forward_operator | Transfer to human agent | After qualification, for complex issues, on visitor request |
create_lead | Create CRM lead from conversation | After collecting email/phone — captures full chat transcript |
Each answer in a question_selection step can redirect to a different follow-up step. For example, "Exploring pricing" jumps to a step that asks about company size, while "Need support" jumps directly to forward_operator. Configure this in the Redirections tab of each answer. This is how you build different paths for sales vs. support without creating separate chatbots.
Operator Assignment and Routing in Odoo 19 Live Chat
When a visitor sends a message (or the chatbot forwards to a human), Odoo needs to decide which operator handles the conversation. The default behavior is round-robin among available operators, but Odoo 19 gives you several ways to make this smarter.
Adding Operators to a Channel
Open your channel in Live Chat → Channels, go to the Operators tab, and add users. Only users added here will receive conversations from this channel.
# Get user IDs for your sales team
sales_user_ids = models.execute_kw(db, uid, password,
'res.users', 'search', [[
('groups_id', 'in', [
models.execute_kw(db, uid, password,
'ir.model.data', 'check_object_reference',
['sales_team', 'group_sale_salesman'])[1]
])
]])
# Add all salespeople as operators on the Sales channel
models.execute_kw(db, uid, password,
'im_livechat.channel', 'write', [[channel_id], {
'user_ids': [(4, uid) for uid in sales_user_ids],
}])Operator Availability Rules
| Status | Receives Chats? | How to Set |
|---|---|---|
| Online (green dot) | Yes — included in round-robin | User is logged in and has Discuss open or is on any Odoo page |
| Away (orange dot) | Yes, but deprioritized | Automatic after 30 minutes of inactivity |
| Offline (grey dot) | No — skipped entirely | User logged out or manually set to offline in Discuss |
Maximum simultaneous chats: Each operator has a configurable limit (default: 0, meaning unlimited). Set this in the operator's line on the channel. For sales teams handling complex conversations, we recommend a limit of 3-5 concurrent chats. For support agents handling quick answers, 8-10 works well. When an operator hits their limit, Odoo routes to the next available operator.
If all operators are offline, the chat widget either hides entirely or shows a "Leave a message" form (configurable per channel). Messages left offline are stored as Discuss messages on the channel. We strongly recommend enabling the offline message form — these are often high-intent leads who visited outside business hours and still took the time to write.
Canned Responses: Answer in Seconds, Not Minutes
Response time is the single biggest predictor of chat conversion. Harvard Business Review found that responding within 5 minutes makes you 21x more likely to qualify a lead compared to responding in 30 minutes. Canned responses let your operators reply in under 3 seconds to the 20 questions that make up 80% of all conversations.
Creating Canned Responses
Navigate to Live Chat → Canned Responses. Each response has a shortcut (what the operator types) and a substitution (what gets sent). Operators trigger canned responses by typing : followed by the shortcut keyword.
canned_responses = [
{
'source': ':pricing',
'substitution': "Our pricing depends on the number of users and modules. "
"I can prepare a custom quote — could you share your "
"team size and which Odoo apps you're interested in?",
},
{
'source': ':demo',
'substitution': "I'd be happy to schedule a live demo! We do 30-minute "
"sessions where we walk through your specific use case. "
"When works best for you this week?",
},
{
'source': ':trial',
'substitution': "Yes! We offer a 15-day free trial with full access to "
"all modules. I can set one up for you right now — "
"just need your email address.",
},
{
'source': ':hours',
'substitution': "Our team is available Monday through Friday, "
"9 AM to 6 PM (EST). Outside those hours, leave a "
"message here and we'll respond first thing next morning.",
},
{
'source': ':transfer',
'substitution': "Let me connect you with a specialist who can help "
"with that. One moment please...",
},
{
'source': ':thanks',
'substitution': "Thank you for chatting with us! If you have any "
"other questions, don't hesitate to reach out. "
"Have a great day!",
},
]
for resp in canned_responses:
models.execute_kw(db, uid, password,
'mail.shortcode', 'create', [resp])
print(f'Created {{len(canned_responses)}} canned responses') Train your operators to type : in the chat input to see all available shortcuts in a dropdown. They can arrow-key through the list and press Enter. This is faster than typing the full shortcut name. Operators who master canned responses handle 3x more concurrent conversations than those who type every reply manually.
Visitor Tracking: Know Who You're Talking To Before They Say a Word
Odoo 19 tracks website visitors in real time and links that data to live chat conversations. When a visitor opens the chat widget, the operator can see their current page, pages visited, time on site, country, and — if they've visited before or are a known contact — their name, email, and previous conversation history.
Enabling Visitor Tracking
Visitor tracking is automatic when both website and im_livechat modules are installed. For external websites, tracking requires the embed script from Step 1. The tracking data is stored in the website.visitor model.
# Get visitors currently on the website (last 5 minutes)
from datetime import datetime, timedelta
five_min_ago = (datetime.utcnow() - timedelta(minutes=5)).strftime(
'%Y-%m-%d %H:%M:%S'
)
active_visitors = models.execute_kw(db, uid, password,
'website.visitor', 'search_read', [[
('last_connection_datetime', '>=', five_min_ago),
]], {
'fields': [
'display_name', 'email', 'country_id',
'visit_count', 'page_count', 'visitor_page_count',
'last_visited_page_id', 'livechat_operator_id',
],
'limit': 50,
})
for v in active_visitors:
status = 'In chat' if v['livechat_operator_id'] else 'Browsing'
print(f"[{{status}}] {{v['display_name']}} — "
f"{{v['page_count']}} pages, "
f"{{v['visit_count']}} visits — "
f"Country: {{v['country_id']}}")URL-Based Chat Rules
You can configure the chat widget to appear only on specific pages, or to show different messages based on the URL. Go to your channel's Channel Rules tab:
| Rule | URL Regex | Action | Delay |
|---|---|---|---|
| Pricing page | /pricing | Auto popup | 15 seconds |
| Product pages | /shop/.* | Show button | 0 seconds |
| Blog / docs | /blog/.*|/docs/.* | Hide | N/A |
| Checkout | /shop/checkout | Auto popup | 30 seconds |
# Auto-popup on the pricing page after 15 seconds
models.execute_kw(db, uid, password,
'im_livechat.channel.rule', 'create', [{
'channel_id': channel_id,
'action': 'auto_popup',
'auto_popup_timer': 15,
'regex_url': '/pricing',
}])
# Show button (no popup) on product pages
models.execute_kw(db, uid, password,
'im_livechat.channel.rule', 'create', [{
'channel_id': channel_id,
'action': 'display_button',
'regex_url': '/shop/.*',
}])
# Hide chat entirely on blog content pages
models.execute_kw(db, uid, password,
'im_livechat.channel.rule', 'create', [{
'channel_id': channel_id,
'action': 'hide_button',
'regex_url': '/blog/.*',
}])Each channel rule also supports a Country field. This lets you show the chat widget only to visitors from specific countries, or route different countries to different channels with language-appropriate operators. For example, route visitors from France and Belgium to a French-speaking channel, and everyone else to English.
Turning Chat Conversations into CRM Leads Automatically
This is where live chat stops being a communication tool and becomes a lead generation engine. Every conversation can create a CRM lead — either automatically via the chatbot's create_lead step, or manually by the operator clicking a button. The lead captures the full chat transcript, visitor data, and any structured information collected during the conversation.
Automatic Lead Creation via Chatbot
When you add a create_lead step to your chatbot script (as shown in Step 02), Odoo creates a lead with the following data mapped automatically:
| CRM Lead Field | Source | Example Value |
|---|---|---|
name | Channel name + visitor identifier | "Sales - Website / John from US" |
email_from | question_email step response | "john@acmecorp.com" |
phone | question_phone step response | "+1-555-0123" |
description | Full chat transcript | Complete conversation history as HTML |
team_id | crm_team_id on the chatbot step | Sales Team ID |
source_id | Automatically set to "Live Chat" | UTM source record |
Manual Lead Creation by Operators
During any conversation, operators can create a lead manually by clicking the Create Lead button in the chat panel (visible in the Discuss sidebar when a live chat session is active). This is useful for conversations that start as general inquiries but reveal sales intent.
# Useful for custom integrations or automated workflows
# that create leads based on chat content analysis
def create_lead_from_chat(channel_uuid, operator_uid):
"""Create a CRM lead from a live chat conversation."""
# Get the mail.channel for this live chat session
channel = models.execute_kw(db, uid, password,
'discuss.channel', 'search_read', [[
('uuid', '=', channel_uuid),
('channel_type', '=', 'livechat'),
]], {
'fields': ['livechat_visitor_id', 'message_ids'],
'limit': 1,
})
if not channel:
return None
ch = channel[0]
# Get visitor data
visitor = models.execute_kw(db, uid, password,
'website.visitor', 'read', [ch['livechat_visitor_id'][0]], {
'fields': ['email', 'display_name', 'country_id'],
})[0]
# Get chat transcript
messages = models.execute_kw(db, uid, password,
'mail.message', 'search_read', [[
('res_id', '=', ch['id']),
('model', '=', 'discuss.channel'),
]], {
'fields': ['body', 'author_id', 'date'],
'order': 'date asc',
})
transcript = '\n'.join(
f"[{{m['date']}}] {{m['author_id'][1]}}: {{m['body']}}"
for m in messages
)
# Create the lead
lead_id = models.execute_kw(db, uid, password,
'crm.lead', 'create', [{
'name': f"Live Chat — {{visitor['display_name']}}",
'email_from': visitor.get('email'),
'description': transcript,
'source_id': models.execute_kw(db, uid, password,
'utm.source', 'search', [[
('name', '=', 'Live Chat'),
]], {'limit': 1})[0],
'user_id': operator_uid,
'type': 'lead',
}])
return lead_idCreating Helpdesk Tickets from Live Chat Conversations
Not every conversation is a sales lead. When existing customers use live chat for support, the conversation should become a helpdesk ticket — tracked, assigned, and measured by your support SLAs. Odoo 19 supports this natively when both im_livechat and helpdesk modules are installed.
Enabling Live Chat on a Helpdesk Team
Go to Helpdesk → Configuration → Helpdesk Teams, select your team, and enable Live Chat under the Channels section. This adds a "Create Ticket" button to the operator's chat panel.
# Find and update the helpdesk team
helpdesk_team_ids = models.execute_kw(db, uid, password,
'helpdesk.team', 'search', [[
('name', '=', 'Customer Support'),
]])
if helpdesk_team_ids:
models.execute_kw(db, uid, password,
'helpdesk.team', 'write', [helpdesk_team_ids, {
'use_website_helpdesk_livechat': True,
}])
# Tickets created from live chat include:
# - Full conversation transcript in the ticket description
# - Link to the original chat session
# - Visitor/customer contact information
# - Pages visited before starting the chatChatbot-to-Ticket Flow
You can also configure the chatbot to create tickets automatically. In the chatbot script, use a question_selection step to determine intent. If the visitor selects "Need support," redirect to a branch that collects the issue description and then uses a create_lead step pointed at the helpdesk team. Alternatively, use the forward_operator step and let the support agent create the ticket manually after understanding the issue.
The most effective setup uses a single chatbot with two branches: sales inquiries flow to create_lead (CRM pipeline), and support requests flow to forward_operator on the support channel (helpdesk queue). The visitor's first answer — "Exploring pricing" vs "Need support" — determines the entire downstream path. One chatbot, two outcomes, zero manual routing.
Live Chat Reporting and KPIs in Odoo 19
Odoo 19 provides built-in reporting for live chat under Live Chat → Reports. But the default reports only scratch the surface. Here are the KPIs that actually predict whether live chat is generating revenue or just consuming operator time:
| KPI | Target | How to Measure in Odoo |
|---|---|---|
| First Response Time | < 30 seconds | Live Chat → Reports → Sessions — "Time to Answer" column |
| Chat-to-Lead Rate | > 15% | CRM leads with source "Live Chat" / Total chat sessions |
| Visitor Satisfaction | > 85% positive | Live Chat → Reports → Customer Ratings |
| Operator Utilization | 60-80% | Active chat minutes / Available minutes per operator |
| Resolution Rate | > 70% | Conversations closed without helpdesk ticket / Total conversations |
# Calculate chat-to-lead conversion rate for the last 30 days
from datetime import datetime, timedelta
thirty_days_ago = (datetime.utcnow() - timedelta(days=30)).strftime(
'%Y-%m-%d'
)
# Total chat sessions
total_sessions = models.execute_kw(db, uid, password,
'discuss.channel', 'search_count', [[
('channel_type', '=', 'livechat'),
('create_date', '>=', thirty_days_ago),
]])
# Leads created from live chat
livechat_source = models.execute_kw(db, uid, password,
'utm.source', 'search', [[
('name', '=', 'Live Chat'),
]], {'limit': 1})
leads_from_chat = models.execute_kw(db, uid, password,
'crm.lead', 'search_count', [[
('source_id', 'in', livechat_source),
('create_date', '>=', thirty_days_ago),
]])
conversion_rate = (leads_from_chat / total_sessions * 100
if total_sessions else 0)
print(f"Chat sessions (30d): {{total_sessions}}")
print(f"Leads from chat: {{leads_from_chat}}")
print(f"Conversion rate: {{conversion_rate:.1f}}%")5 Live Chat Mistakes That Kill Your Conversion Rate
Auto-Popup on Every Page After 0 Seconds
The most common configuration mistake. When the chat widget pops open instantly on every page, visitors perceive it as a popup ad and close it reflexively. Worse, they develop "chat blindness" and won't click it even when they have a real question later. We see chat engagement rates drop by 40-60% when auto-popup is too aggressive.
Use auto-popup only on high-intent pages (pricing, checkout, contact). Set a delay of 15-30 seconds — enough time for the visitor to start reading before the widget appears. On all other pages, use "display button" so the widget is visible but not intrusive.
No Operators Online During Business Hours
You install live chat, add operators, and launch — but nobody on the team keeps their Odoo tab open. The widget shows "Leave a message" during peak hours because all operators show as offline. Visitors who wanted instant answers now have to fill out a form, which is exactly the friction live chat was supposed to eliminate.
Create a rotation schedule. Use Discuss notifications to alert operators when a chat is waiting. If your team is small, the chatbot handles the first 60 seconds of every conversation — buying time for operators to respond. Monitor "unanswered chat" metrics weekly.
Chatbot Dead Ends Without Human Handoff
A chatbot that collects information but never connects to a human operator frustrates visitors. We've audited chatbot scripts where the final step was a "Thank you" message — no forward_operator, no create_lead, no next action. The visitor provided their email, described their problem, and then... nothing. They leave and never return.
Every chatbot branch must end with either forward_operator (immediate handoff), create_lead (async follow-up), or both. Add a forward_operator step after create_lead so the visitor gets both a recorded lead and immediate human attention.
CORS Errors on External Website Embeds
When embedding the live chat widget on a non-Odoo website, the browser blocks the request to your Odoo server with a CORS (Cross-Origin Resource Sharing) error. The widget silently fails to load — no error message for the visitor, no chat button, no indication anything is wrong. You only discover it by opening the browser console.
Add your external domain to odoo.conf with the http_allowed_origins parameter. If using Nginx, ensure you're not stripping the Origin header. Test with browser DevTools Network tab — the response to the loader script should include Access-Control-Allow-Origin.
WebSocket Connection Drops Behind Reverse Proxy
Live chat relies on WebSocket connections for real-time messaging. If your Nginx proxy doesn't forward the Upgrade and Connection headers to the longpolling port (8072), chat messages are delayed by 30-60 seconds as Odoo falls back to HTTP polling. Operators see messages late, responses feel slow, and visitors abandon the conversation.
Ensure your Nginx config includes the /websocket location block with proxy_set_header Upgrade $http_upgrade and proxy_set_header Connection "upgrade". Set proxy_read_timeout 3600s to prevent idle disconnections. See our Nginx reverse proxy guide for the complete configuration.
The Revenue Impact of Real-Time Visitor Engagement
Live chat isn't a cost center — it's a revenue multiplier. Here are the numbers we see across client deployments after the first 90 days:
Visitors who chat are 3-5x more likely to convert than form-fillers. The conversation removes doubt in real time instead of making them wait 24 hours for an email reply.
Harvard Business Review: responding within 5 minutes makes you 21x more likely to qualify a lead. Live chat makes sub-30-second response times achievable.
One operator handles 5-10 simultaneous chats vs. one phone call at a time. Canned responses and chatbot pre-qualification reduce average handling time by 40%.
The math is straightforward: if your website gets 10,000 visitors/month and your current form conversion rate is 2% (200 leads), adding live chat with a 5% chat engagement rate and a 15% chat-to-lead conversion rate generates an additional 75 qualified leads per month — with no increase in traffic spend.
Optimization Metadata
Complete guide to Odoo 19 Live Chat: install the module, build chatbot flows, assign operators, create CRM leads and helpdesk tickets from conversations, and track visitor behavior in real time.
1. "Installing and Configuring Live Chat in Odoo 19"
2. "Building Chatbot Flows with Conditional Logic in Odoo 19"
3. "Turning Chat Conversations into CRM Leads Automatically"
4. "5 Live Chat Mistakes That Kill Your Conversion Rate"