External API using JSON-RPC
Contents
External API using JSON-RPC#
This guide explains how to connect to and interact with OpenSPP Registry using the JSON-RPC API, with practical Python examples for adding individuals, groups, and memberships.
Prerequisites#
OpenSPP server running and accessible.
User credentials or API key with appropriate permissions.
Python 3.x and the
requestslibrary installed (pip install requests).
Process#
Understanding JSON-RPC in OpenSPP#
OpenSPP exposes much of its data and functionality via JSON-RPC endpoints. You can use these endpoints to authenticate, read, create, update, and delete records from external applications.
Endpoint:
/jsonrpc— All JSON-RPC calls (authentication and model methods)
JSON-RPC payload structure and methods#
All interactions with the OpenSPP JSON-RPC API use a standard payload structure. Each request is a JSON object with the following keys:
jsonrpc: The JSON-RPC protocol version (always"2.0").method: The JSON-RPC method (always"call"for OpenSPP).params: The parameters for the call, including:service: The Odoo service to use ("common"for authentication,"object"for model methods).method: The method to call on the service (e.g.,"authenticate","execute_kw").args: A list of arguments for the method.
id: A unique identifier for the request (integer or string).
Example payload:
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"res.partner", "search_read",
[[["is_group", "=", False]]], # Domain filter
{"fields": ["name", "reg_id"], "limit": 5} # Options
]
},
"id": 10,
}
Common JSON-RPC methods:
authenticate: Used for logging in and obtaining a user ID.execute_kw: Used for calling model methods (such ascreate,write,unlink,search_read).
Common model methods and args#
When using the "object" service with the "execute_kw" method, you can call various model methods. Here are the most common:
create
Creates a new record.
Args:
Model name (e.g.,
"res.partner")Method name (
"create")List of dictionaries with field values
Example:
["res.partner", "create", [{"name": "Jane Doe", "reg_id": "IND654321"}]]
Result: Returns the ID of the newly created record.
write
Updates existing records.
Args:
Model name
Method name (
"write")List of record IDs to update
Dictionary of fields to update
Example:
["res.partner", "write", [[individual_id], {"birthdate": "1991-02-02"}]]
Result: Returns a dictionary indicating the result of the update.
unlink
Deletes records.
Args:
Model name
Method name (
"unlink")List of record IDs to delete
Example:
["res.partner", "unlink", [individual_id]]
Result: Returns a dictionary indicating the result of the deletion.
search_read
Searches for records and reads their fields.
Args:
Model name
Method name (
"search_read")Domain filter (list of conditions)
Options dictionary (fields, limit, offset, etc.)
Example:
["res.partner", "search_read", [[["is_group", "=", False]]], {"fields": ["name", "reg_id"], "limit": 5}]
Domain filters:
A domain is a list of conditions, each as a list: [field, operator, value].
Example: [["name", "=", "John Doe"]]
Options:
fields: List of fields to return.limit: Maximum number of records.offset: Skip the first N records.
Result Returns a dictionary of the search results.
Authentication#
You must authenticate before accessing most data. Use your password or an API key (recommended).
import requests
url = "http://localhost:8069/jsonrpc"
db = "my_database"
username = "admin"
password = "your_password_or_api_key"
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "common",
"method": "authenticate",
"args": [db, username, password, {}]
},
"id": 1,
}
response = requests.post(url, json=payload).json()
uid = response.get("result")
if not uid:
raise Exception("Authentication failed")
print("Authenticated UID:", uid)
Working with individuals#
Gather the fields
For this example we are going to use these fields:
name(string, required)given_name(string, required)family_name(string, required)gender(many2one, required)birthdate(date, required)is_registrant(boolean, default=True)is_group(boolean, default=False)
Example: Create an individual
# First, get a gender_id (e.g., for "Male")
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"gender.type", "search_read",
[[["code", "=", "Male"]]],
{"fields": ["id"], "limit": 1}
]
},
"id": 2,
}
response = requests.post(url, json=payload).json()
gender_id = response["result"][0]["id"]
# Now, create the individual
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"res.partner", "create",
[{
"name": "John Doe",
"given_name": "John",
"family_name": "Doe",
"gender": gender_id,
"birthdate": "1990-01-01",
"is_registrant": True,
"is_group": False,
}]
]
},
"id": 3,
}
response = requests.post(url, json=payload).json()
individual_id = response["result"]
print("Created Individual ID:", individual_id)
# Update the individual's birthdate
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"res.partner", "write",
[[individual_id], # List of IDs to update
{"birthdate": "1991-02-02"}] # Fields to update
]
},
"id": 8,
}
response = requests.post(url, json=payload).json()
updated = response["result"]
print("Result:", updated)
Working with groups#
Gather the fields
For this example we are going to use these fields:
name(string, required)kind(many2one, required)is_registrant(boolean, default=True)is_group(boolean, default=True)
Example: Create a group
# Get a group kind (e.g., "Household")
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"g2p.group.kind", "search_read",
[[["name", "=", "Household"]]],
{"fields": ["id"], "limit": 1}
]
},
"id": 4,
}
response = requests.post(url, json=payload).json()
kind_id = response["result"][0]["id"]
# Create the group
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"res.partner", "create",
[{
"name": "Doe Family",
"kind": kind_id
"is_registrant": True,
"is_group": True,
}]
]
},
"id": 5,
}
response = requests.post(url, json=payload).json()
group_id = response["result"]
print("Created Group ID:", group_id)
# Update the group's name
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"res.partner", "write",
[[group_id], # List of IDs to update
{"name": "Doe Family Updated"}] # Fields to update
]
},
"id": 10,
}
response = requests.post(url, json=payload).json()
updated = response["result"]
print("Result:", updated)
Working with memberships#
Gather the fields
From your g2p_registry_membership module, the main model is likely g2p_registry_membership.group_membership. Required fields typically include:
individuak(many2one, required)group(many2one, required)kind(many2many, required; e.g., "Head", "Member")start_date(date, required)Any other required fields as defined in your model
Example: Add an individual to a group
# Get a membership kind (e.g., "Head")
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"g2p.group.membership.kind", "search_read",
[[["name", "=", "Head"]]],
{"fields": ["id"], "limit": 1}
]
},
"id": 6,
}
response = requests.post(url, json=payload).json()
kind_id = response["result"][0]["id"]
# Create the membership
payload = {
"jsonrpc": "2.0",
"method": "call",
"params": {
"service": "object",
"method": "execute_kw",
"args": [
db, uid, password,
"g2p.group.membership", "create",
[{
"individual": individual_id,
"group": group_id,
"kind": [[4, kind_id]],
"start_date": "2025-01-01"
}]
]
},
"id": 7,
}
response = requests.post(url, json=payload).json()
membership_id = response["result"]
print("Created Membership ID:", membership_id)
Security: Using API keys#
API keys are recommended over passwords for scripts and integrations.
Generate API keys in your OpenSPP user preferences under Account Security.
Use the API key in place of your password in all JSON-RPC calls.
Best practices#
Use API Keys: Safer than passwords; revoke if compromised.
Limit Permissions: Create dedicated users for API access with only necessary rights.
Validate Responses: Always check for errors or unexpected results.
Secure Connections: Use HTTPS for all API traffic.
Log and Monitor: Track API usage for auditing and troubleshooting.
openspp.org