API issues¶
Common failure patterns seen in dk_customer_portal_api, and how to
recognize them from the response.
CORS preflight failures¶
Symptom: requests fail in the browser before your code even sees a
response — dev tools show a failed OPTIONS request or a missing
Access-Control-Allow-Origin header.
Common causes:
The calling origin isn’t in the controller’s
allowed_originslist (see Authentication § CORS). Local dev origins outsidelocalhost:3000/3001/8069won’t get the header back.A route’s
methodslist is missing"OPTIONS"— every route needs it explicitly, since Odoo won’t add it implicitly.A response was built without going through the controller’s
_json_response()helper, so the CORS headers were never attached.
Fix: add the origin to allowed_origins, add "OPTIONS" to the route’s
methods, and make sure every return path uses _json_response() rather
than constructing a response manually.
401 on routes that should require a session¶
Symptom: request.env.user._is_public() is used to check
authentication on an auth="none" route.
Cause: auth="none" routes never populate request.env.user from a
session — there is no session. Use
self._get_authenticated_user() (the shared bearer-token helper — see
Authentication) instead.
400 on POST /api/subscription/select-plan / saas.configuration not saved¶
Symptom: the select-plan call returns 200, but the next call in the
flow (subscribe or confirm) fails with a 400 because it can’t find a
configuration for the tenant.
Cause: the saas.configuration record created by
select_subscription_plan wasn’t actually persisted — check that the
create()/write() call isn’t being rolled back by an exception earlier in
the same request, and that the tenant lookup used to scope the record
matches the tenant used later in subscribe/confirm.
500 with a generic message¶
Every controller wraps its body in try/except Exception as e and returns
{"success": false, "message": str(e)} with status 500. The message is
whatever Python’s exception str() produces — often not very descriptive
on its own. Check the Odoo server log around the request timestamp;
controllers log with _logger.exception(...) right before returning, which
captures the full traceback even though the HTTP response doesn’t include
it.