If something talks to Odoo from the outside, such as a webshop sync, a warehouse tool, a BI export or a .NET line-of-business application, there is a good chance it uses XML-RPC or JSON-RPC. Odoo 19 has put an end date on both. This article sums up what the official documentation says, what changes with the replacement JSON-2 API, where the .NET client libraries stand, and the checklist we use to plan a migration.
What Odoo has announced
The External RPC API page of the Odoo 19 documentation now opens with a deprecation notice: the XML-RPC and JSON-RPC APIs at /xmlrpc, /xmlrpc/2 and /jsonrpc are "scheduled for removal in Odoo 22 (fall 2028) and Online 21.1 (winter 2027)". The replacement is the External JSON-2 API, which is new in version 19.0.
- Odoo Online: the endpoints go away with version 21.1, which the documentation places in winter 2027. This is the earlier of the two dates.
- Odoo 22: planned for fall 2028. Any database running Odoo 22 or later will no longer have these endpoints.
Two details on the same pages are easy to miss:
- The deprecation covers the three RPC services (
common,dbandobject). Other controllers declared with@route(type='jsonrpc')are explicitly "not subject to this deprecation notice". - Access to data through the external API "is only available on Custom Odoo pricing plans"; it is not available on the One App Free or Standard plans. On Odoo Online, check the plan before planning any integration work.
What changes with JSON-2
JSON-2 is plain HTTP: you POST a JSON object to /json/2/<model>/<method>. For an integration, these are the differences that matter:
- Authentication. No more user ID and password on every call. Each request carries an API key in an
Authorization: bearerheader, and the user is derived from the key. If you still need the user's own ID, the documentation suggests callingres.users/context_get. - Named arguments only.
execute_kwtook a positionalargslist. In JSON-2 "all the arguments are named; there is no way in JSON-2 to call a function with positional arguments". Record IDs go inids; everything else, such asdomain,fieldsorlimit, is a named property. - Database selection. The
X-Odoo-Databaseheader is only required when one server hosts several databases and the dbfilter does not use the Host header. - One transaction per call. Every JSON-2 call runs in its own SQL transaction, and calls cannot be chained into one transaction. A flow that creates an order, adds lines and confirms it in separate calls can leave half-finished data if one step fails. The documentation recommends calling a single method that does the related work, such as
action_confirmonsale.order, or adding such a method in a small custom module. - Errors are HTTP errors. Success is a 200 with the method's return value as JSON. A failure is a 4xx or 5xx status with a JSON body that includes
name,messageandarguments.
API keys now expire within three months
This is the operational change most existing integrations are not ready for. According to the JSON-2 documentation, every key needs a description and a duration, and "it is not possible to create keys that last for more than three months. This means that long lasting keys must be rotated at least once every three months." A key pasted into a configuration file today will stop working, and the integration will start receiving 401 responses.
Odoo 19 also documents programmatic key management. res.users.apikeys/generate creates a key with a name, a scope and an expiration date, and res.users.apikeys/revoke revokes one. By default only users with the Settings administration right can call these, unless the system parameter base.enable_programmatic_api_keys is set to True, and a user can generate up to 10 keys this way unless base.programmatic_api_keys_limit is changed. The recommended order is: generate the new key, store it securely, switch every service over, verify, and only then revoke the old key.
The same page recommends a dedicated bot user for each integration, with the minimum access rights it needs and an empty password, so the account cannot be used to log in interactively.
Where .NET clients stand
At the time of writing (October 2026), the most-downloaded Odoo client on NuGet is PortaCapena.OdooJsonRpcClient, with about 248,000 downloads. It is a JSON-RPC client, and its latest NuGet release, 1.0.20, dates from April 2023. A few smaller packages now mention JSON-2 in their descriptions, but they are young and have few users, so read their source and maintenance history before you depend on one.
You may not need a library at all. A JSON-2 call is a POST with a bearer token, which HttpClient handles in a few lines:
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
// Read the key from your secret store; never from a committed config file.
var apiKey = Environment.GetEnvironmentVariable("ODOO_API_KEY")
?? throw new InvalidOperationException("ODOO_API_KEY is not set");
using var http = new HttpClient { BaseAddress = new Uri("https://mycompany.example.com/json/2/") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", apiKey);
http.DefaultRequestHeaders.UserAgent.ParseAdd("my-integration/1.0");
// Only needed when one host serves several databases:
// http.DefaultRequestHeaders.Add("X-Odoo-Database", "mycompany");
var response = await http.PostAsJsonAsync("res.partner/search_read", new
{
domain = new object[] { new object[] { "is_company", "=", true } },
fields = new[] { "name", "email" },
limit = 50,
context = new { lang = "en_US" }
});
if (!response.IsSuccessStatusCode)
{
// Odoo returns a JSON error object with "name", "message", "arguments", ...
var error = await response.Content.ReadFromJsonAsync<JsonElement>();
throw new HttpRequestException(
$"Odoo returned {(int)response.StatusCode}: {error.GetProperty("message").GetString()}");
}
var partners = await response.Content.ReadFromJsonAsync<JsonElement>();
Over XML-RPC, the same query was execute_kw(db, uid, password, "res.partner", "search_read", [domain], {"fields": [...], "limit": 50}). In JSON-2 the domain moves out of the positional list into a named domain property, and the database, user ID and password disappear from the call. In a real application, register the client through IHttpClientFactory and add retry and logging around it.
Migration checklist
- Inventory every caller. Search code and configuration for
/xmlrpc,/xmlrpc/2,/jsonrpc,execute_kwandauthenticate, and for Odoo client packages. Include scheduled scripts, BI connectors and third-party tools, not only your own repositories. - Confirm version, plan and hosting. JSON-2 exists from Odoo 19. A database on an older version has to be upgraded before it can use JSON-2, and on Odoo Online external API access needs a Custom plan.
- Translate each call. For every model and method you use, write down the JSON-2 request: IDs in
ids, all other parameters named. This is also a good moment to drop calls nobody needs any more. - Find multi-step workflows. Anything that relies on several calls succeeding together should become one server-side method: an existing
action_*method, or a small custom module. - Create one bot user per integration, with minimal access rights and no password login.
- Build key rotation in from day one. Keep keys in a secret store, rotate them before they expire (generate, deploy, verify, revoke), and alert on 401 responses so an expired key is noticed within minutes rather than days.
- Handle errors as HTTP. Map 4xx and 5xx responses explicitly and log Odoo's
nameandmessagefields. - Test against a copy. Run the old and the new path against a duplicated, neutralised database and compare the results before switching production.
- Check for web-client routes. Some integrations post to routes that Odoo's own web client uses, such as
/web/dataset/call_kw. These are not part of the documented external API. The deprecation notice does not cover them, but depending on undocumented routes is a risk in its own right, and moving to JSON-2 removes it.
When to start
The dates look far away, but integrations are usually the last thing anyone remembers during an upgrade project. On Odoo Online, the cut-off is the 21.1 release in winter 2027. On other hosting, it is the move to Odoo 22, planned for fall 2028. If you are already on Odoo 19, building new integrations on JSON-2 today avoids writing code that will need rewriting before it has paid for itself.
Common questions
Does XML-RPC still work on Odoo 19?
Yes. In Odoo 19 the endpoints are deprecated, not removed. The documentation schedules their removal for Odoo 22 and for Odoo Online 21.1.
Are my own JSON controllers affected?
Not by this notice. The documentation states that other controllers declared with type='jsonrpc' (called type='json' until Odoo 18) are not subject to the deprecation. Only /xmlrpc, /xmlrpc/2 and /jsonrpc are scheduled for removal.
Can one API key be used forever?
No. Keys created in the interface cannot last longer than three months, so any long-running integration needs a rotation routine.
Rui Codex helps teams with Odoo integrations, including the move to JSON-2: an inventory of existing RPC calls, the JSON-2 mapping, a key-rotation routine and the migration itself, done in your own repository. See our Odoo services or contact us.
Sources: Odoo 19, External RPC API; Odoo 19, External JSON-2 API; NuGet, PortaCapena.OdooJsonRpcClient. Checked on 22 October 2026.