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 pending review 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

name

string (max 200)

Yes

Full name of the court with location

code

string (max 20)

Yes

Unique court identifier based on ECLI (e.g., “BVerfG”)

state_name

string (max 50)

Yes

State name for automatic resolution

court_type

string (max 10)

No

Court type code (e.g., “AG”, “LG”, “OLG”)

city_name

string (max 100)

No

City name for automatic resolution

jurisdiction

string (max 100)

No

Jurisdiction of court (ordinary, civil, …)

level_of_appeal

string (max 100)

No

Level of appeal (local, federal, …)

aliases

string

No

List of aliases (one per line)

description

string

No

Court description

homepage

URL

No

Official court homepage

street_address

string (max 200)

No

Street address with house number

postal_code

string (max 200)

No

Postal code (ZIP code)

address_locality

string (max 200)

No

Locality (city name)

telephone

string (max 200)

No

Telephone number

fax_number

string (max 200)

No

Fax number

email

email

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

id

integer

Unique court ID

slug

string

URL-friendly identifier (auto-generated from court type and city)

review_status

string

Review status (pending for API submissions)

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'."
}

401 Unauthorized

{
  "detail": "Authentication credentials were not provided."
}

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:

  1. Exact match: Checks for an exact name match

  2. 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:

  1. Exact match: Checks for an exact name match within the state

  2. Case-insensitive match: Falls back to case-insensitive matching

  3. 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:

  1. Submission: Courts are submitted via the API with review_status="pending"

  2. Review: Administrators review pending courts in the Django admin

  3. Decision: Admins set review_status to 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:

  1. Navigate to Courts > Courts in the admin

  2. Filter by Review status: Pending to see pending submissions

  3. Filter by API submission: Created via API to see API-submitted courts

  4. Review court data and metadata

  5. 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

  1. Validate state names: Use the states API to verify state names before bulk imports

  2. Handle duplicates gracefully: 409 responses indicate the court already exists

  3. Provide court type: Including court_type helps with slug generation and filtering

  4. Expect review delays: All API submissions require manual approval before being accepted

  5. Batch with care: Implement rate limiting and error handling for bulk imports