Metadata-Version: 2.4
Name: till-ac
Version: 0.1.1
Summary: Python SDK for Till - activation-limited API key proxy
Project-URL: Homepage, https://till.ac
Project-URL: Documentation, https://till.ac
Project-URL: Repository, https://github.com/dbhurley/till-python
Project-URL: Issues, https://github.com/dbhurley/till-python/issues
Author-email: DBH Ventures <hello@till.ac>
License-Expression: MIT
License-File: LICENSE
Keywords: activation-limits,ai,anthropic,api-keys,llm,openai,proxy
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == 'openai'
Description-Content-Type: text/markdown

# till

Python SDK for [Till](https://till.ac) - activation-limited API key proxy.

Ship disposable API keys without handing over the real one. Give agents and contractors scoped access that auto-expires after N activations, tokens, or dollars spent.

## Install

```bash
pip install till-ac
```

With OpenAI SDK support:

```bash
pip install till-ac[openai]
```

## Quick start

### Create and manage scoped keys

```python
from till import TillClient

client = TillClient(admin_key="till_admin_...")

# Create a scoped key (50 activations, then dead)
key = client.create_key(
    provider="openai",
    upstream_key="sk-proj-...",
    max_activations=50,
    name="research-agent",
)

# This is shown once - save it
print(key.scoped_key)  # till_sk_abc123.xyz...

# List all keys
for k in client.list_keys():
    print(f"{k.metadata.get('name')}: {k.used_activations}/{k.max_activations}")

# Revoke a key
client.revoke_key(key.id)
```

### Use scoped keys with OpenAI

The scoped key works as a drop-in replacement. Just point the base URL at Till:

```python
import openai

client = openai.OpenAI(
    api_key="till_sk_abc123.xyz...",
    base_url="https://api.till.ac/v1",
)

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}],
)
```

Or use the helper:

```python
from till.openai_compat import patched_openai

client = patched_openai("till_sk_abc123.xyz...")

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello"}],
)
```

### Set token and dollar limits

```python
# Key dies after 100K tokens OR $5, whichever comes first
key = client.create_key(
    provider="openai",
    upstream_key="sk-proj-...",
    max_activations=1000,
    max_tokens=100_000,
    max_spend_cents=500,  # $5.00
    name="budget-agent",
)
```

### Async support

```python
import asyncio
from till.client import AsyncTillClient

async def main():
    async with AsyncTillClient(admin_key="till_admin_...") as client:
        key = await client.create_key(
            provider="openai",
            upstream_key="sk-...",
            max_activations=50,
        )
        print(key.scoped_key)

asyncio.run(main())
```

## Multi-provider

Till proxies to OpenAI, Anthropic, and Google. The scoped key holder doesn't need to know which provider they're hitting:

```python
# OpenAI key
openai_key = client.create_key(
    provider="openai",
    upstream_key="sk-proj-...",
    max_activations=100,
)

# Anthropic key
anthropic_key = client.create_key(
    provider="anthropic",
    upstream_key="sk-ant-...",
    max_activations=100,
)

# Google key
google_key = client.create_key(
    provider="google",
    upstream_key="AIza...",
    max_activations=100,
)
```

## Account info

```python
account = client.account()
print(f"Plan: {account.tenant.plan}")
print(f"Keys: {account.keys_used}/{account.tenant.max_keys}")
print(f"Activations this month: {account.tenant.activations_this_month}")
```

## Error handling

```python
from till.client import TillError

try:
    key = client.create_key(...)
except TillError as e:
    print(f"Error {e.status_code}: [{e.code}] {e}")
```

## How it works

1. You create a scoped key via the admin API (this SDK)
2. The upstream API key is encrypted into the scoped token - Till's database stores zero upstream keys
3. Hand the scoped key to an agent/contractor
4. They use it exactly like a normal API key (pointed at Till's proxy URL)
5. After N activations (or token/dollar limit), the key auto-expires

## Links

- [Till homepage](https://till.ac)
- [Dashboard](https://api.till.ac/dashboard)
- [API reference](https://till.ac)

## License

MIT
