Skip to content

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

  1. Always set type: Use 'lead' for new inquiries, 'opportunity' for qualified prospects
  2. Track sources: Set source_id to measure marketing effectiveness
  3. Use stages consistently: Follow your pipeline stages for accurate reporting
  4. Store contact info: Always capture email_from and/or phone for follow-up
  5. Link to customers: Set partner_id when lead becomes a repeat customer