Your Helpdesk Is Losing Customers Without You Knowing It
Here is the pattern we see at every mid-market company running support out of shared inboxes and spreadsheets: a customer emails about a billing issue on Monday. Nobody owns it. By Wednesday the customer emails again, CC'ing their account manager. By Friday they are evaluating your competitor. You never breached a contract — you just never defined what "on time" meant.
Odoo 19's Helpdesk module solves this with SLA policies — enforceable time-based rules that define how quickly your team must respond to, and resolve, every ticket based on its priority, type, and customer tier. When an SLA deadline approaches, Odoo escalates automatically. When it's breached, the ticket turns red and managers get notified. No spreadsheet can do that.
This guide covers everything from initial helpdesk team configuration through SLA policy design, ticket stage automation, assignment rules, email gateway integration, customer portal setup, CSAT surveys, after-sales service linking, and the reporting dashboards that tell you whether your support operation is actually working. Every field name and menu path is from Odoo 19 Enterprise.
Configuring Helpdesk Teams in Odoo 19: Channels, Visibility, and Assignment Methods
Before you create a single SLA policy, you need a properly structured helpdesk team. Navigate to Helpdesk → Configuration → Helpdesk Teams and create your first team. The configuration choices you make here determine how tickets flow through your entire support operation.
| Field | Purpose | Our Recommendation |
|---|---|---|
name | Team display name shown in ticket views and portal | Use functional names: "Billing Support", "Technical Support", not "Team A" |
alias_id | Email alias that auto-creates tickets (e.g., support@yourcompany.com) | One alias per team — never route all emails to a single catchall |
assign_method | How new tickets are assigned: Manual, Random, Balanced | Balanced — distributes by current open ticket count per agent |
member_ids | Team members eligible for ticket assignment | Only include agents who are actively handling tickets — not managers |
use_sla | Enable SLA tracking on this team | Always enable — even internal teams benefit from response time tracking |
use_rating | Enable customer satisfaction surveys on ticket close | Enable for all external-facing teams |
Email Gateway Configuration
The email alias is the primary ticket creation channel for most teams. When a customer sends an email to support@yourcompany.com, Odoo creates a helpdesk.ticket record with the email subject as the ticket name, the body as the description, and the sender matched to an existing res.partner via email lookup. To configure it, go to Settings → Technical → Email → Aliases or set it directly on the team form under the Channels tab.
from odoo import models, api
class HelpdeskTicket(models.Model):
_inherit = 'helpdesk.ticket'
@api.model_create_multi
def create(self, vals_list):
"""Auto-set priority based on partner's SLA tier tag."""
for vals in vals_list:
if vals.get('partner_id') and not vals.get('priority'):
partner = self.env['res.partner'].browse(
vals['partner_id']
)
# Check for VIP tag on the contact
vip_tag = self.env.ref(
'my_helpdesk.partner_tag_vip', raise_if_not_found=False
)
if vip_tag and vip_tag in partner.category_id:
vals['priority'] = '3' # Urgent
elif partner.parent_id and partner.parent_id.is_company:
# Enterprise customers get High priority by default
contract = self.env['sale.order'].search([
('partner_id', '=', partner.parent_id.id),
('state', '=', 'sale'),
('tag_ids.name', 'ilike', 'Premium Support'),
], limit=1)
if contract:
vals['priority'] = '2' # High
return super().create(vals_list)Random assignment ignores workload — an agent with 30 open tickets gets the same chance as one with 5. Balanced counts current open tickets per member and assigns to the least loaded agent. For teams larger than 3 agents, Balanced reduces average first response time by 25-40% in our measurements, because tickets stop piling up on whoever happened to be assigned three urgent ones in a row.
Designing SLA Policies in Odoo 19: Response Time, Resolution Time, and Priority Escalation
SLA policies are the backbone of a professional support operation. In Odoo 19, navigate to Helpdesk → Configuration → SLA Policies to create them. Each policy defines a target stage and a time limit — if the ticket doesn't reach that stage within the allotted time, the SLA is marked as failed.
A well-designed SLA matrix uses two policies per priority level: one for first response (reaching the "In Progress" stage) and one for resolution (reaching the "Solved" stage). Here is the matrix we deploy for most mid-market clients:
| Priority | First Response Target | Resolution Target | Business Hours Only |
|---|---|---|---|
| Urgent (3) | 30 minutes | 4 hours | No (24/7) |
| High (2) | 2 hours | 8 hours | Yes |
| Medium (1) | 4 hours | 24 hours | Yes |
| Low (0) | 8 hours | 48 hours | Yes |
<odoo>
<data noupdate="1">
<!-- ── First Response SLAs ────────────────────── -->
<record id="sla_urgent_response" model="helpdesk.sla">
<field name="name">Urgent — First Response (30 min)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">3</field>
<field name="stage_id" ref="helpdesk.stage_in_progress"/>
<field name="time">0.5</field>
<!-- time is in hours; 0.5 = 30 minutes -->
<field name="exclude_stage_ids"
eval="[(6, 0, [])]"/>
</record>
<record id="sla_urgent_resolution" model="helpdesk.sla">
<field name="name">Urgent — Resolution (4 hrs)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">3</field>
<field name="stage_id" ref="helpdesk.stage_solved"/>
<field name="time">4</field>
</record>
<record id="sla_high_response" model="helpdesk.sla">
<field name="name">High — First Response (2 hrs)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">2</field>
<field name="stage_id" ref="helpdesk.stage_in_progress"/>
<field name="time">2</field>
</record>
<record id="sla_high_resolution" model="helpdesk.sla">
<field name="name">High — Resolution (8 hrs)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">2</field>
<field name="stage_id" ref="helpdesk.stage_solved"/>
<field name="time">8</field>
</record>
<record id="sla_medium_response" model="helpdesk.sla">
<field name="name">Medium — First Response (4 hrs)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">1</field>
<field name="stage_id" ref="helpdesk.stage_in_progress"/>
<field name="time">4</field>
</record>
<record id="sla_medium_resolution" model="helpdesk.sla">
<field name="name">Medium — Resolution (24 hrs)</field>
<field name="team_id" ref="helpdesk.helpdesk_team1"/>
<field name="priority">1</field>
<field name="stage_id" ref="helpdesk.stage_solved"/>
<field name="time">24</field>
</record>
</data>
</odoo>How SLA Deadlines Are Calculated
Odoo 19 calculates SLA deadlines using the resource.calendar assigned to the helpdesk team. If your team's working hours are Monday–Friday 9:00–17:00, a ticket created at 16:30 on Friday with a 2-hour first response SLA has a deadline of Monday at 10:30, not Saturday at 18:30. The field sla_deadline on helpdesk.ticket stores the computed datetime. The boolean sla_fail flips to True when the current time exceeds the deadline and the ticket hasn't reached the target stage.
Most teams create a single SLA for "resolution time" and call it done. This misses the most important metric: first response time. A customer who gets acknowledged within 30 minutes will tolerate a 24-hour resolution. A customer who hears nothing for 4 hours assumes you don't care — even if you resolve the issue in 5 hours total. Always pair a response SLA (target stage: In Progress) with a resolution SLA (target stage: Solved).
Ticket Automation in Odoo 19: Stage Transitions, Escalation Rules, and Scheduled Actions
SLA policies define deadlines. Automation enforces them. Odoo 19 provides three automation mechanisms for helpdesk tickets: automated actions (server actions triggered by field changes), scheduled actions (cron-based), and mail templates tied to stage transitions. Together, these replace the manual monitoring that burns out support managers.
Ticket Stage Pipeline
Define your stages under Helpdesk → Configuration → Stages. The stage sequence controls the Kanban column order. A standard pipeline for most support teams looks like this:
| Stage | Sequence | Fold in Kanban | Purpose |
|---|---|---|---|
| New | 1 | No | Incoming tickets, not yet triaged |
| In Progress | 2 | No | Agent is actively working — triggers "first response" SLA |
| Waiting on Customer | 3 | No | Agent requested info, SLA clock should pause |
| Solved | 4 | Yes | Resolution confirmed — triggers CSAT survey |
| Cancelled | 5 | Yes | Duplicate, spam, or customer withdrew request |
from odoo import models, fields, api
from datetime import timedelta
class HelpdeskSLAEscalation(models.Model):
_inherit = 'helpdesk.ticket'
is_escalated = fields.Boolean(
string='Escalated',
default=False,
tracking=True,
)
escalation_date = fields.Datetime(
string='Escalation Date',
readonly=True,
)
def _cron_escalate_sla_breached(self):
"""Scheduled action: escalate tickets that breached SLA.
Run every 15 minutes via ir.cron.
Menu: Settings → Technical → Automation → Scheduled Actions
"""
breached_tickets = self.search([
('sla_fail', '=', True),
('is_escalated', '=', False),
('stage_id.fold', '=', False), # Not in closed stages
])
manager_group = self.env.ref('helpdesk.group_helpdesk_manager')
managers = manager_group.users
for ticket in breached_tickets:
ticket.write({
'is_escalated': True,
'escalation_date': fields.Datetime.now(),
'priority': str(min(int(ticket.priority) + 1, 3)),
})
# Post internal note visible only to agents
ticket.message_post(
body=(
f'<strong>SLA Breached — Auto-Escalated</strong>'
f'<br/>Priority raised to '
f'{{ticket.priority}}. '
f'Original deadline: '
f'{{ticket.sla_deadline}}'
),
message_type='comment',
subtype_xmlid='mail.mt_note',
partner_ids=managers.mapped('partner_id').ids,
)
def _cron_auto_close_stale_tickets(self):
"""Close tickets stuck in 'Waiting on Customer' for 7+ days.
Sends a final notification before closing.
"""
stale_date = fields.Datetime.now() - timedelta(days=7)
waiting_stage = self.env.ref(
'my_helpdesk.stage_waiting_customer',
raise_if_not_found=False,
)
solved_stage = self.env.ref(
'helpdesk.stage_solved',
raise_if_not_found=False,
)
if not waiting_stage or not solved_stage:
return
stale_tickets = self.search([
('stage_id', '=', waiting_stage.id),
('write_date', '<=', stale_date),
])
for ticket in stale_tickets:
ticket.write({'stage_id': solved_stage.id})
ticket.message_post(
body=(
'This ticket was automatically closed after '
'7 days without customer response. '
'Please reopen if you still need assistance.'
),
message_type='comment',
subtype_xmlid='mail.mt_comment',
)Setting Up the Scheduled Actions
Navigate to Settings → Technical → Automation → Scheduled Actions and create two entries:
- Escalate SLA-Breached Tickets — Model:
helpdesk.ticket, Method:_cron_escalate_sla_breached, Interval: 15 minutes. - Auto-Close Stale Tickets — Model:
helpdesk.ticket, Method:_cron_auto_close_stale_tickets, Interval: 1 day.
In Odoo 19, you can exclude specific stages from SLA time calculation by adding them to the exclude_stage_ids field on the SLA policy record. When a ticket enters an excluded stage, the SLA clock pauses. When it leaves, the clock resumes. Always exclude your "Waiting on Customer" stage — otherwise every customer who takes 2 days to reply causes an SLA breach that isn't your team's fault.
Customer Portal, CSAT Surveys, and After-Sales Service Integration in Odoo 19
The helpdesk module's value extends beyond your internal team. Odoo 19 provides a customer portal where clients can submit tickets, track progress, and view their history — and a CSAT rating system that collects satisfaction data automatically when tickets close.
Portal Ticket Submission
Enable the portal under Helpdesk → Configuration → Helpdesk Teams → [Your Team] → Help Center. Toggle Website Form to let portal users submit tickets via /helpdesk/[team-slug]/submit. Portal users see only their own tickets, can add messages, and can close resolved tickets directly. The portal view respects ir.rule record rules — customers never see other customers' tickets.
CSAT Rating Configuration
Enable Customer Ratings on the team form. When a ticket moves to a closing stage (any stage with is_close = True), Odoo sends a rating email with a 3-point scale: satisfied (green), okay (yellow), dissatisfied (red). The rating is stored on the rating.rating model linked to the ticket. You can view aggregate scores at Helpdesk → Reporting → Customer Ratings.
<odoo>
<data>
<record id="rating_ticket_email_custom" model="mail.template">
<field name="name">Helpdesk: Custom CSAT Survey</field>
<field name="model_id" ref="helpdesk.model_helpdesk_ticket"/>
<field name="subject">How did we do? Ticket #{{object.id}} — {{object.name}}</field>
<field name="email_from">{{object.team_id.alias_id.display_name}}</field>
<field name="email_to">{{object.partner_id.email}}</field>
<field name="body_html" type="html">
<div style="margin: 0; padding: 0;">
<p>Hello {{object.partner_id.name}},</p>
<p>
Your support ticket <strong>#{{object.id}} —
{{object.name}}</strong> has been resolved
by {{object.user_id.name}}.
</p>
<p>How would you rate your experience?</p>
<t t-set="access_token"
t-value="object._rating_get_access_token()"/>
<table style="margin: 20px 0; border-spacing: 12px;">
<tr>
<td>
<a t-attf-href="/rate/{{access_token}}/5"
style="text-decoration: none; font-size: 32px;"
>🙂</a>
<br/>Satisfied
</td>
<td>
<a t-attf-href="/rate/{{access_token}}/3"
style="text-decoration: none; font-size: 32px;"
>😐</a>
<br/>Okay
</td>
<td>
<a t-attf-href="/rate/{{access_token}}/1"
style="text-decoration: none; font-size: 32px;"
>😞</a>
<br/>Dissatisfied
</td>
</tr>
</table>
<p style="color: #888; font-size: 12px;">
Resolved in {{object.sla_status_ids[0].reached_datetime}}
by team {{object.team_id.name}}.
</p>
</div>
</field>
</record>
</data>
</odoo>After-Sales Service Integration
Odoo 19 links helpdesk tickets to sales orders, subscriptions, and product lots via the sale_order_id, subscription_id, and lot_id fields on helpdesk.ticket. Enable this under Helpdesk → Configuration → Settings → After-Sales. When enabled, agents can see the customer's purchase history, warranty status, and active subscriptions directly from the ticket form — no tab-switching required.
- Refunds — agents can create credit notes directly from the ticket via the Refund button.
- Returns — agents can initiate a reverse transfer linked to the original delivery order.
- Coupons — agents can generate a discount coupon as a goodwill gesture, tracked on the ticket.
- Repair Orders — if the Repair module is installed, agents can create repair orders linked to the ticket's product lot.
The average CSAT email response rate is 8-12%. To improve it: send the survey immediately on ticket close (not 24 hours later — by then the customer has moved on), keep the email under 100 words, and make the rating a single click with no login required. Odoo's rating access tokens handle this natively — the customer clicks the emoji and the rating is recorded without authentication.
Helpdesk Reporting Dashboards: SLA Performance, Agent Load, and Trend Analysis
Data without dashboards is just noise. Odoo 19's helpdesk module includes built-in reporting under Helpdesk → Reporting with four views: Ticket Analysis, SLA Status Analysis, Customer Ratings, and Team Performance. Each view supports pivot tables, charts, and custom filter combinations.
| Report | Key Metrics | Who Uses It |
|---|---|---|
| Ticket Analysis | Volume by channel, avg. resolution time, tickets by stage | Support managers — weekly capacity planning |
| SLA Status Analysis | SLA success rate, avg. time to target stage, breaches by priority | Operations leads — SLA compliance reporting |
| Customer Ratings | CSAT score by agent, team, and time period | Team leads — agent performance reviews |
| Team Performance | Tickets per agent, first response time, resolution time | Support managers — workload balancing |
from odoo import models, fields
class HelpdeskSLAReport(models.Model):
_inherit = 'helpdesk.sla.report.analysis'
breach_hours = fields.Float(
string='Hours Over SLA',
compute='_compute_breach_hours',
store=True,
)
@api.depends('sla_deadline', 'sla_reached_datetime')
def _compute_breach_hours(self):
for record in self:
if record.sla_fail and record.sla_deadline:
reached = (
record.sla_reached_datetime
or fields.Datetime.now()
)
delta = reached - record.sla_deadline
record.breach_hours = delta.total_seconds() / 3600
else:
record.breach_hours = 0.0For executive reporting, create a custom Spreadsheet Dashboard under Dashboards → Spreadsheet that pulls from the helpdesk pivot views. The most useful executive view combines: weekly ticket volume trend, SLA compliance percentage (target: above 90%), average first response time, and CSAT score. Share the spreadsheet with your leadership team and set it as their Odoo home action.
3 Helpdesk Configuration Mistakes That Tank Your SLA Compliance Rate
Not Excluding "Waiting on Customer" from SLA Calculation
This is the most common SLA configuration mistake we encounter. Your agent responds within 15 minutes and moves the ticket to "Waiting on Customer." The customer takes 3 days to reply. The SLA clock kept running the entire time, so now your 8-hour resolution SLA shows as breached by 64 hours — even though your team did everything right. Managers see a 40% SLA failure rate and blame the team when the real culprit is the clock configuration.
On every SLA policy record, add your "Waiting on Customer" stage to the exclude_stage_ids many2many field. In Odoo 19, navigate to the SLA policy form and use the Excluding Stages field. This pauses the SLA clock when the ticket enters that stage and resumes it when the ticket moves forward. Apply this to every SLA policy — it is easy to forget when you create new ones.
Using a Single SLA Policy for All Priority Levels
Some teams create one SLA — "Resolve within 24 hours" — and apply it to every ticket regardless of priority. The result: urgent production-down tickets and low-priority feature requests have the same deadline. Agents learn to ignore the SLA because everything is equally urgent, which means nothing is. The SLA metric becomes meaningless noise, and the team reverts to gut-feel triage.
Create separate SLA policies per priority level with progressively tighter deadlines. Use the priority field on the SLA record to filter. Urgent tickets get 30-minute response; low-priority gets 8 hours. This gives agents a clear signal about what to work on next without manual manager intervention.
No Working Hours Calendar Assigned to the Helpdesk Team
Without a resource.calendar linked to the team, SLA deadlines are calculated on a 24/7 basis. A ticket created at 17:01 on Friday with a 4-hour SLA gets a deadline of 21:01 Friday — when nobody is in the office. The SLA breaches overnight, the manager gets an escalation notification at 6 AM Saturday, and the team starts Monday demoralized by a queue full of red "SLA Failed" badges for tickets that were never actionable on a weekend.
Navigate to Helpdesk → Configuration → Helpdesk Teams → [Your Team] and set the Working Hours field to your team's actual schedule. Create the calendar under Employees → Configuration → Working Schedules if it doesn't exist. Exception: if you offer 24/7 premium support, create a separate team with a 24/7 calendar and route premium customers to it.
What a Properly Configured Helpdesk Saves Your Business
The helpdesk module is included with Odoo Enterprise. The ROI comes from the operational improvements:
Balanced assignment + SLA escalation rules eliminate the "nobody owned this ticket" problem. Average first response drops from 6 hours to under 2.
With proper clock pausing on customer-wait stages and priority-tiered deadlines, teams consistently hit 90%+ SLA compliance within the first quarter.
Companies with sub-2-hour first response and CSAT above 85% see measurably lower churn. The cost of one retained customer exceeds the entire helpdesk setup investment.
Beyond the numbers: SLA data gives you leverage in contract negotiations. When you can show a prospective customer that your support team resolved 93% of tickets within SLA last quarter, that is a sales asset. When renewal time comes, documented CSAT scores above 90% make the pricing conversation about value, not cost.
Optimization Metadata
Complete guide to Odoo 19 Helpdesk SLA policies, ticket automation, CSAT surveys, and reporting. Covers team setup, stage pipeline, escalation rules, and customer portal.
1. "Designing SLA Policies in Odoo 19: Response Time, Resolution Time, and Priority Escalation"
2. "Ticket Automation in Odoo 19: Stage Transitions, Escalation Rules, and Scheduled Actions"
3. "3 Helpdesk Configuration Mistakes That Tank Your SLA Compliance Rate"