# Python SDK

The FervusAI Python SDK provides a sync and async client for integrating with FervusAI from Python agent frameworks including LangChain, AutoGen, and custom agents.

***

## Installation

```bash
pip install fervusai
```

Requires Python 3.9 or later.

***

## Initialisation

```python
import fervusai
import os

# Synchronous client
client = fervusai.Client(api_key=os.environ["FERVUS_API_KEY"])

# Async client
client = fervusai.AsyncClient(api_key=os.environ["FERVUS_API_KEY"])
```

**Options**

| Option        | Type  | Default                      | Description                        |
| ------------- | ----- | ---------------------------- | ---------------------------------- |
| `api_key`     | str   | required                     | Your FervusAI API key              |
| `base_url`    | str   | `"https://api.fervus.ai/v1"` | Override base URL                  |
| `timeout`     | float | `30.0`                       | Request timeout in seconds         |
| `max_retries` | int   | `3`                          | Retries on network or `5xx` errors |

***

## Wallets

```python
# Create
wallet = client.wallets.create(
    agent_id="agent_research_v2",
    label="Research Agent - Market Data",
    policy_id="pol_conservative",
    tags=["research", "production"],
)

# Get
wallet = client.wallets.get("wal_9f3a2b")

# List
page = client.wallets.list(status="active", tags=["production"], limit=50)
for wallet in page.data:
    print(wallet.wallet_id, wallet.usdc_balance)

# Update
client.wallets.update("wal_9f3a2b", label="Research Agent v2")

# Freeze / Unfreeze
client.wallets.update("wal_9f3a2b", status="frozen")
client.wallets.update("wal_9f3a2b", status="active")

# Fund
client.wallets.fund(wallet_id="wal_9f3a2b", amount="100.00", currency="USDC")

# Withdraw
client.wallets.withdraw(wallet_id="wal_9f3a2b", amount="50.00")
client.wallets.withdraw(wallet_id="wal_9f3a2b", amount="all")

# Auto-topup
client.wallets.set_auto_topup(
    "wal_9f3a2b",
    enabled=True,
    trigger_below="10.00",
    topup_amount="50.00",
)
```

### Wallet instance methods

```python
wallet = client.wallets.get("wal_9f3a2b")

# Pay
tx = wallet.pay(
    to="api.perplexity.ai",
    amount="2.50",
    currency="USDC",
    memo="Query batch #8821",
)

# Balance
balance = wallet.balance()  # "97.50"

# Transactions
page = wallet.transactions.list(limit=50)
```

***

## Policies

```python
# Create
policy = client.policies.create(
    name="Conservative - Research Agent",
    max_per_tx="5.00",
    max_per_day="50.00",
    max_per_month="500.00",
    velocity_cap=20,
    allowed_recipients=["api.perplexity.ai", "api.tavily.com"],
)

# Get
policy = client.policies.get("pol_7a2c4e")

# List
page = client.policies.list()

# Update
client.policies.update("pol_7a2c4e", max_per_day="75.00")

# Clone
new_policy = client.policies.clone(
    "pol_conservative",
    name="Conservative + Perplexity only",
    allowed_recipients=["api.perplexity.ai"],
)

# Delete
client.policies.delete("pol_7a2c4e")
```

***

## Payments

```python
# Standard payment
tx = client.payments.create(
    wallet_id="wal_9f3a2b",
    to="api.perplexity.ai",
    amount="2.50",
    currency="USDC",
    memo="Query batch #8821",
    idempotency_key="batch_8821_pay",
)

print(tx.tx_signature)
print(tx.on_chain_slot)
```

### A2A payments

```python
# Create escrow
escrow = client.payments.a2a.create(
    from_wallet="wal_requester",
    to_wallet="7xKp...nR4Q",
    amount="10.00",
    currency="USDC",
    task_description="Summarise Q1 2026 earnings calls - healthcare sector",
    timeout_seconds=3600,
)

# Complete
client.payments.a2a.complete(
    "escrow_xyz",
    proof_type="output_hash",
    proof="sha256:a3f2b1c4...",
)

# Dispute
client.payments.a2a.dispute(
    "escrow_xyz",
    reason="Output does not match specification",
    evidence="sha256:evidence_hash...",
)
```

***

## Async usage

All methods on `fervusai.AsyncClient` are coroutines:

```python
import asyncio
import fervusai
import os

client = fervusai.AsyncClient(api_key=os.environ["FERVUS_API_KEY"])

async def run_agent_payment():
    wallet = await client.wallets.get("wal_9f3a2b")
    
    tx = await wallet.pay(
        to="api.perplexity.ai",
        amount="2.50",
        currency="USDC",
        memo="Query batch #8821",
    )
    
    print(f"Confirmed: {tx.tx_signature}")

asyncio.run(run_agent_payment())
```

The async client is suitable for use inside asyncio-based agent frameworks like LangChain's async runnables or AutoGen's async agent loops.

***

## Error handling

```python
from fervusai.errors import (
    FervusAIError,
    PolicyViolationError,
    NotFoundError,
    AuthenticationError,
)

try:
    tx = wallet.pay(to="api.perplexity.ai", amount="50.00", currency="USDC")
except PolicyViolationError as e:
    print(f"Blocked: {e.violation['rule']} - limit {e.violation['limit']}, attempted {e.violation['attempted']}")
except NotFoundError:
    print("Wallet not found")
except AuthenticationError:
    print("Invalid API key")
except FervusAIError as e:
    print(f"API error {e.code}: {e.message} (request_id: {e.request_id})")
```

***

## Webhook signature verification

```python
import fervusai
import os

is_valid = fervusai.verify_webhook_signature(
    raw_body=request.body,
    signature=request.headers["Fervus-Signature"],
    secret=os.environ["WEBHOOK_SECRET"],
)
```

***

## Pagination

```python
# Manual pagination
cursor = None
while True:
    page = client.wallets.list(limit=100, cursor=cursor)
    for wallet in page.data:
        # process wallet
        pass
    if not page.has_more:
        break
    cursor = page.next_cursor

# Auto-paginating iterator
for wallet in client.wallets.iterate(status="active"):
    # process wallet - pages automatically
    pass
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fervusai.com/sdk/python.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
