Skip to content

REST API Module

RESTful interface for Odoo data access with multiple authentication methods.

Field Value
Technical Name restapi
Version 2.0.0
Category Tools
Dependencies base, web, base_automation
Python Dependencies oauthlib, jwt, cryptography

Overview

The Odoo REST API module provides:

  • Standard HTTP methods (GET, POST, PUT, DELETE)
  • Multiple authentication methods (OAuth1, OAuth2, JWT, API Key)
  • Full CRUD operations on any Odoo model
  • Method calls for custom business logic
  • Report printing
  • IP restriction support
  • Configurable token expiry

Authentication Methods

Method Use Case Stateless Best For
Client Credentials Server-to-server No AWS Lambda, cron jobs
Client Credentials + JWT High-performance serverless Yes Frequent API calls
API Key Simple integrations N/A Scripts, testing
OAuth2 Authorization Code Web apps with user login No User-facing apps
OAuth1 Legacy integrations No Existing OAuth1 clients

Quick Start

1. Get Access Token

curl -X POST https://your-odoo.com/restapi/1.0/common/oauth2/token \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Response:

{
    "access_token": "abc123xyz...",
    "token_type": "Bearer",
    "expires_in": 3600
}

2. Make API Requests

# Search partners
curl "https://your-odoo.com/restapi/1.0/object/res.partner?limit=10" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Create partner
curl -X POST https://your-odoo.com/restapi/1.0/object/res.partner \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "New Partner"}'

JWT Authentication

For high-performance scenarios, use JWT tokens:

curl -X POST https://your-odoo.com/restapi/1.0/common/oauth2/token \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "token_format=jwt"

JWT tokens are verified cryptographically without database lookups.

API Key Authentication

For simple scripts:

# Using X-API-Key header
curl "https://your-odoo.com/restapi/1.0/object/res.partner" \
  -H "X-API-Key: YOUR_API_KEY"

# Or using Authorization header
curl "https://your-odoo.com/restapi/1.0/object/res.partner" \
  -H "Authorization: ApiKey YOUR_API_KEY"

Data Models

auth.auth

Main authentication configuration record.

Field Type Description
name Char Application name
description Text Application description
organization_name Char Organization name
consumer_key Char Client ID for OAuth
consumer_secret Char Client Secret for OAuth
user_id Many2one User to act as
client_credentials_enabled Boolean Enable client credentials grant
token_expiry_seconds Integer Token validity (default: 3600)
api_key Char API Key (generated)
api_key_active Boolean API Key active status
jwt_enabled Boolean Enable JWT tokens
allowed_ips Text IP whitelist (one per line)
redirect_uris One2many OAuth redirect URIs

auth.access.token

Stores active access tokens.

Field Type Description
access_token Char Token value
access_token_validity Datetime Expiration time
auth_id Many2one Link to auth.auth

Configuration

1. Create REST API Client

Navigate to Settings > Technical > REST API > Auth:

  1. Click Create
  2. Fill in fields:
Field Value
Name "My Application"
User Select API user
Enable Client Credentials True
Token Expiry (seconds) 3600
Enable JWT Tokens True (optional)
Allowed IPs Optional whitelist
  1. Save - Consumer Key and Secret are auto-generated

2. Generate API Key (Optional)

  1. Open the auth record
  2. Click Generate API Key
  3. Copy the API key (shown once)

3. Add Redirect URIs (for OAuth2 Authorization Code)

In the Redirect URIs tab: - Add your callback URL(s) - Example: https://your-app.com/oauth/callback

API Reference

Base URL

https://your-odoo.com/restapi/1.0

Authentication Endpoints

Endpoint Method Description
/common/oauth2/token POST Get access token
/common/oauth2/authorize GET OAuth2 authorization
/common/oauth2/revoke POST Revoke token
/common/version GET Get Odoo version

Token Request Parameters

Parameter Required Description
grant_type Yes client_credentials or authorization_code
client_id Yes Consumer key
client_secret Yes Consumer secret
token_format No Set to jwt for JWT tokens
code For auth code Authorization code
redirect_uri For auth code Redirect URI

Data Endpoints

Endpoint Method Description
/object/{model} GET Search and read records
/object/{model} POST Create new record
/object/{model}/{id} GET Read single record
/object/{model}/{id} PUT Update record
/object/{model}/{id} DELETE Delete record
/object/{model}/{id}/{method} POST Call record method
/object/{model}/{method} POST Call model method

Query Parameters

Parameter Type Description Example
domain string Search filter (Odoo domain) [('name','ilike','test')]
fields string Fields to return ['name','email']
offset int Skip first N records 10
limit int Maximum records 50
order string Sort order name asc

Python Client

import requests
import time

class OdooAPI:
    def __init__(self, base_url, client_id, client_secret):
        self.base_url = base_url.rstrip('/')
        self.client_id = client_id
        self.client_secret = client_secret
        self.access_token = None
        self.token_expires_at = 0

    def get_token(self):
        """Get or refresh access token."""
        if self.access_token and time.time() < self.token_expires_at - 60:
            return self.access_token

        response = requests.post(
            f"{self.base_url}/restapi/1.0/common/oauth2/token",
            data={
                'grant_type': 'client_credentials',
                'client_id': self.client_id,
                'client_secret': self.client_secret
            }
        )
        response.raise_for_status()
        data = response.json()

        self.access_token = data['access_token']
        self.token_expires_at = time.time() + data['expires_in']
        return self.access_token

    def _headers(self):
        return {'Authorization': f'Bearer {self.get_token()}'}

    def search_read(self, model, domain=None, fields=None, limit=None, offset=None, order=None):
        """Search and read records."""
        params = {}
        if domain: params['domain'] = str(domain)
        if fields: params['fields'] = str(fields)
        if limit: params['limit'] = limit
        if offset: params['offset'] = offset
        if order: params['order'] = order

        response = requests.get(
            f"{self.base_url}/restapi/1.0/object/{model}",
            headers=self._headers(),
            params=params
        )
        response.raise_for_status()
        return response.json()

    def read(self, model, record_id, fields=None):
        """Read single record."""
        params = {}
        if fields: params['fields'] = str(fields)

        response = requests.get(
            f"{self.base_url}/restapi/1.0/object/{model}/{record_id}",
            headers=self._headers(),
            params=params
        )
        response.raise_for_status()
        return response.json()

    def create(self, model, values):
        """Create new record."""
        response = requests.post(
            f"{self.base_url}/restapi/1.0/object/{model}",
            headers={**self._headers(), 'Content-Type': 'application/json'},
            json=values
        )
        response.raise_for_status()
        return response.json()

    def write(self, model, record_id, values):
        """Update record."""
        response = requests.put(
            f"{self.base_url}/restapi/1.0/object/{model}/{record_id}",
            headers={**self._headers(), 'Content-Type': 'application/json'},
            json=values
        )
        response.raise_for_status()
        return response.json()

    def delete(self, model, record_id):
        """Delete record."""
        response = requests.delete(
            f"{self.base_url}/restapi/1.0/object/{model}/{record_id}",
            headers=self._headers()
        )
        response.raise_for_status()
        return response.json()

    def call_method(self, model, record_id, method, args=None, kwargs=None):
        """Call method on record."""
        response = requests.post(
            f"{self.base_url}/restapi/1.0/object/{model}/{record_id}/{method}",
            headers={**self._headers(), 'Content-Type': 'application/json'},
            json={'args': args or [], 'kwargs': kwargs or {}}
        )
        response.raise_for_status()
        return response.json()

Usage Example

api = OdooAPI(
    base_url='https://your-odoo.com',
    client_id='your_client_id',
    client_secret='your_client_secret'
)

# Search partners
partners = api.search_read('res.partner',
    domain=[('customer_rank', '>', 0)],
    fields=['name', 'email'],
    limit=10
)

# Create partner
new_id = api.create('res.partner', {
    'name': 'New Customer',
    'email': 'customer@example.com'
})

# Update partner
api.write('res.partner', new_id, {'phone': '555-1234'})

# Call method
result = api.call_method('fsm.order', 123, 'action_mark_signature_complete')

Error Handling

HTTP Status Error Code Description
400 invalid_request Missing or invalid parameters
401 invalid_client Invalid credentials
401 token_expired Token has expired
403 access_denied Insufficient permissions
403 ip_restricted IP not in whitelist
404 not_found Record not found
500 server_error Internal server error

Error Response Format

{
    "error": {
        "code": 401,
        "message": "Authentication Required"
    }
}

Module Structure

restapi/
├── __manifest__.py
├── __init__.py
├── controllers/
│   ├── __init__.py
│   └── main.py              # API endpoints
├── models/
│   ├── __init__.py
│   ├── auth.py              # Authentication models
│   ├── jwt_utils.py         # JWT utilities
│   ├── ir_models.py         # Model extensions
│   └── res_users.py         # User extensions
├── views/
│   ├── auth_view.xml        # Auth configuration views
│   ├── restapi_cron.xml     # Cron jobs
│   └── ir_models_view.xml
├── security/
│   └── ir.model.access.csv
├── data/
│   ├── auth_data.xml        # Default data
│   └── mail_template.xml    # Email templates
├── tests/
│   ├── test_client_credentials.py
│   ├── test_jwt_utils.py
│   ├── test_endpoints.py
│   └── test_api_key.py
└── doc/
    └── DEVELOPER_GUIDE.md

Security Best Practices

  1. Use HTTPS - Always use HTTPS in production
  2. Rotate credentials - Regenerate secrets periodically
  3. IP whitelist - Restrict access to known IPs
  4. Minimal permissions - Use user with limited access
  5. JWT for high throughput - Reduces database load
  6. Monitor access - Review access logs regularly

Best Practices

  1. Cache tokens - Don't request new token for every call
  2. Use JWT for high throughput - Eliminates DB lookups
  3. Request only needed fields - Improves performance
  4. Implement retry logic - Handle transient failures
  5. Set appropriate timeouts - Prevent hanging requests
  6. Use pagination - Don't fetch all records at once

Troubleshooting

Invalid Credentials

  1. Verify consumer key and secret are correct
  2. Check client credentials grant is enabled
  3. Ensure auth record is active

Token Expired

  1. Request new token before expiry
  2. Implement automatic token refresh in client

IP Restricted

  1. Check your IP is in the whitelist
  2. Verify IP format (one per line)
  3. Clear whitelist to allow all IPs

Permission Denied

  1. Check API user has required permissions
  2. Verify record access rules
  3. Test with admin user to isolate issue