> ## Documentation Index
> Fetch the complete documentation index at: https://game-narrative.docs.kitefrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK

> Synchronous Python client for KiteFrost.

# Python SDK

SDKs are per-pack. This page covers the **shared core** every per-pack
client is built on (`kitefrost-core`, installed automatically as a
dependency) - auth, transport, and the resources common to every pack. For
pack-specific resources (characters, continuity, dialogue, encounters,
sessions, ...), see your pack's own quickstart and
[API reference](/api-reference).

## Installation

SDKs are per-pack - there is no single `kitefrost` package. Install the one
for your pack, e.g.:

```bash theme={null}
pip install kitefrost-game-narrative   # or kitefrost-ttrpg-gm
```

See [SDKs overview](overview) for the exact package name per pack, or your pack's own quickstart for a working example.

## Quick Start

```python theme={null}
from kitefrost_game_narrative import GameNarrativeClient

client = GameNarrativeClient.from_api_key("sk_your_key_here")

# every per-pack client exposes the shared core at .core
health = client.core.health.check()
print(health)  # {"status": "ok", "version": "..."}
```

The client is **synchronous** - calls block and return directly, no
`async`/`await`.

## Shared Core Resources

Available as `client.core.<resource>` on every per-pack client.

### Health

```python theme={null}
client.core.health.check()  # liveness probe - no auth required
```

### Auth

```python theme={null}
client.core.auth.whoami()  # introspect the current API key
```

### Projects

```python theme={null}
client.core.projects.list()
client.core.projects.get(project_id)
client.core.projects.create({"name": "my-project"})
```

### API Keys

```python theme={null}
client.core.keys.list()
client.core.keys.create({"scopes": ["read", "write"]})
client.core.keys.revoke(key_id)
```

### Billing

```python theme={null}
client.core.billing.usage()
```

### Events

```python theme={null}
# Send a client/telemetry event
client.core.events.send({"event_type": "...", "payload": {...}})
```

### Context

```python theme={null}
client.core.context.get(project_id)
```

### BYOK (Bring Your Own Key)

```python theme={null}
client.core.byok.list()
client.core.byok.set({"provider": "...", "api_key": "..."})
```

### Webhooks

```python theme={null}
client.core.webhooks.list()
client.core.webhooks.create({"url": "https://your-server.com/hook", "events": [...]})
client.core.webhooks.delete(webhook_id)
```

## Error Handling

All exceptions inherit from `kitefrost_core.KiteFrostError` and expose
`.message`, `.status_code`, and `.feedback_id`.

```
KiteFrostError
├── AuthError        (401/403) - missing, invalid, or under-permissioned API key
├── NotFoundError     (404)    - project/entity/resource not found
├── ValidationError   (422)    - malformed request payload
├── RateLimited       (429)    - too many requests; check .retry_after
└── ServerError       (5xx)    - unexpected server error
```

There is no automatic retry beyond a one-shot re-auth on a 401. On
`RateLimited`, back off yourself using `.retry_after` (seconds, may be
`None`):

```python theme={null}
from kitefrost_core import RateLimited, ValidationError, KiteFrostError

try:
    client.core.projects.create({"name": "my-project"})
except RateLimited as e:
    print(f"Rate limited. Retry after {e.retry_after}s")
except ValidationError as e:
    print(f"Bad request: {e.message}")
except KiteFrostError as e:
    print(f"API error {e.status_code}: {e.message}")
    if e.feedback_id:
        print(f"Feedback reference: {e.feedback_id}")
```

See [Telemetry & Privacy](../core-concepts/telemetry) for what `feedback_id` is and how to use it.

## Source

The core is at `sdk/python/packages/core/` and per-pack clients at
`sdk/python/packages/<pack>/` in the main repository.
