405 Method Not Allowed in Odoo API
When calling Odoo API endpoints, you may encounter:
HTTP/1.1 405 Method Not Allowed
Allow: POST
{"error": {"code": 404, "message": "404: Not Found"}}
TypeError: my_endpoint() got an unexpected keyword argument 'jsonrpc'
werkzeug.exceptions.MethodNotAllowed: 405 Method Not AllowedUnderstanding Odoo Route Types
Odoo has two route types with different requirements:
| Property | type='json' | type='http' |
|---|---|---|
| HTTP Method | POST only | GET or POST |
| Content-Type | application/json | form or multipart |
| Request body | JSON-RPC format | Form data or query params |
| Response | JSON-RPC wrapped | Raw HTML or JSON |
Fix 1: Wrong HTTP Method for JSON Routes
# Odoo JSON routes ONLY accept POST
# Wrong:
curl -X GET https://odoo.example.com/api/data
# Returns: 405 Method Not Allowed
# Right:
curl -X POST https://odoo.example.com/api/data \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "params": {}}'Fix 2: Missing JSON-RPC Wrapper
# Wrong — raw JSON without JSON-RPC envelope
curl -X POST https://odoo.example.com/api/data \
-H "Content-Type: application/json" \
-d '{"name": "test"}'
# Returns: TypeError or empty response
# Right — JSON-RPC envelope
curl -X POST https://odoo.example.com/api/data \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "method": "call", "params": {"name": "test"}}'
# Note: for type='json' routes, your method parameters
# go inside the "params" objectFix 3: Specifying HTTP Methods on Routes
# Default: type='json' only accepts POST
# To allow specific methods on type='http':
@http.route('/api/items', type='http', auth='public', methods=['GET'])
def list_items(self, **kwargs):
return json.dumps({'items': []})
@http.route('/api/items', type='http', auth='public', methods=['POST'])
def create_item(self, **kwargs):
return json.dumps({'status': 'created'})
# Wrong — trying to use methods parameter with type='json'
# type='json' ignores the methods parameter and always uses POST
@http.route('/api/data', type='json', auth='public', methods=['GET']) # GET won't work
def get_data(self, **kwargs):
passFix 4: Route Not Found (404 vs 405)
# If you get 404 instead of 405, the route does not exist at all
# Check if the module defining the route is installed
# Settings > Apps > search for module name
# Verify the route is registered
# In odoo shell:
from odoo.http import root
for rule in root.routing_map.iter_rules():
if 'api' in rule.rule:
print(f"{rule.rule} -> {rule.methods}")
# Common issues:
# - Module not installed
# - Controller file not imported in __init__.py
# - Typo in route pathFix 5: CSRF Token Issues
# For type='http' with auth='user', Odoo requires CSRF token
# Error: Session expired or invalid CSRF token
# Option 1: Disable CSRF for API endpoints
@http.route('/api/data', type='http', auth='public', csrf=False)
def get_data(self, **kwargs):
pass
# Option 2: Include CSRF token in request
# Get token from session first, then include as parameter
curl -X POST https://odoo.example.com/api/data \
-b "session_id=abc123" \
-d "csrf_token=xyz789&name=test"
# type='json' routes are CSRF-exempt by defaultFix 6: Authentication Method Mismatch
# auth='user' requires a valid session (logged in via web)
# auth='public' allows anonymous access
# auth='none' bypasses authentication entirely
# For API endpoints called by external systems:
@http.route('/api/data', type='json', auth='public')
def get_data(self, **kwargs):
# Validate API key or JWT manually
api_key = request.httprequest.headers.get('Authorization')
if not api_key:
return {'error': 'Unauthorized'}
# ... validate and proceed
# For internal endpoints:
@http.route('/api/internal', type='json', auth='user')
def internal_data(self, **kwargs):
# request.env.user is available
return {'user': request.env.user.name}Debugging API Routes
# Test with curl and verbose output
curl -v -X POST https://odoo.example.com/api/endpoint \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "params": {}}'
# Check response headers for clues:
# Allow: POST → you used the wrong method
# 404 → route doesn't exist
# 500 → route exists but code crashedPrevention
DeployMonkey's AI agent validates API endpoint configurations and tests request formats during development. Route mismatches and authentication issues are caught before deployment.