MADATA / DEV developer docs Français madata.africa

Data API

Your data, from your code

Every <workspace>.madata.app workspace exposes its data API: everything you see on screen can be read and written from a program, with your credentials and your permissions.

One engine, two front doors. The MCP server talks to AI assistants in ready made business tools. The data API talks to your code: it exposes the raw models of the workspace, their fields and their methods. A customer, a quotation, a journal entry, a stock move: all of it readable and writable.

01Which one to pick

You want toUse
Connect Claude, ChatGPT, Gemini or Mistral to the workspaceMCP server: about 150 business tools, no knowledge of the data model needed.
Sync an online shop, a till, a directoryData API: full control, field by field.
Build a custom export or feed a dashboardData API: search_read and read_group do the work.
Let an assistant act without writing codeConnect an assistant.

Both share the same API key and the same permissions. Nothing stops you from using them together.

02Address & database

Two pieces of information are enough to call a workspace.

Address
https://<workspace>.madata.app, the one you log into every day.
Database
prod_<workspace>. For kouassi.madata.app, the database is prod_kouassi.
User
A numeric uid, returned when you log in. It is not your email address: it is its internal identifier.
Password
An API key. Never your login password.
One workspace, one database

Each workspace has its own database, isolated from the others. A key only opens the workspace that issued it. There is nothing to "select": the database name follows from the subdomain, and a database that is not yours stays invisible.

03Three protocols, one engine

Entry pointProtocolFor whom
/xmlrpc/2/common
/xmlrpc/2/object
XML-RPCThe most widespread. Python, PHP, Ruby and Java ship an XML-RPC client in their standard library.
/jsonrpcJSON-RPC 2.0JavaScript, Node, Go, and anything that prefers JSON. Same services, same semantics.
/web/dataset/call_kwJSON over sessionThe browser channel. Useful to replay exactly what the interface does. Requires a session cookie.

All three hit the same engine and honour the same permissions. XML-RPC and JSON-RPC are stateless: every call carries its credentials, there is no session to maintain.

04The first call

Three steps: check the version, log in, count records.

pythonpython 3, standard library only
import xmlrpc.client

URL = "https://<workspace>.madata.app"
DB  = "prod_<workspace>"
KEY = "your-api-key"
LOGIN = "[email protected]"

common = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/common")
print(common.version())
# {'server_version': '17.0', 'server_serie': '17.0', 'protocol_version': 1}

uid = common.authenticate(DB, LOGIN, KEY, {})
print("uid =", uid)          # 7  (False if the key is refused)

models = xmlrpc.client.ServerProxy(f"{URL}/xmlrpc/2/object")
count = models.execute_kw(DB, uid, KEY, 'res.partner', 'search_count', [[]])
print(count, "contacts")
bashthe same thing in JSON-RPC, no library
curl -s https://<workspace>.madata.app/jsonrpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"call","params":{
        "service":"object","method":"execute_kw",
        "args":["prod_<workspace>", 7, "your-api-key",
                "res.partner","search_count",[[]]]}}'

# {"jsonrpc": "2.0", "id": null, "result": 143}
Keep the uid

authenticate costs a round trip and a credential check. Call it once when your program starts, keep the uid in memory, and only go back to it when a call fails with AccessDenied.

05The shape of a call

Everything goes through execute_kw, on the object service. Seven arguments, always the same ones.

pythonexecute_kw, argument by argument
models.execute_kw(
    db,          # database name of the workspace
    uid,         # numeric user id
    key,         # API key
    'res.partner',   # model
    'search_read',   # method
    [[['customer_rank', '>', 0]]],          # positional arguments
    {'fields': ['name', 'email'], 'limit': 10},  # keyword arguments
)

The last two are the key to reading any example: a list of positional arguments, then a dictionary of keyword arguments. A method with no keyword argument gets {}, or nothing at all.

06Type conventions

Field typeWhat you receiveWhat you send
char, text, htmlA string. An empty field is false, not "".A string.
integer, float, monetaryA number. A monetary is a float: the currency lives in currency_id.A number.
booleantrue / false.Same.
date"2026-09-17".Same shape.
datetime"2026-09-17 14:32:08", always UTC, with no timezone suffix.In UTC. Converting to local time is up to you.
selectionThe technical value ("posted"), not its label.The technical value.
many2oneA pair [id, "label"], or false.The id alone.
one2many, many2manyA list of id.Relational commands.
binaryBase64 encoded content.Base64.
false is everywhere

An unset field comes back as false whatever its type: empty string, missing date, empty many2one. This is the most common early trap. if contact["email"]: is the right reflex; contact["email"].lower() crashes on a contact with no email.

07What stays closed

  • Database management (/web/database/*: create, duplicate, restore, list) answers 403 from the outside. No key opens it.
  • The server database list is not published: db.list answers "access denied". You already know yours, and it is the only one that concerns you.
  • Users, companies, subscription, applications are billed resources, driven by the portal (madata.africa/portail). Creating them through the API puts the workspace and the billing out of step. The MCP server refuses them outright.

08Limits & good manners

Call duration
Around 300 seconds. A call that runs longer is cut off: split the work instead of insisting.
Volume
No row cap, but a search_read without limit on a large model brings back everything. Paginate.
Rate
No published quota on the data API. A tight loop that saturates the workspace penalises your own users first: batch your calls.
Batch
create, write and read accept lists. A hundred contacts in one call beats a hundred calls.
Maintenance
While the workspace is being updated, the API answers 503 with a Retry-After header. Honour it and resume: nothing is lost.

Authenticate

Create a key, get a uid, understand what two factor authentication changes.

Authentication ›

The data model

Models, fields, external identifiers, multi company, and how to discover them instead of guessing.

Models & fields ›

Read

search_read, domains, grouping, pagination, context.

Reading data ›

Write

Create, update, relate, and trigger business actions: a confirmed order, a posted invoice.

Writing data ›