> For the complete documentation index, see [llms.txt](https://peoplescourt.ai/llms.txt). This page is also available as Markdown.

# Python SDK

Use the `peoplescourt` Python client to prepare filings, manage account authorization, submit evidence, and follow case events.
It supports synchronous and asynchronous applications on Python 3.10 or later.
For a complete platform flow, see [Integrate a marketplace dispute](https://peoplescourt.ai/docs/api/workflows/marketplace-dispute.md).

## Install the prepared beta

The MIT-licensed Python SDK beta `0.1.0b1` is prepared for publication and is not yet on PyPI.
Contact [contact@peoplescourt.ai](mailto:contact@peoplescourt.ai) to request the prepared distribution or source access.
With the release wheel, install it in your application’s virtual environment:

```sh
python -m pip install ./peoplescourt-0.1.0b1-py3-none-any.whl
# Or, from a source checkout:
python -m pip install -e ./sdk/python
# Include Ed25519 callback verification when your integration needs it:
python -m pip install -e './sdk/python[webhooks]'
```

After publication, the pinned PyPI installation command is `python -m pip install 'peoplescourt==0.1.0b1'`.
The source package version is `0.1.0b1`.
Record the source revision alongside that version when deploying your integration.
MIT applies to the Python client distribution; it does not license undistributed server code or grant hosted API access.
Platform access remains subject to approval and separately issued live credentials.
You can use the public [HTTP quickstart](https://peoplescourt.ai/docs/api/get-started.md) without the SDK or a source checkout.
JavaScript and TypeScript integrations can use the fetch examples in the [endpoint reference](https://peoplescourt.ai/docs/api/reference.md).

## Connect to the API

Obtain credentials through [Get access](https://peoplescourt.ai/docs/api/access.md).
An account OAuth token acts only for its approving account and scopes.
A registered agent’s client-credentials token acts for that agent account.
Platforms acting for many users need approved, role-bound partner credentials and each principal’s authority.

Set `PEOPLESCOURT_BASE_URL` to your deployment’s origin, without `/api`, and keep `PEOPLESCOURT_TOKEN` in your backend secret store.
Use a non-production deployment with its matching credentials for evaluation.
The client’s default origin is `https://peoplescourt.ai`; confirming a filing there can open a real proceeding.

```python
import os
from peoplescourt import Client

with Client(
    os.environ["PEOPLESCOURT_TOKEN"],
    base_url=os.environ["PEOPLESCOURT_BASE_URL"],
    timeout=30.0,
) as court:
    rules = court.current_rules()
    case = court.get_case(os.environ["PEOPLESCOURT_CASE_ID"])
    print(case["stage"], case["revision"])
```

Most results unwrap the API’s `data` object and retain its camelCase field names.
Filing, invitation and pagination methods return typed objects instead.
`Client` closes its connections when the `with` block ends.

## Prepare and confirm a filing

The account OAuth example below uses the request body from [Get started](https://peoplescourt.ai/docs/api/get-started.md#3-prepare-the-filing), saved as `filing.json` after the claimant reviews its contents.
The required scopes include `filings:write`, `consents:write` and `cases:read`.
Partner filings use a different request contract with registered authority and confirmation proofs; follow the [marketplace walkthrough](https://peoplescourt.ai/docs/api/workflows/marketplace-dispute.md).

```python
import json
import os
from peoplescourt import AcceptanceRequired, Client, Confirmation, FilingFeeRequired

with Client(
    os.environ["PEOPLESCOURT_TOKEN"],
    base_url=os.environ["PEOPLESCOURT_BASE_URL"],
) as court:
    with open("filing.json", encoding="utf-8") as source:
        filing = json.load(source)
    draft = court.prepare_filing(filing, idempotency_key="order-1042-prepare")

    # Deliver draft.hosted_acceptance_url privately to the claimant.
    # Continue only after the claimant reviews and accepts on People’s Court.
    input("Continue after the claimant has accepted the exact review packet: ")
    result = court.confirm_filing(draft.filing_id, idempotency_key="order-1042-confirm")
    if isinstance(result, Confirmation) and result.case_id:
        case = court.get_case(result.case_id)
    elif isinstance(result, AcceptanceRequired):
        # Deliver the returned link privately when present; do not log it.
        acceptance_url = result.hosted_acceptance_url
    elif isinstance(result, FilingFeeRequired):
        # Hand off the returned payment link or details["filingPath"] to the owner.
        payment_url = result.filing_payment_url
```

Preparing a draft does not open a case.
The principal must accept the exact claim, Rules, resolved procedure, disclosures and fees.
Choose AI-only or AI-assisted human in that packet; the Court resolves the AI-only procedure before acceptance.
A human appeal remains available unless both parties separately accept the exact waiver.
The SDK cannot perform personal hosted acceptance or infer consent from a credential.

`confirm_filing` returns `Confirmation`, `AcceptanceRequired` or `FilingFeeRequired`.
The two prerequisite results are values rather than raised exceptions; neither opens a case.
Check `Confirmation.case_id` before continuing.
Show-once acceptance and invitation links are omitted from ordinary replays; preserve them through the intended recipient’s authenticated channel.

## Submit a party’s evidence

Read `get_case` before writing and follow its `availableActions`, requirements and effective deadlines.
After the party has affirmed truthfulness, use `submit_brief` with that party’s credential and actual declaration:

```python
import os
from peoplescourt import Client

with Client(
    os.environ["PEOPLESCOURT_TOKEN"],
    base_url=os.environ["PEOPLESCOURT_BASE_URL"],
) as court:
    with open("receipt.txt", "rb") as receipt:
        attachment = receipt.read()
    submission = court.submit_brief(
        os.environ["PEOPLESCOURT_CASE_ID"],
        party="claimant",
        brief="Order #1042 was paid for and never arrived.",
        attest=True,
        declaration={
            "declarantName": "Maya Chen",
            "declarantCapacity": "Individual party",
            "executionLocation": "Brussels, Belgium",
        },
        files=[("receipt.txt", attachment, "text/plain")],
        idempotency_key="order-1042-claimant-evidence",
    )
```

Replace the example declaration with the actual declarant and location.
Files are `(filename, bytes, media_type)` tuples held in memory.
The client sends multipart data and `x-pc-party`, including when there are no attachments.
It does not grant access to an opponent’s sealed filing.

## Follow cases and events

| Method | Result and use |
|---|---|
| `get_case(case_id)` | Current authorized case view, including `stage`, `revision`, `availableActions` and `waitingOn` |
| `list_cases()` / `iter_cases()` | Account-only case listing; `CasePage.cases` and `.next_url`, or iteration across the Link-header pages |
| `events(case_id, after=cursor)` | `EventPage.events`, `.next_cursor` and `.has_more`; persist the cursor with successfully processed work |
| `iter_events(case_id, after=cursor)` | Drains currently available pages and stops; schedule subsequent reads yourself |
| `create_invitation(case_id, idempotency_key=key)` | `Invitation.hosted_link`; creating a new invitation rotates the old one |
| `get_answer_requirements(case_id)` | The invited respondent’s current Complaint, claim IDs and Answer requirements |
| `prepare_answer(case_id, body)` / `confirm_answer(answer_id)` | Prepare an account Answer for hosted acceptance, then file the already accepted packet |

Case-list and case-event cursors are different and cannot be interchanged.
The [marketplace walkthrough](https://peoplescourt.ai/docs/api/workflows/marketplace-dispute.md) shows durable polling checkpoints and signed callback handling.

## Use account OAuth

Register the client and exact redirect URI at [Account authorizations](https://peoplescourt.ai/account/authorizations).
`authorization_url(client_id, redirect_uri, scopes)` builds an S256 PKCE request and retains its verifier and state.
Keep that authorization object server-side through the callback, then call `login.callback_code(callback_url)` to validate the destination, state and issuer.
Pass the returned code, `login.code_verifier` and `login.redirect_uri` to `court.exchange_code` on a client constructed with `oauth=OAuth(client_id, ...)`.

Restore saved grants with `OAuth(client_id, tokens=saved_tokens, on_token=persist_tokens)`.
The synchronous `on_token` callback receives a `TokenSet` on every rotation: persist it durably before returning.
One process must own each rotating grant; the client’s lock does not coordinate multiple workers.
An uncertain refresh is not automatically retried, and `consent_required` requires renewed authorization.
Registered autonomous agents use `OAuth(client_id, client_secret=secret)` with `client_credentials(scopes)` and obtain another token when it expires.
For another deployment, pass its configured API audience as `resource` to both the authorization helper and `OAuth`, and its origin as `base_url` to the helper and client.
See [Authentication](https://peoplescourt.ai/docs/api/authentication.md) for the exact grants and scopes.

## Use async Python

```python
import asyncio
import os
from peoplescourt import AsyncClient

async def main():
    async with AsyncClient(
        os.environ["PEOPLESCOURT_TOKEN"],
        base_url=os.environ["PEOPLESCOURT_BASE_URL"],
    ) as court:
        case = await court.get_case(os.environ["PEOPLESCOURT_CASE_ID"])
        print(case["stage"])
        async for event in court.iter_events(os.environ["PEOPLESCOURT_CASE_ID"]):
            print(event["id"], event["type"])

asyncio.run(main())
```

`AsyncClient` has the same operation names, with awaited requests and async iterators.

## Handle errors and retries

`APIError` exposes `status`, `code`, `request_id`, `details`, `retry_after` and `idempotency_key`.
Its subclasses include `AuthenticationError`, `InsufficientScopeError`, `ConflictError`, `ValidationError` and `RateLimitError`.
Network failures use `TransportError`; malformed responses use `ProtocolError`.
All derive from `PeoplesCourtError`.

Supply and durably store an explicit `idempotency_key` for every business mutation and reuse it only with the identical request.
The SDK generates a key when omitted, but that does not recover the same operation after your process restarts.
There are no automatic mutation or rate-limit retries.
Honor `RateLimitError.retry_after`, refresh case state after a conflict, and preserve the sent key on uncertain outcomes.
OAuth code exchange and refresh must not be replayed as ordinary idempotent writes.

## Verify signed callbacks

With the `webhooks` extra installed, `verify_webhook` checks Ed25519 signatures, the exact body digest, path and query, timestamp, schema version and signing-key validity:

```python
from peoplescourt import verify_webhook

def verified_event_id(raw_body, headers, actual_path_and_query, trusted_key_set):
    return verify_webhook(
        raw_body,
        headers,
        target_path=actual_path_and_query,
        keys=trusted_key_set["keys"],
    )
```

Load verification keys from the configured trusted Court origin’s `/.well-known/peoples-court-webhook-keys.json`, applying your signing-key trust and rotation policy.
Use the untouched request bytes and receiver path/query.
Durably deduplicate and enqueue the verified event ID before acknowledging, then fetch current case state.
Invalid verification raises `WebhookVerificationError`.
Registration requires a partner `platform_orchestrator` credential with `callbacks:manage`; account OAuth cannot register callbacks.

## Operations that use the HTTP API

This Python beta has no dedicated record-summary confirmation, record-export, Award-download or Award-signature verification methods.
Use the documented [HTTP operations](https://peoplescourt.ai/docs/api/workflows/decision-and-execution.md) for those steps and apply a separate Award verification policy.
`verify_webhook` verifies callbacks, not Awards.
The private TypeScript client is not a distributed public SDK.
The [endpoint reference](https://peoplescourt.ai/docs/api/reference.md) provides public curl, TypeScript/fetch and Python HTTP examples for the remaining operations.
