External API

Read and write a workspace's data from another system. Every workspace exposes the same data through four transports, all at your workspace URL and all authenticated by a per-user API key. New integrations use the JSON API (POST /json/2/...); AI agents use the MCP server (POST /mcp), which is the one surface that adds write guardrails and an audit trail on top of your permissions. The classic XML-RPC and JSON-RPC endpoints stay supported for existing clients.

Which transport should I use?

Transport Path Auth Use it for
JSON API POST /json/2/<model>/<method> Bearer key Scripts and server-to-server integrations. Preferred.
MCP POST /mcp Bearer key AI agents. See AI agents & MCP.
XML-RPC /xmlrpc/2/common, /xmlrpc/2/object Login + key as password Legacy clients.
JSON-RPC POST /jsonrpc Login + key Legacy clients.

Guardrails live on /mcp only

The write denylist, confirmation gating, secret masking, and audit log apply to the MCP server. The JSON API and the classic RPC endpoints are bounded by the connected user's access rights and record rules, and nothing more. A key with wide permissions can write anything that role can write over /json/2. Point scripts at a scoped integration user, and point agents at /mcp.

Authenticate with an API key

Use a dedicated integration user, not a person's login. Sign in as that user and mint a key from its profile: Profile Settings → API & AI Agent → New API Key (on workspaces with agents enabled), or Profile Settings → Account Security → New API Key otherwise. The key is shown once. Every call is bounded by that user's access rights, so grant the integration user only the roles it needs.

  • Base URLhttps://<workspace>.everjust.app
  • Database — the workspace's name (its subdomain), passed on classic calls.

Call the JSON API

POST /json/2/<model>/<method> with the key as a bearer token and a JSON body. The body carries the method's keyword arguments; the response is the raw return value. Stateless, JSON in and out.

Read with search_read:

curl -X POST https://acme.everjust.app/json/2/crm.lead/search_read \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain": [["stage_id.name", "=", "New"]],
       "fields": ["name", "email_from"], "limit": 20}'

Create with vals_list (a list of records to create):

curl -X POST https://acme.everjust.app/json/2/crm.lead/create \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"vals_list": [{"name": "Globex", "email_from": "buyer@globex.com"}]}'

Two rules to remember:

  • Record methods take ids; model-level methods omit it. A method that operates on existing records (read, write, unlink, action_confirm) receives the target ids under "ids". A method that operates on the model itself (create, search, search_read, name_search) has no ids.
  • Only public methods are callable. Requests route through the platform's public-method gate, so private methods (underscore-prefixed, or marked private) return an access error. This is the same gate the MCP call tool uses.

Explore the API from your workspace

Your workspace serves an interactive schema browser at GET /doc — the models, fields, and methods reachable with your key, on this workspace's actual installed apps. It is the fastest way to confirm a model name or field before you write a call. What you see there is what your key can reach.

Classic XML-RPC / JSON-RPC

Still supported for existing clients and every standard library that speaks the protocol. New integrations use the JSON API above.

import xmlrpc.client

url = "https://acme.everjust.app"
db  = "acme"                 # the workspace subdomain
key = "YOUR_API_KEY"         # from the integration user's profile

common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, "integration@acme.com", key, {})

models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")
ids = models.execute_kw(db, uid, key,
    "crm.lead", "search", [[["stage_id.name", "=", "New"]]])
records = models.execute_kw(db, uid, key,
    "crm.lead", "read", [ids], {"fields": ["name", "email_from"]})
new_id = models.execute_kw(db, uid, key,
    "crm.lead", "create", [{"name": "Globex"}])

Practical notes

  • One database per workspace. The db is the workspace's database name (its subdomain). A key only ever reaches its own workspace.
  • Scope the user. The key inherits its user's permissions. Give a script a user with the roles it needs and no more.
  • Rate and batch. Prefer search_read over search + read, and batch writes into one call.
  • AI agents & MCP — the guarded, audited surface over this same API, for Claude Code and Codex.
  • Integration & agent users — provision the scoped account your key should belong to.
  • The methods you call are standard model methods — search_read, read, create, write, unlink, and any public method a model exposes.

Last reviewed: 2026-07-21

Need a hand with this? company@everjust.co — a human answers.