Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
201 changes: 201 additions & 0 deletions endpoint_json2/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association

==============
Endpoint JSON2
==============

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:c7e6ba5b070db8b93a1ae44bd87344a39b885ff2b4667a1a26e9e7b1cc677402
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb--api-lightgray.png?logo=github
:target: https://github.com/OCA/web-api/tree/19.0/endpoint_json2
:alt: OCA/web-api
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-api-19-0/web-api-19-0-endpoint_json2
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/web-api&target_branch=19.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Adds ``exec_mode="json2"`` to the endpoint framework, enabling
declarative JSON-2 API endpoint configuration. Select a model, method,
and parameters — the module handles dispatch, parameter validation,
access control, and result filtering. A code snippet can be used as an
alternative to a model method for quick, ad-hoc logic.

Also provides auto-generated API documentation endpoints at
``/json2/doc``.

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

Configuration
=============

Go to *Settings > Technical > Endpoints* and create a new endpoint with
**Exec Mode** set to **JSON-2 API**.

Basic Setup
-----------

- **Route Group** and **Name**: Together these determine the endpoint
URL, which is automatically computed as
``/json2/{route_group}/{name}``. For example, a route group
``contacts`` with name ``get_partners`` produces
``/json2/contacts/get_partners``. The route group also organizes
endpoints in the API documentation at ``/json2/doc/{route_group}``.

- **Model**: The Odoo model to operate on (e.g. ``res.partner``).

- **Method**: A public model method (e.g. ``search_read``).
Alternatively, provide a **Code Snippet** for custom logic — these
two fields are mutually exclusive.

- **Response Fields**: One field per line. Optionally follow with an
alias to rename the key in the response. Use dotted notation (one
level) for relational fields (Many2one, Many2many, One2many). Leave
empty to return all fields. Example:

::

name
email
country_id.name country
write_date last_modified

- **Default Domain**: A JSON-formatted domain filter applied to every
request (e.g. ``[["active", "=", true]]``).

- **Parameters**: Define named parameters with types, defaults, and
required flags. These are validated before the method is called.

Access Control
--------------

All endpoint execution is wrapped in ``sudo()``, allowing API users to
operate with minimal Odoo privileges. Access is controlled at two
levels:

- **Auth Type**: Select the authentication method for the endpoint
(e.g. **Bearer** for API key authentication).
- **Allowed Groups**: Restrict endpoint access to specific user groups.
Create integration-specific groups (e.g. "Hospital System", "WMS")
and assign them to the corresponding API users. Each endpoint
declares which groups may call it. Leave empty to allow any
authenticated user.

Code Snippets
-------------

As an alternative to a model method, a code snippet can be used for
quick, ad-hoc logic. Available variables:

- ``Model``: The target model (with ``sudo()``).
- ``params``: Validated parameters from the request.
- ``env``: The Odoo environment.
- ``Command``: Odoo's ``Command`` helper for relational field writes.
- ``json``: Safe JSON module for serialization.
- ``exceptions``: Werkzeug exceptions (``BadRequest``, ``NotFound``,
etc.).
- ``log``: Log messages to the ``ir.logging`` table.

The snippet must set a ``result`` variable with the response data.

Usage
=====

Calling an Endpoint
-------------------

Send a POST request with a JSON body to the endpoint's route. The
example below uses Bearer authentication with an API key:

.. code:: bash

curl -X POST https://your-odoo.com/json2/contacts/get_partners \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"domain": [["is_company", "=", true]], "limit": 10}'

API Documentation
-----------------

Auto-generated documentation for all JSON-2 endpoints is available at
``/json2/doc``, grouped by route group. Each endpoint's visibility
respects the **Allowed Groups** setting — users only see endpoints they
have access to. Filter by route group with ``/json2/doc/{route_group}``.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/web-api/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web-api/issues/new?body=module:%20endpoint_json2%0Aversion:%2019.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* Quartile

Contributors
------------

- Quartile <https://www.quartile.co>

- Yoshi Tashiro

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

.. |maintainer-yostashiro| image:: https://github.com/yostashiro.png?size=40px
:target: https://github.com/yostashiro
:alt: yostashiro
.. |maintainer-aungkokolin1997| image:: https://github.com/aungkokolin1997.png?size=40px
:target: https://github.com/aungkokolin1997
:alt: aungkokolin1997

Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-yostashiro| |maintainer-aungkokolin1997|

This module is part of the `OCA/web-api <https://github.com/OCA/web-api/tree/19.0/endpoint_json2>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions endpoint_json2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import controllers
from . import models
20 changes: 20 additions & 0 deletions endpoint_json2/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
{
"name": "Endpoint JSON2",
"summary": "Declarative JSON-2 API endpoints on the endpoint stack",
"version": "19.0.1.0.0",
"license": "LGPL-3",
"development_status": "Alpha",
"author": "Quartile, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/web-api",
"category": "Technical",
"depends": ["endpoint"],
"data": [
"security/ir.model.access.csv",
"views/endpoint_views.xml",
],
"demo": ["demo/endpoint_json2_demo.xml"],
"installable": True,
"maintainers": ["yostashiro", "aungkokolin1997"],
}
1 change: 1 addition & 0 deletions endpoint_json2/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
67 changes: 67 additions & 0 deletions endpoint_json2/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2026 Quartile (https://www.quartile.co)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).

from werkzeug.exceptions import NotFound

from odoo import http
from odoo.http import request


class EndpointJson2DocController(http.Controller):
def _get_accessible_endpoints(self, extra_domain=None):
domain = [("exec_mode", "=", "json2")] + (extra_domain or [])
all_endpoints = request.env["endpoint.endpoint"].sudo().search(domain)
user = request.env.user
return all_endpoints.filtered(
lambda ep: ep.json2_group_ids & user.all_group_ids
)

def _endpoint_to_doc(self, endpoint):
return {
"name": endpoint.name,
"description": endpoint.json2_description or "",
"method": endpoint.json2_method,
"model": endpoint.json2_model_name,
"url": endpoint.route,
"parameters": [
{
"name": p.name,
"type": p.param_type,
"required": p.required,
"description": p.description or "",
"default": p.default_value,
}
for p in endpoint.json2_param_ids
],
}

@http.route(
"/json2/doc",
methods=["GET"],
auth="user",
type="http",
readonly=True,
save_session=False,
)
def doc_index(self):
endpoints = self._get_accessible_endpoints()
result = {}
for ep in endpoints:
result.setdefault(ep.route_group, []).append(self._endpoint_to_doc(ep))
return request.make_json_response(result)

@http.route(
"/json2/doc/<string:route_group>",
methods=["GET"],
auth="user",
type="http",
readonly=True,
save_session=False,
)
def doc_domain(self, route_group):
endpoints = self._get_accessible_endpoints([("route_group", "=", route_group)])
if not endpoints:
raise NotFound(f"No endpoints found for domain {route_group!r}")
return request.make_json_response(
[self._endpoint_to_doc(ep) for ep in endpoints]
)
Loading
Loading