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

# Getting Started

> Install and configure the keystoneos Python client.

## Installation

```bash theme={null}
pip install keystoneos
```

<Note>
  [View on PyPI](https://pypi.org/project/keystoneos/) | [Source on GitHub](https://github.com/KeyStone-OS/keystone-sdk-python)
</Note>

Requires Python 3.10+. Dependencies: `httpx` (HTTP client) and `pydantic` (type validation).

## Initialize

<CodeGroup>
  ```python Async (recommended) theme={null}
  from keystoneos import KeystoneClient

  client = KeystoneClient(
      client_id="YOUR_CLIENT_ID",
      client_secret="YOUR_CLIENT_SECRET",
      environment="production",
  )

  # Use as async context manager for clean connection handling
  async with client:
      settlements = await client.settlements.list()
  ```

  ```python Sync theme={null}
  from keystoneos import KeystoneSyncClient

  client = KeystoneSyncClient(
      client_id="YOUR_CLIENT_ID",
      client_secret="YOUR_CLIENT_SECRET",
  )

  with client:
      settlements = client.settlements.list()
  ```
</CodeGroup>

The async client is recommended for production. The sync client wraps the async client for scripts, notebooks, and frameworks without native async support.

## Configuration

| Option          | Type          | Default              | Description                                     |
| --------------- | ------------- | -------------------- | ----------------------------------------------- |
| `client_id`     | `str`         | required             | Auth0 M2M client ID                             |
| `client_secret` | `str`         | required             | Auth0 M2M client secret                         |
| `environment`   | `str`         | `"production"`       | `"development"`, `"staging"`, or `"production"` |
| `base_url`      | `str`         | per environment      | Override API base URL                           |
| `auth_domain`   | `str`         | per environment      | Override Auth0 domain                           |
| `audience`      | `str`         | per environment      | Override Auth0 audience                         |
| `timeout`       | `float`       | `30.0`               | Request timeout in seconds                      |
| `retry`         | `RetryConfig` | 3 retries, 0.5s base | Retry configuration                             |

### RetryConfig

Override retry behavior:

```python theme={null}
from keystoneos import RetryConfig

client = KeystoneClient(
    client_id="...",
    client_secret="...",
    retry=RetryConfig(
        max_retries=5,
        base_delay=1.0,
        max_delay=60.0,
        retryable_status_codes={429, 500, 502, 503, 504},
    ),
)
```

## Resources

```python theme={null}
client.instructions   # Submit, get, list, cancel bilateral instructions
client.settlements    # Create, get, list, events, compliance decisions
client.templates      # List and get settlement templates (read-only)
client.webhooks       # CRUD + test, rotate secret, delivery logs, signature verification
client.environments   # CRUD + deactivate platform environments
client.compliance     # approve() and reject() convenience methods
```

## Idempotency Keys

```python theme={null}
key = client.generate_idempotency_key()            # "ks-{uuid4}"
key = client.generate_idempotency_key("my-prefix")  # "my-prefix-{uuid4}"

# Safe to retry - same key returns existing resource
settlement = await client.settlements.create({
    "idempotency_key": key,
    # ...
})
```

## Pagination

All list methods return `PaginatedResponse[T]` with convenience properties:

```python theme={null}
result = await client.settlements.list(limit=20)

print(result.items)       # list[Settlement]
print(result.total)       # Total count
print(result.has_more)    # True if more pages exist
print(result.next_offset) # Offset for next page

# Iterate through all pages
offset = 0
while True:
    page = await client.settlements.list(limit=50, offset=offset)
    for settlement in page.items:
        process(settlement)
    if not page.has_more:
        break
    offset = page.next_offset
```
