CRM Leads API
Manage CRM leads and opportunities via the REST API.
Overview
| Operation |
Method |
Endpoint |
| Create |
POST |
/restapi/1.0/object/crm.lead |
| List/Search |
GET |
/restapi/1.0/object/crm.lead |
| Read |
GET |
/restapi/1.0/object/crm.lead/{id} |
| Update |
PUT |
/restapi/1.0/object/crm.lead/{id} |
| Delete |
DELETE |
/restapi/1.0/object/crm.lead/{id} |
Authentication
X-API-Key: your_api_key_here
Lead Fields Reference
Core Fields
| Field |
Type |
Required |
Description |
name |
String |
Yes |
Lead/Opportunity name |
type |
String |
No |
'lead' or 'opportunity' |
contact_name |
String |
No |
Contact person name |
email_from |
String |
No |
Email address |
phone |
String |
No |
Phone number |
mobile |
String |
No |
Mobile number |
partner_id |
Integer |
No |
Linked customer ID |
Address Fields
| Field |
Type |
Description |
street |
String |
Street address |
street2 |
String |
Street address line 2 |
city |
String |
City |
state_id |
Integer |
State ID |
zip |
String |
ZIP/Postal code |
country_id |
Integer |
Country ID |
Sales Fields
| Field |
Type |
Description |
stage_id |
Integer |
Pipeline stage ID |
team_id |
Integer |
Sales team ID |
user_id |
Integer |
Salesperson ID |
expected_revenue |
Float |
Expected revenue |
probability |
Float |
Win probability (0-100) |
date_deadline |
Date |
Expected closing date |
Tracking Fields (UTM)
| Field |
Type |
Description |
source_id |
Integer |
Lead source (utm.source) |
medium_id |
Integer |
Medium (utm.medium) |
campaign_id |
Integer |
Campaign (utm.campaign) |
Priority Values
| Value |
Label |
0 |
Normal |
1 |
Low |
2 |
High |
3 |
Very High |
Pipeline Stages (JDX)
| Stage |
Description |
| New Sign-Up |
Fresh lead inquiry |
| Contacted |
Initial contact made |
| Appointment |
Measurement scheduled |
| Visited |
On-site visit completed |
| Quote Sent |
Quotation delivered |
| 2nd Reminded |
First follow-up done |
| 3rd Reminded |
Second follow-up done |
| Need Time |
Customer needs time |
| Won |
Deal closed |
API Examples
Create a Lead
curl -X POST "https://your-odoo.com/restapi/1.0/object/crm.lead" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Website Inquiry - Motorized Blinds",
"type": "lead",
"contact_name": "John Smith",
"email_from": "john@example.com",
"phone": "+1-555-123-4567",
"street": "123 Main St",
"city": "Austin",
"state_id": 44,
"zip": "78701",
"description": "Interested in motorized blinds for living room and bedroom",
"source_id": 1
}'
Response (201 Created):
{
"crm.lead": {
"id": 2107,
"name": "Website Inquiry - Motorized Blinds",
"type": "lead",
"contact_name": "John Smith",
"email_from": "john@example.com",
"phone": "+1-555-123-4567",
"stage_id": [1, "New Sign-Up"],
"create_date": "2025-12-13 10:30:00"
}
}
Create an Opportunity (Skip Lead Stage)
curl -X POST "https://your-odoo.com/restapi/1.0/object/crm.lead" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Office Blinds - ABC Corp",
"type": "opportunity",
"partner_id": 123,
"expected_revenue": 5000.00,
"probability": 50,
"user_id": 5,
"team_id": 1,
"date_deadline": "2025-12-31"
}'
Search Leads by Stage
# Get all leads in "New Sign-Up" stage
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.lead" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('stage_id','=',1),('type','=','lead')]" \
--data-urlencode "fields=['name','contact_name','phone','email_from','create_date']" \
--data-urlencode "limit=50" \
--data-urlencode "order=create_date desc"
Search by Email Domain
# Find leads from a specific company
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.lead" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "domain=[('email_from','ilike','@bigcustomer.com')]" \
--data-urlencode "fields=['name','contact_name','email_from','stage_id']"
Get Lead Detail
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.lead/2107" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','contact_name','phone','mobile','email_from','street','city','state_id','zip','stage_id','user_id','expected_revenue','probability','description']"
Update Lead Stage
# Move lead to "Contacted" stage
curl -X PUT "https://your-odoo.com/restapi/1.0/object/crm.lead/2107" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"stage_id": 2
}'
Convert Lead to Opportunity
curl -X PUT "https://your-odoo.com/restapi/1.0/object/crm.lead/2107" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"type": "opportunity",
"expected_revenue": 3500.00,
"probability": 30
}'
Mark Lead as Won
curl -X PUT "https://your-odoo.com/restapi/1.0/object/crm.lead/2107" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"stage_id": 9,
"probability": 100
}'
Archive (Mark as Lost)
curl -X PUT "https://your-odoo.com/restapi/1.0/object/crm.lead/2107" \
-H "X-API-Key: your_api_key" \
-H "Content-Type: application/json" \
-d '{
"active": false,
"lost_reason": 1
}'
CRM Stages
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.stage" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','sequence','is_won','fold']" \
--data-urlencode "order=sequence"
Sales Teams
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.team" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name','user_id','member_ids']"
Lead Sources (UTM)
curl -X GET "https://your-odoo.com/restapi/1.0/object/utm.source" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name']"
Lost Reasons
curl -X GET "https://your-odoo.com/restapi/1.0/object/crm.lost.reason" \
-H "X-API-Key: your_api_key" \
-G \
--data-urlencode "fields=['name']"
Domain Filter Examples
# Active leads only
[('active', '=', True)]
# Leads (not opportunities)
[('type', '=', 'lead')]
# Opportunities only
[('type', '=', 'opportunity')]
# By stage
[('stage_id', '=', 1)]
# By salesperson
[('user_id', '=', 5)]
# Unassigned leads
[('user_id', '=', False)]
# Created in last 7 days
[('create_date', '>=', '2025-12-06')]
# High probability opportunities
[('type', '=', 'opportunity'), ('probability', '>=', 50)]
# Phone number contains
[('phone', 'ilike', '555')]
# Multiple conditions (AND)
[('stage_id', '=', 1), ('active', '=', True)]
# OR conditions
['|', ('phone', 'ilike', '555'), ('mobile', 'ilike', '555')]
Python Client Example
import requests
class CRMLeadClient:
def __init__(self, base_url: str, api_key: str):
self.base_url = base_url.rstrip('/')
self.headers = {
'X-API-Key': api_key,
'Content-Type': 'application/json'
}
def create_lead(
self,
name: str,
contact_name: str = None,
email: str = None,
phone: str = None,
source_id: int = None,
**kwargs
) -> dict:
"""Create a new lead from website form, webhook, etc."""
data = {
'name': name,
'type': 'lead'
}
if contact_name:
data['contact_name'] = contact_name
if email:
data['email_from'] = email
if phone:
data['phone'] = phone
if source_id:
data['source_id'] = source_id
data.update(kwargs)
response = requests.post(
f"{self.base_url}/restapi/1.0/object/crm.lead",
headers=self.headers,
json=data
)
response.raise_for_status()
return response.json().get('crm.lead', {})
def get_leads_by_stage(self, stage_id: int, limit: int = 100) -> list:
"""Get leads in a specific pipeline stage."""
params = {
'domain': str([('stage_id', '=', stage_id), ('active', '=', True)]),
'fields': "['name','contact_name','phone','email_from','create_date']",
'limit': limit,
'order': 'create_date desc'
}
response = requests.get(
f"{self.base_url}/restapi/1.0/object/crm.lead",
headers=self.headers,
params=params
)
response.raise_for_status()
return response.json().get('crm.lead', [])
def update_stage(self, lead_id: int, stage_id: int) -> dict:
"""Move lead to a different stage."""
response = requests.put(
f"{self.base_url}/restapi/1.0/object/crm.lead/{lead_id}",
headers=self.headers,
json={'stage_id': stage_id}
)
response.raise_for_status()
return response.json().get('crm.lead', {})
def search_leads(self, query: str, limit: int = 50) -> list:
"""Search leads by name, email, or phone."""
# Use OR domain for multi-field search
domain = [
'|', '|',
('name', 'ilike', query),
('contact_name', 'ilike', query),
('email_from', 'ilike', query)
]
params = {
'domain': str(domain),
'fields': "['name','contact_name','phone','email_from','stage_id']",
'limit': limit
}
response = requests.get(
f"{self.base_url}/restapi/1.0/object/crm.lead",
headers=self.headers,
params=params
)
response.raise_for_status()
return response.json().get('crm.lead', [])
# Usage
client = CRMLeadClient('https://your-odoo.com', 'your_api_key')
# Create lead from website form
lead = client.create_lead(
name='Website Inquiry - Blinds',
contact_name='Jane Doe',
email='jane@example.com',
phone='+1-555-987-6543',
source_id=2 # Website source
)
print(f"Created lead #{lead['id']}")
# Get new leads
new_leads = client.get_leads_by_stage(stage_id=1)
print(f"Found {len(new_leads)} new leads")
Webhook Integration
Receive leads from external sources (website forms, landing pages):
from flask import Flask, request, jsonify
app = Flask(__name__)
client = CRMLeadClient('https://your-odoo.com', 'your_api_key')
@app.route('/webhook/lead', methods=['POST'])
def receive_lead():
"""Webhook endpoint for external lead sources."""
data = request.json
try:
lead = client.create_lead(
name=f"Web Lead - {data.get('name', 'Unknown')}",
contact_name=data.get('name'),
email=data.get('email'),
phone=data.get('phone'),
description=data.get('message'),
source_id=2 # Website
)
return jsonify({
'success': True,
'lead_id': lead['id']
})
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
Best Practices
- Always set
type: Use 'lead' for new inquiries, 'opportunity' for qualified prospects
- Track sources: Set
source_id to measure marketing effectiveness
- Use stages consistently: Follow your pipeline stages for accurate reporting
- Store contact info: Always capture
email_from and/or phone for follow-up
- Link to customers: Set
partner_id when lead becomes a repeat customer