Metadata-Version: 2.4
Name: clovrix
Version: 0.1.0
Summary: Official Python SDK for the Clovrix Public API
Project-URL: Homepage, https://github.com/clovrix/sdk/tree/main/python
Project-URL: Repository, https://github.com/clovrix/sdk
Author: Clovrix
License: MIT
Keywords: clovrix,config,environment-variables,sdk,secrets
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.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# clovrix

Official Python SDK for the [Clovrix](https://clovrix.com) Public API. Sync and async clients
built on [httpx](https://www.python-httpx.org/), fully typed.

## Install

```bash
pip install clovrix
```

Requires Python 3.9+.

## Authentication

Create an API token from your organization's **Settings → API tokens** page, then either pass it
to the client or expose it as an environment variable:

```bash
export CLOVRIX_TOKEN="icr_xxxxxxxx…"
```

Resolution order: `token=` argument → `CLOVRIX_TOKEN` env var → otherwise a `ClovrixConfigError`
is raised. The host defaults to `https://app.clovrix.com`; override with `base_url=` or
`CLOVRIX_API_URL` (scheme + host only — the `/api/public/v1` path is added for you).

## Usage

```python
from clovrix import Client

clovrix = Client()  # token from CLOVRIX_TOKEN

# Fetch every entry in an environment as a dict.
config = clovrix.get_entries("backend", "production")
print(config["DATABASE_URL"])

# Fetch a single entry (raises NotFoundError if unset).
entry = clovrix.get_entry("backend", "production", "DATABASE_URL")
print(entry.key, entry.value, entry.is_secret)

# Write one entry. is_secret applies only when the key is first created.
result = clovrix.set_entry("backend", "production", "API_KEY", "s3cr3t", is_secret=True)
print(result.created)  # True if newly created, False if a new version

# Write up to 100 entries atomically; returns the number written.
from clovrix import WriteItem

written = clovrix.set_entries("backend", "production", [
    WriteItem(key="FEATURE_X", value="on"),
    {"key": "API_KEY", "value": "s3cr3t", "is_secret": True},  # mappings work too
])
```

Use it as a context manager to close the underlying HTTP connection pool:

```python
with Client() as clovrix:
    config = clovrix.get_entries("backend", "production")
```

Load straight into the process environment:

```python
import os
os.environ.update(clovrix.get_entries("backend", "production"))
```

### Async

```python
import asyncio
from clovrix import AsyncClient

async def main():
    async with AsyncClient() as clovrix:
        config = await clovrix.get_entries("backend", "production")
        print(config)

asyncio.run(main())
```

## Configuration

```python
Client(
    token="icr_…",            # default: CLOVRIX_TOKEN
    base_url="https://…",     # default: CLOVRIX_API_URL or https://app.clovrix.com
    timeout=30.0,             # per-request timeout (seconds)
    user_agent="my-app/1.0",
    http_client=my_httpx_client,  # bring your own httpx.Client / httpx.AsyncClient
)
```

## Error handling

Every failed request raises a subclass of `ClovrixError`. API errors expose `.status_code`; a
billing block also exposes `.billing_status`.

| Exception | Cause |
| --- | --- |
| `ClovrixConfigError` | Missing token / misconfiguration |
| `ClovrixConnectionError` | Network failure or timeout (no HTTP response) |
| `AuthenticationError` | `401` — missing/invalid/expired token |
| `BillingError` | `402` — billing pending or restricted (`.billing_status`) |
| `ForbiddenError` | `403` — token role lacks access |
| `NotFoundError` | `404` — project/environment/key not found |
| `ValidationError` | `400` / `413` / `422` — invalid request, nothing written |
| `ServerError` | `5xx` |

```python
from clovrix import NotFoundError, BillingError

try:
    entry = clovrix.get_entry("backend", "production", "MAYBE")
except NotFoundError:
    ...  # key isn't set
except BillingError as err:
    print("billing:", err.billing_status)
```

## Limits

- Keys match `^[A-Z_][A-Z0-9_]*$`, max 128 chars.
- Values are UTF-8 up to 32 KiB.
- Bulk write ≤ 100 keys (atomic); bulk fetch ≤ 500 entries per project.
- `is_secret` is fixed at creation and cannot be changed afterwards.

## Development

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```
