Helpdesk Ticket API¶
This guide covers how to manage helpdesk tickets via the REST API using Odoo's XML-RPC/JSON-RPC interface.
Overview¶
The REST API module provides generic CRUD endpoints for helpdesk.ticket model:
| Operation | HTTP Method | Endpoint |
|---|---|---|
| Create | POST | /restapi/1.0/object/helpdesk.ticket |
| List/Search | GET | /restapi/1.0/object/helpdesk.ticket |
| Read | GET | /restapi/1.0/object/helpdesk.ticket/{id} |
| Update | PUT | /restapi/1.0/object/helpdesk.ticket/{id} |
| Delete | DELETE | /restapi/1.0/object/helpdesk.ticket/{id} |
Authentication¶
All API requests require authentication. Supported methods:
API Key (Recommended)¶
Bearer Token (OAuth2)¶
Generate API Key¶
- Go to Settings > Technical > REST API > Authentications
- Create new authentication record
- Click "Generate API Key"
- Copy and store the key securely
Ticket Fields Reference¶
Core Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
name |
String | Yes | Ticket subject |
description |
HTML | No | Detailed description |
team_id |
Integer | No | Helpdesk team ID |
partner_id |
Integer | No | Customer (res.partner) ID |
partner_name |
String | No | Customer name |
partner_email |
String | No | Customer email |
partner_phone |
String | No | Customer phone |
user_id |
Integer | No | Assigned user ID |
stage_id |
Integer | No | Stage ID |
ticket_type_id |
Integer | No | Ticket type ID |
tag_ids |
List[Int] | No | Tag IDs |
priority |
String | No | Priority: '0', '1', '2', '3' |
active |
Boolean | No | Active status (default: true) |
Priority Values¶
| Value | Label |
|---|---|
0 |
All (Default) |
1 |
Low Priority |
2 |
High Priority |
3 |
Urgent |
Read-Only Fields¶
| Field | Type | Description |
|---|---|---|
create_date |
Datetime | Creation timestamp |
write_date |
Datetime | Last update timestamp |
assign_date |
Datetime | First assignment date |
close_date |
Datetime | Ticket close date |
assign_hours |
Integer | Hours to first assignment |
close_hours |
Integer | Hours to close |
sla_deadline |
Datetime | SLA deadline |
sla_fail |
Boolean | SLA failed status |
Important: Request Format¶
The REST API expects form data with a vals parameter, NOT a JSON body:
# Correct format - form data with vals parameter
curl -X POST "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-d "vals={\"name\": \"Test Ticket\", \"team_id\": 1}"
# Incorrect - JSON body will cause "type http vs json" error
curl -X POST "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{"name": "Test Ticket"}'
In Python:
import json
# Correct
requests.post(url, headers={'X-API-Key': api_key}, data={'vals': json.dumps(values)})
# Incorrect
requests.post(url, headers={'X-API-Key': api_key}, json=values)
API Examples¶
Create a Ticket¶
curl -X POST "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-d 'vals={"name": "Installation Issue - Blinds not fitting", "description": "<p>Customer reports blinds are 2cm too wide for window frame.</p>", "partner_id": 123, "priority": "2", "team_id": 1}'
Response (201 Created):
{
"helpdesk.ticket": {
"id": 456,
"name": "Installation Issue - Blinds not fitting",
"description": "<p>Customer reports blinds are 2cm too wide for window frame.</p>",
"partner_id": [123, "John Smith"],
"priority": "2",
"team_id": [1, "Support"],
"stage_id": [1, "New"],
"user_id": [5, "Support Agent"],
"create_date": "2025-12-13 10:30:00"
}
}
Search Tickets¶
# Get all urgent tickets
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('priority','=','3')]" \
--data-urlencode "fields=['name','partner_id','stage_id','create_date']" \
--data-urlencode "limit=20" \
--data-urlencode "order=create_date desc"
Response (200 OK):
{
"helpdesk.ticket": [
{
"id": 456,
"name": "Installation Issue - Blinds not fitting",
"partner_id": [123, "John Smith"],
"stage_id": [1, "New"],
"create_date": "2025-12-13 10:30:00"
},
{
"id": 455,
"name": "Urgent: Motor malfunction",
"partner_id": [124, "Jane Doe"],
"stage_id": [2, "In Progress"],
"create_date": "2025-12-12 15:00:00"
}
]
}
Get Single Ticket¶
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket/456" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','description','partner_id','stage_id','user_id','priority','sla_deadline']"
Update a Ticket¶
# Change stage and priority
curl -X PUT "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket/456" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"stage_id": 3,
"priority": "3",
"user_id": 10
}'
Delete a Ticket¶
curl -X DELETE "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket/456" \
-H "X-API-Key: your_api_key"
Search by Customer¶
# Find all tickets for a specific customer
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('partner_id','=',123)]" \
--data-urlencode "fields=['name','stage_id','priority','create_date']" \
--data-urlencode "order=create_date desc"
Search Open Tickets¶
# Find tickets not in closed stage
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('stage_id.is_close','=',False)]" \
--data-urlencode "fields=['name','stage_id','user_id','sla_deadline']"
Related Models¶
Helpdesk Team (helpdesk.team)¶
# List teams
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.team" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','use_sla']"
Helpdesk Stage (helpdesk.stage)¶
# List stages for a team
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.stage" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('team_ids','in',[1])]" \
--data-urlencode "fields=['name','sequence','is_close']"
Ticket Types (helpdesk.ticket.type)¶
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket.type" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','sequence']"
Tags (helpdesk.tag)¶
curl -X GET "https://your-odoo.com/restapi/1.0/object/helpdesk.tag" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','color']"
Domain Filter Examples¶
Common Filters¶
# Urgent tickets
[('priority', '=', '3')]
# Open tickets (not closed)
[('stage_id.is_close', '=', False)]
# Assigned to specific user
[('user_id', '=', 10)]
# Unassigned tickets
[('user_id', '=', False)]
# Created today
[('create_date', '>=', '2025-12-13 00:00:00')]
# SLA failed
[('sla_fail', '=', True)]
# Customer email contains domain
[('partner_email', 'ilike', '@company.com')]
# Multiple conditions (AND)
[('priority', '=', '3'), ('stage_id.is_close', '=', False)]
# OR conditions
['|', ('priority', '=', '3'), ('priority', '=', '2')]
Error Handling¶
Common Error Responses¶
401 Unauthorized:
404 Not Found:
400 Bad Request:
Rate Limiting¶
The API does not enforce rate limiting by default. However, consider implementing client-side throttling for high-volume integrations to avoid overloading the server.
Creating Tickets with Partner & Address¶
When creating tickets from external forms (like the landing page), you often need to: 1. Find or create a partner with address 2. Create the ticket linked to the partner
Step 1: Look Up State ID¶
Convert state code (e.g., "TX") to Odoo state ID:
curl -X GET "https://your-odoo.com/restapi/1.0/object/res.country.state" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('code','=','TX'),('country_id.code','=','US')]" \
--data-urlencode "fields=['id','name']" \
--data-urlencode "limit=1"
Response: {"res.country.state": {"id": 52, "name": "Texas"}}
Step 2: Find or Create Partner¶
# Search for existing partner by email
curl -X GET "https://your-odoo.com/restapi/1.0/object/res.partner" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('email','=','john@example.com')]" \
--data-urlencode "fields=['id','name','phone','street','city']" \
--data-urlencode "limit=1"
# If not found, create new partner with address
curl -X POST "https://your-odoo.com/restapi/1.0/object/res.partner" \
-H "X-API-Key: your_api_key" \
-d 'vals={"name": "John Smith", "email": "john@example.com", "phone": "512-555-1234", "street": "123 Main St", "city": "Austin", "state_id": 52, "zip": "78701", "country_id": 233}'
Step 3: Create Ticket with Partner¶
curl -X POST "https://your-odoo.com/restapi/1.0/object/helpdesk.ticket" \
-H "X-API-Key: your_api_key" \
-d 'vals={"name": "Issue with blinds", "partner_id": 60, "partner_name": "John Smith", "partner_email": "john@example.com", "partner_phone": "512-555-1234", "description": "<p>Blinds not responding to remote</p>", "team_id": 1}'
Complete Flow (Python)¶
import json
import requests
def create_ticket_with_partner(api_url, api_key, form_data):
"""Create a helpdesk ticket with partner from form submission."""
headers = {'X-API-Key': api_key}
# 1. Look up state ID
state_id = None
if form_data.get('state_code'):
response = requests.get(
f"{api_url}/restapi/1.0/object/res.country.state",
headers=headers,
params={
'domain': json.dumps([
('code', '=', form_data['state_code']),
('country_id.code', '=', 'US')
]),
'fields': json.dumps(['id']),
'limit': 1
}
)
data = response.json().get('res.country.state', {})
state_id = data.get('id') if isinstance(data, dict) else data[0].get('id') if data else None
# 2. Find or create partner
response = requests.get(
f"{api_url}/restapi/1.0/object/res.partner",
headers=headers,
params={
'domain': json.dumps([('email', '=', form_data['email'])]),
'fields': json.dumps(['id']),
'limit': 1
}
)
partner_data = response.json().get('res.partner', {})
if partner_data and partner_data.get('id'):
partner_id = partner_data['id']
else:
# Create new partner
partner_vals = {
'name': form_data['name'],
'email': form_data['email'],
'phone': form_data.get('phone', ''),
'street': form_data.get('street', ''),
'city': form_data.get('city', ''),
'zip': form_data.get('zip', ''),
}
if state_id:
partner_vals['state_id'] = state_id
partner_vals['country_id'] = 233 # US
response = requests.post(
f"{api_url}/restapi/1.0/object/res.partner",
headers=headers,
data={'vals': json.dumps(partner_vals)}
)
partner_id = response.json().get('res.partner', {}).get('id')
# 3. Create ticket
ticket_vals = {
'name': form_data['subject'],
'partner_id': partner_id,
'partner_name': form_data['name'],
'partner_email': form_data['email'],
'partner_phone': form_data.get('phone', ''),
'description': form_data.get('description', ''),
'team_id': 1,
}
response = requests.post(
f"{api_url}/restapi/1.0/object/helpdesk.ticket",
headers=headers,
data={'vals': json.dumps(ticket_vals)}
)
return response.json().get('helpdesk.ticket', {})
Next Steps¶
- See Python Integration Examples for complete code samples
- Review REST API Authentication for detailed auth setup
- Check Helpdesk Module Documentation for business logic details
- See Landing Page Forms API for website form integration