# optropic

Official Python SDK for Optropic product authentication.

[![PyPI version](https://badge.fury.io/py/optropic.svg)](https://pypi.org/project/optropic/)
[![Python](https://img.shields.io/pypi/pyversions/optropic.svg)](https://pypi.org/project/optropic/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Features

- **Simple Authentication** - Single API key for all operations
- **Type-Safe** - Full type annotations with `py.typed` support
- **Fast Integration** - Copy-paste quickstart, production-ready
- **Vertical Agnostic** - Same API for pharma, luxury, art, and more
- **Error Transparency** - Clear error codes, actionable messages

## Installation

```bash
pip install optropic
```

## Quick Start

```python
from optropic import OptropicClient

# Initialize the client
client = OptropicClient(api_key="op_live_xxxxxxxxxxxx")

# Verify an asset
result = client.verify("asset-id-or-short-id")

if result.signature_valid:
    print("Product is genuine!")
    print(f"Security level: {result.security_level}")
else:
    print("Warning: verification failed")

# Or use as a context manager
with OptropicClient(api_key="op_live_xxxxxxxxxxxx") as client:
    result = client.verify("asset-id-or-short-id")
    print(result.status)
```

## API Reference

### OptropicClient

The main client class for all SDK operations.

#### Constructor

```python
client = OptropicClient(
    api_key="op_live_xxx",       # Required: Your API key
    base_url="https://...",      # Optional: For self-hosted deployments
    timeout=30.0,                # Optional: Request timeout in seconds (default: 30)
)
```

### Methods

#### verify(asset_id: str) -> VerifyResult

Verify an asset by ID or short_id.

```python
result = client.verify("asset-id-or-short-id")

# Result fields
result.id                  # str: Asset ID
result.status              # str: Verification status
result.security_level      # str: 'signed', 'sealed', etc.
result.signature_valid     # bool: Whether signature is valid
result.verification_count  # int: Number of times verified
result.last_verified_at    # str: ISO timestamp of last verification
result.revocation_status   # str: 'active' or 'revoked'
result.verification_mode   # str: 'online' or 'offline'
result.provenance_valid    # Optional[bool]: Provenance chain validity
result.evidence            # Optional[VerificationEvidence]: Detailed evidence
```

## Error Handling

All errors extend `OptropicError` with clear error codes:

```python
from optropic import (
    OptropicClient,
    OptropicError,
    AuthenticationError,
    NotFoundError,
)

try:
    result = client.verify("asset-id")
except NotFoundError:
    print("Asset not found in system.")
except AuthenticationError:
    print("Invalid API key.")
except OptropicError as e:
    print(f"Error {e.code}: {e}")
    print(f"HTTP status: {e.status}")
```

### Error Types

| Error | Code | Description |
|-------|------|-------------|
| `AuthenticationError` | `INVALID_API_KEY` | API key is invalid or missing |
| `NotFoundError` | `NOT_FOUND` | Asset doesn't exist in the system |
| `OptropicError` | varies | Base error for all other API errors |

## Types

All types are frozen dataclasses with full type annotations:

```python
from optropic import VerifyResult, VerificationEvidence
```

- `VerifyResult` - Result of verifying an asset
- `VerificationEvidence` - Structured evidence from verification

## Environment Variables

Recommended setup for production:

```bash
# .env
OPTROPIC_API_KEY=op_live_xxxxxxxxxxxx
```

```python
import os
from optropic import OptropicClient

client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])
```

## Examples

### Flask Backend

```python
from flask import Flask, request, jsonify
from optropic import OptropicClient, NotFoundError

app = Flask(__name__)
client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])

@app.route("/api/verify", methods=["POST"])
def verify():
    try:
        result = client.verify(request.json["asset_id"])
        return jsonify({
            "status": result.status,
            "signature_valid": result.signature_valid,
            "security_level": result.security_level,
        })
    except NotFoundError:
        return jsonify({"error": "Asset not found"}), 404
```

### Django View

```python
import os
from django.http import JsonResponse
from optropic import OptropicClient

client = OptropicClient(api_key=os.environ["OPTROPIC_API_KEY"])

def verify_asset(request, asset_id):
    result = client.verify(asset_id)
    return JsonResponse({
        "status": result.status,
        "signature_valid": result.signature_valid,
    })
```

## Support

- [Documentation](https://docs.optropic.com)
- [Email Support](mailto:support@optropic.com)
- [Issue Tracker](https://github.com/optropic/sdk-python/issues)

## License

MIT - [Virtrex GmbH](https://optropic.com)
