Metadata-Version: 2.4
Name: opedd
Version: 0.1.0
Summary: Official Python SDK for the Opedd content licensing API (buyer-side)
Project-URL: Homepage, https://opedd.com
Project-URL: Documentation, https://docs.opedd.com
Project-URL: Repository, https://github.com/Opedd/opedd-python
Project-URL: Issues, https://github.com/Opedd/opedd-python/issues
Project-URL: Changelog, https://github.com/Opedd/opedd-python/blob/main/CHANGELOG.md
Author-email: Opedd <support@opedd.com>
License: MIT
License-File: LICENSE
Keywords: ai-licensing,ai-training,content-licensing,opedd,rag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.4.0; extra == 'dev'
Description-Content-Type: text/markdown

# opedd

[![PyPI](https://img.shields.io/pypi/v/opedd.svg)](https://pypi.org/project/opedd/)
[![Python](https://img.shields.io/pypi/pyversions/opedd.svg)](https://pypi.org/project/opedd/)
[![License](https://img.shields.io/pypi/l/opedd.svg)](https://github.com/Opedd/opedd-python/blob/main/LICENSE)

Official Python SDK for the [Opedd](https://opedd.com) content licensing API (buyer-side).

Opedd is programmatic licensing infrastructure between AI buyers and publishers — rights, usage tracking, and payment. "Stripe for content licensing."

## Install

```bash
pip install opedd
```

Requires Python 3.10+.

## Quickstart

```python
from opedd import Opedd

client = Opedd(buyer_token="opedd_buyer_live_...")

# Fetch a single licensed article
article = client.content.get("article-uuid")
print(article["title"], article["author"], article["word_count"])

# Stream the full licensed catalog as NDJSON
client = Opedd(access_key="eak_xxx", buyer_email="eng@yourlab.com")
for row in client.feed.stream_ndjson(limit=5000):
    train_model.ingest(row["content"], metadata=row)

# Pull a procurement-defense compliance dossier
client = Opedd(buyer_jwt="eyJhbGc...")
dossier = client.compliance.report(from_="2026-04-01", to="2026-04-30")
print(f"Retrievals: {dossier['dossier_metadata']['summary']['total_retrievals']}")
```

For end-to-end walkthroughs, see the [cookbook](https://docs.opedd.com/cookbook.md).

## Credentials

The SDK supports three credential types depending on which endpoint you call:

| Endpoint | Credential | Construction |
|---|---|---|
| `client.content.get(...)` | Bearer buyer token | `Opedd(buyer_token="opedd_buyer_live_...")` |
| `client.feed.list(...)` / `client.feed.stream_ndjson(...)` | Access key (query param) | `Opedd(access_key="eak_...")` |
| `client.audit.events(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
| `client.compliance.report(...)` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |
| `client.licenses.purchase(...)` | None (returns Stripe `client_secret`) | `Opedd(buyer_token="...")` |
| `client.licenses.list()` | Supabase JWT | `Opedd(buyer_jwt="eyJhbGc...")` |

Multiple credentials can be supplied at once and the SDK selects the correct one per endpoint.

### Env-var fallbacks

The constructor reads from these env vars when arguments are omitted:

- `OPEDD_BUYER_TOKEN`
- `OPEDD_BUYER_JWT`
- `OPEDD_ACCESS_KEY`
- `OPEDD_BASE_URL` (default `https://api.opedd.com`)

### Exchanging an access key for a bearer token

```python
client = Opedd.from_access_key(
    access_key="eak_xyz...",
    buyer_email="eng@yourlab.com",
)
# client.buyer_token is now set; you can call /content-delivery
```

## API surface

```python
client.content.get(article_id)

client.feed.list(since=None, cursor=None, limit=200)
client.feed.stream_ndjson(since=None, cursor=None, limit=5000)  # generator

client.audit.events(from_=None, to=None, event_type=None, cursor=None, limit=100)

client.compliance.report(from_, to, cursor=None)

client.licenses.purchase(publisher_ids, buyer_email, buyer_org, ...)
client.licenses.list()
```

All methods return dicts matching the backend wire format. See [docs.opedd.com](https://docs.opedd.com) for full response shapes.

## Error handling

```python
from opedd import (
    OpeddError,
    OpeddAuthError,
    OpeddNotFoundError,
    OpeddRateLimitError,
    OpeddServerError,
    OpeddValidationError,
)

try:
    article = client.content.get(uuid)
except OpeddRateLimitError as e:
    time.sleep(e.retry_after_seconds or 60)
    retry()
except OpeddAuthError:
    refresh_token()
except OpeddNotFoundError:
    log.warning("article gone; skipping")
except OpeddError as e:
    log.error(f"{e} request_id={e.request_id}")
```

Every error carries `status_code`, `request_id`, and `body` for forensic correlation.

## Schema version pinning

The SDK ships pinned to backend schema version **`phase-11-m4`** (`opedd.__schema_version__`).

When the backend bumps `X-Opedd-Schema-Version`, the SDK ships a follow-up release within 1 sprint per the [schema-pin invariant](https://github.com/Opedd/opedd-backend/blob/main/INVARIANTS.md#python-sdk--mcp-server-pin-to-backend-x-opedd-schema-version-phase-11-m6).

Additive field bumps are absorbed transparently (dict pass-through). Subtractive or rename bumps require an SDK release; ensure your `requirements.txt` pins to a tested version.

## Tests

Two test suites:

### Unit tests (run in CI, no live API calls)

```bash
pip install -e ".[dev]"
pytest tests/test_unit.py
```

29+ unit tests covering: client construction, credential precedence, env-var fallback, auth-header building per credential type, HTTP error mapping (401/403/404/400/422/429/5xx), NDJSON streaming + cursor pagination, all 5 namespaces' request shapes.

### Integration tests (manual, before each release)

```bash
export OPEDD_BUYER_JWT="..."     # for /buyer-audit + /buyer-compliance-report
export OPEDD_BUYER_TOKEN="..."   # for /content-delivery
export OPEDD_ACCESS_KEY="..."    # for /enterprise-license GET feed
pytest --integration
```

Live tests against `api.opedd.com`. Read-only. Skipped by default (require `--integration` flag).

Per [release discipline](./RELEASE.md), integration tests must pass locally before any version tag is pushed. CI does NOT run integration tests — per institutional risk discipline, autonomous CI runs against production state can pollute `usage_records`, distort metered-publisher payouts, and fire production API calls at scale.

## Development

```bash
git clone https://github.com/Opedd/opedd-python.git
cd opedd-python
pip install -e ".[dev]"
pytest tests/test_unit.py            # unit only (default)
pytest --integration                  # unit + integration (requires env vars)
ruff check src/ tests/                # lint
mypy src/                             # type-check
```

## Contributing

Issues and PRs welcome at [github.com/Opedd/opedd-python](https://github.com/Opedd/opedd-python). For broader questions about Opedd as a platform, email [support@opedd.com](mailto:support@opedd.com).

## License

[MIT](./LICENSE)

## See also

- [docs.opedd.com](https://docs.opedd.com) — full API reference + cookbook
- [opedd-mcp](https://github.com/Opedd/opedd-mcp) — Model Context Protocol server for Claude Desktop / Cursor
- [opedd-backend](https://github.com/Opedd/opedd-backend) — backend implementation (private)
