Hi,
I am porting a script that synchronize our Odoo instance stock data with an internal stock management system. The original script used the XML-RPC interface and worked fine. We use the Perl programming language, and our ageing XML-RPC client made us decide to move to JSON-RPC: we guessed that it would be more reliable and that we would be able to find more code examples (porting from Python to Perl is not a problem). Since the Odoo UI also use the JSON-RPC interface it looked like a quality improvement to us, so we made the switch. 
I'll post some Perl code and the generated JSON here, but please consider that as pseudocode, as it's the same as Python in functionalities. If you don't know Perl, don't worry: the syntax is similar to PHP. My biggest concerns are:
- which method to use (especially for the action_apply_inventory call)
- what parameters to pass
- in what data structures those parameters should be stored
This is what I do for a specific Product id. It's the same method as what is described in this post. 
First, we create a new entry through the stock_quant model:
my %params = (
args => [
# It prepares the login params list
$self->_aggregate_auth_params(),
'stock.quant',
'create', {
location_id => $location_id,
product_id => $odoo_product_id,
in_date => $odoo_now,
owner_id => $owner_id,
quantity => $new_quantity,
inventory_date => $odoo_now,
},
],
service => 'object',
method => 'execute',
);
my ($fail, $res, $err) = $self->object_service->call_named(
'call',
%params,
);
$self->_check_errors({ err => $err, res => $res, fail => $fail });
Generated JSON:
{
  "method": "call",
  "jsonrpc": "2.0",
  "params": {
  "args": [
    "CENSORED_DATABASE",
    666,
    "CENSORED_PASSWORD",
    "stock.quant",
    "create",
    {
      "quantity": 20,
      "inventory_date": "2024-08-29 13:47:40",
      "in_date": "2024-08-29 13:47:40",
      "product_id": 4337,
      "location_id": 76
    }
  ],
  "service":"object",
  "method":"execute
}      
After running that, $res contains the id of the stock_quant added item. I checked the stock_quant table: it does have a new entry with the information stored. It worked!
Something to notice is that after some time (a couple of hours), the entry is purged and deleted from the database: obviously it's marked as not applied and is removed by a cron.
Then, we proceed to the action_apply_inventory method call. This one is not conclusive:
my %paramss = (
args => [
$self->_aggregate_auth_params(),
'stock_quant',
'action_apply_inventory',
["" . $new_stock_quant_id]
],
# Also considered 'action' here
service => 'object',
# Also considered 'action_execute' here
method => 'execute',
);
my ($faill, $ress, $errr) = $self->object_service->call_named(
'call',
%paramss,
);
$self->_check_errors({ err => $errr, res => $ress, fail => $faill });
Generated JSON:
{
  "id":0,
  "params": {
    "args" [
      "CENSORED_DATABASE",
      666,
      "CENSORED_PASSWORD",
      "stock.quant",
      "action_apply_inventory",
      ["14505"]
    ],
    "method": "execute",
    "service": "object"
  },
  "method": "call",
  "jsonrpc": "2.0"
}                                                                                                                              The following exception is returned:
{
  code      200,                                                                        data {
    arguments [
      [0] "Record does not exist or has been deleted. (Record: stock.quant('14505',), User: 666)"
    ],
    context     {},
    debug       "Traceback (most recent call last):
File \"/usr/lib/python3/dist-packages/odoo/http.py\", line 1765, in _serve_db
  return service_model.retrying(self._serve_ir_http, self.env)
File \"/usr/lib/python3/dist-packages/odoo/service/model.py\", line 133, in retrying
  result = func()
File \"/usr/lib/python3/dist-packages/odoo/http.py\", line 1792, in _serve_ir_http
  response = self.dispatcher.dispatch(rule.endpoint, args)
File \"/usr/lib/python3/dist-packages/odoo/http.py\", line 1996, in dispatch
  result = self.request.registry['ir.http']._dispatch(endpoint)
File \"/usr/lib/python3/dist-packages/odoo/addons/website/models/ir_http.py\", line 235, in _dispatch
  response = super()._dispatch(endpoint)
File \"/usr/lib/python3/dist-packages/odoo/addons/base/models/ir_http.py\", line 222, in _dispatch
  result = endpoint(**request.params)
File \"/usr/lib/python3/dist-packages/odoo/http.py\", line 722, in route_wrapper
  result = endpoint(self, *args, **params_ok)
File \"/usr/lib/python3/dist-packages/odoo/addons/base/controllers/rpc.py\", line 155, in jsonrpc
  return dispatch_rpc(service, method, args)
File \"/usr/lib/python3/dist-packages/odoo/http.py\", line 388, in dispatch_rpc
  return dispatch(method, params)
File \"/usr/lib/python3/dist-packages/odoo/service/model.py\", line 35, in dispatch
  res = execute(db, uid, *params[3:])
File \"/usr/lib/python3/dist-packages/odoo/service/model.py\", line 65, in execute
  res = execute_cr(cr, uid, obj, method, *args, **kw)
File \"/usr/lib/python3/dist-packages/odoo/service/model.py\", line 50, in execute_cr
  result = retrying(partial(odoo.api.call_kw, recs, method, args, kw), env)
File \"/usr/lib/python3/dist-packages/odoo/service/model.py\", line 133, in retrying
  result = func()
File \"/usr/lib/python3/dist-packages/odoo/api.py\", line 468, in call_kw
  result = _call_kw_multi(method, model, args, kwargs)
File \"/usr/lib/python3/dist-packages/odoo/api.py\", line 453, in _call_kw_multi
  result = method(recs, *args, **kwargs)
File \"/usr/lib/python3/dist-packages/odoo/addons/stock/models/stock_quant.py\", line 413, in action_apply_inventory
  rounding = quant.product_uom_id.rounding
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 2895, in __get__
  return super().__get__(records, owner)
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 1209, in __get__
  self.compute_value(record)
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 1389, in compute_value
  records._compute_field_value(self)
File \"/usr/lib/python3/dist-packages/odoo/models.py\", line 4869, in _compute_field_value
  fields.determine(field.compute, self)
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 105, in determine
  return needle(records, *args)
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 695, in _compute_related
  values = [first(value[name]) for value in values]
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 695, in 
  values = [first(value[name]) for value in values]
File \"/usr/lib/python3/dist-packages/odoo/models.py\", line 6597, in __getitem__
  return self._fields[key].__get__(self, self.env.registry[self._name])
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 2895, in __get__
  return super().__get__(records, owner)
File \"/usr/lib/python3/dist-packages/odoo/fields.py\", line 1188, in __get__
  raise MissingError(\"\n\".join([
odoo.exceptions.MissingError: Record does not exist or has been deleted.
  (Record: stock.quant('14505',), User: 666)I get the exceptions from the JSON-RPC API so forgive me if it's badly formatted.
At the time of execution, the 14505 stock_quant entry does exist, so I don't understand: I guess it's marked as in a specific status, but the entry does exist for sure in the database... 
Yesterday I posted a similar question.  The $params data structure were wrong during the JSON-RPC POST query: the answer from one of the community members was very useful and unblocked the situation quickly. I am pretty sure I am doing something silly.
Other paths I followed
-  inventory_quantity_auto_apply : I did tests with that, but couldn't obtain any interesting results
- tried to use action_set_inventory_quantity
Thanks!
 
                        
What version of Odoo is the database?
This is Odoo v17.
17.0+e-20240418 (Enterprise Edition)
Hi Sébastien
Are you sure with that concept ? inventory_diff_quantity is for the purpose as :
/// <summary>
/// inventory_diff_quantity - float <br />
/// Required: False, Readonly: True, Store: True, Sortable: True <br />
/// Help: Indicates the gap between the product's theoretical quantity and its counted quantity. <br />
/// </summary>