Court Creation API
This document describes the API endpoint for programmatically creating new courts.
Overview
The Court Creation API allows authenticated users with write permissions to create new courts. The API handles:
Automatic state resolution: States are identified from their name, not requiring foreign key IDs
Automatic city resolution: Cities are resolved from name within a state, or created if not found
Duplicate prevention: Courts with the same code are rejected
API token tracking: The token used for creation is recorded for audit purposes
Review workflow: API-submitted courts are set to
pendingreview status
Endpoint
POST /api/courts/
Authentication
Requires a valid API token with courts:write permission.
Authorization: Token YOUR_API_TOKEN
Request Format
Headers
Content-Type: application/json
Authorization: Token YOUR_API_TOKEN
Body
Field |
Type |
Required |
Description |
|---|---|---|---|
|
string (max 200) |
Yes |
Full name of the court with location |
|
string (max 20) |
Yes |
Unique court identifier based on ECLI (e.g., “BVerfG”) |
|
string (max 50) |
Yes |
State name for automatic resolution |
|
string (max 10) |
No |
Court type code (e.g., “AG”, “LG”, “OLG”) |
|
string (max 100) |
No |
City name for automatic resolution |
|
string (max 100) |
No |
Jurisdiction of court (ordinary, civil, …) |
|
string (max 100) |
No |
Level of appeal (local, federal, …) |
|
string |
No |
List of aliases (one per line) |
|
string |
No |
Court description |
|
URL |
No |
Official court homepage |
|
string (max 200) |
No |
Street address with house number |
|
string (max 200) |
No |
Postal code (ZIP code) |
|
string (max 200) |
No |
Locality (city name) |
|
string (max 200) |
No |
Telephone number |
|
string (max 200) |
No |
Fax number |
|
No |
Email address |
Example Request
curl -X POST "https://de.openlegaldata.io/api/courts/" \
-H "Authorization: Token YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Amtsgericht Berlin-Mitte",
"code": "AGBERLINMITTE",
"state_name": "Berlin",
"court_type": "AG",
"city_name": "Berlin",
"jurisdiction": "ordinary",
"homepage": "https://www.berlin.de/gerichte/amtsgericht-mitte/"
}'
Response Format
Success Response (201 Created)
{
"id": 42,
"slug": "ag-berlin",
"review_status": "pending"
}
Field |
Type |
Description |
|---|---|---|
|
integer |
Unique court ID |
|
string |
URL-friendly identifier (auto-generated from court type and city) |
|
string |
Review status ( |
Error Responses
400 Bad Request - Validation Error
{
"name": ["This field is required."],
"code": ["Court code cannot be empty."]
}
400 Bad Request - State Not Found
{
"detail": "Could not resolve state from the provided name: 'InvalidState'."
}
403 Forbidden
{
"detail": "You do not have permission to perform this action."
}
409 Conflict - Duplicate Court
{
"detail": "A court with code 'BVerfG' already exists."
}
State and City Name Resolution
State Resolution
The API resolves the state from the provided state_name:
Exact match: Checks for an exact name match
Case-insensitive match: Falls back to case-insensitive matching
If no state is found, a 400 Bad Request error is returned.
City Resolution
The API resolves the city from the provided city_name within the resolved state:
Exact match: Checks for an exact name match within the state
Case-insensitive match: Falls back to case-insensitive matching
Auto-creation: If no city is found, a new city is created within the state
City is optional — state-level courts (e.g., Bundesgerichtshof) do not require a city.
Duplicate Prevention
Duplicates are detected based on the court code. If a court with the same code already exists, a 409 Conflict error is returned.
Review Workflow
Courts created via the API use a three-state review workflow:
Submission: Courts are submitted via the API with
review_status="pending"Review: Administrators review pending courts in the Django admin
Decision: Admins set
review_statusto either"accepted"or"rejected"
Note: Existing courts (not created via API) default to review_status="accepted".
Admin Review Process
Administrators can manage pending courts via the Django admin:
Navigate to Courts > Courts in the admin
Filter by Review status: Pending to see pending submissions
Filter by API submission: Created via API to see API-submitted courts
Review court data and metadata
Change Review status to “Accepted” or “Rejected” and save
API Token Tracking
The API token used for court creation is recorded on the court for audit purposes. This allows:
Tracking which application/user created each court
Identifying courts created via API vs. other methods
Revoking access and identifying affected courts
Querying API Submissions
To view all courts created by a specific API token:
from oldp.apps.courts.models import Court
from oldp.apps.accounts.models import APIToken
token = APIToken.objects.get(name="Scraper Token")
pending_courts = Court.objects.filter(created_by_token=token, review_status="pending")
Examples
Python Example
import requests
API_TOKEN = "your_api_token_here"
BASE_URL = "https://de.openlegaldata.io/api"
headers = {
"Authorization": f"Token {API_TOKEN}",
"Content-Type": "application/json",
}
court_data = {
"name": "Amtsgericht Berlin-Mitte",
"code": "AGBERLINMITTE",
"state_name": "Berlin",
"court_type": "AG",
"city_name": "Berlin",
"jurisdiction": "ordinary",
}
response = requests.post(f"{BASE_URL}/courts/", json=court_data, headers=headers)
if response.status_code == 201:
result = response.json()
print(f"Court created: ID={result['id']}, Slug={result['slug']}, Status={result['review_status']}")
elif response.status_code == 409:
print("Error: Court already exists")
elif response.status_code == 400:
print(f"Validation error: {response.json()}")
else:
print(f"Error: {response.status_code} - {response.text}")
Batch Import Example
import requests
API_TOKEN = "your_api_token_here"
BASE_URL = "https://de.openlegaldata.io/api"
headers = {
"Authorization": f"Token {API_TOKEN}",
"Content-Type": "application/json",
}
courts_to_import = [
{
"name": "Amtsgericht Köln",
"code": "AGKOELN",
"state_name": "Nordrhein-Westfalen",
"court_type": "AG",
"city_name": "Köln",
},
{
"name": "Landgericht Köln",
"code": "LGKOELN",
"state_name": "Nordrhein-Westfalen",
"court_type": "LG",
"city_name": "Köln",
},
]
results = {"created": 0, "duplicates": 0, "errors": 0}
for court_data in courts_to_import:
response = requests.post(f"{BASE_URL}/courts/", json=court_data, headers=headers)
if response.status_code == 201:
results["created"] += 1
elif response.status_code == 409:
results["duplicates"] += 1
else:
results["errors"] += 1
print(f"Error importing {court_data['code']}: {response.text}")
print(f"Import complete: {results}")
Best Practices
Validate state names: Use the states API to verify state names before bulk imports
Handle duplicates gracefully: 409 responses indicate the court already exists
Provide court type: Including
court_typehelps with slug generation and filteringExpect review delays: All API submissions require manual approval before being accepted
Batch with care: Implement rate limiting and error handling for bulk imports