Metadata-Version: 2.5
Name: shepherd-sdk
Version: 0.2.0
Summary: Python client for the Shepherd API: connect wallets, classify transactions, and pull financial statements.
Project-URL: Homepage, https://getshepherd.co/developers.html
Author-email: Shepherd <alan@getshepherd.co>
License: MIT
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.8
Requires-Dist: requests>=2.25
Description-Content-Type: text/markdown

# shepherd-sdk (Python)

Python client for the [Shepherd API](https://getshepherd.co/developers.html): connect wallets, classify every transaction, and pull audit-ready financial statements.

```bash
pip install shepherd-sdk
```

```python
from shepherd import Shepherd

shep = Shepherd("shep_live_...")          # or Shepherd.create_sandbox() for an instant test key

wallet = shep.wallets.connect("0x...", "base", end_user_id="cust_4821")
pnl = shep.statements.income(start_date="2026-09-01", end_date="2026-09-30", end_user_id="cust_4821")
print(pnl["net_income"])
```

## Sandbox

```python
shep = Shepherd.create_sandbox(email="you@company.com")
print(shep.sandbox_info)    # seeded wallet + customer
```

Sandbox keys start with `shep_test_`. Data is synthetic; connecting any address returns generated activity.

## Webhooks

```python
from shepherd import construct_event, SignatureVerificationError

# Flask example
@app.post("/shepherd/webhook")
def hook():
    try:
        event = construct_event(request.get_data(), request.headers["Shepherd-Signature"], WEBHOOK_SECRET)
    except SignatureVerificationError:
        return "", 400
    if event["type"] == "wallet.scan_completed":
        ...
    return "", 200
```

## Push your own transactions

```python
acct = shep.accounts.create("acct_123", type="bank", currency="USD", end_user_id="cust_4821")
r = shep.accounts.push_transactions(acct["id"], [
    {"external_id": "tx_1", "date": "2026-09-03", "amount": 1250.00, "direction": "inflow", "counterparty": "Acme Corp"},
])
print(r["created"], r["duplicates"], r["rejected"])
shep.jobs.wait(r["classification"]["job_id"])      # classification runs in the background
```

Sending the same `external_id` again changes nothing, so it is safe to retry.

## Pagination

```python
for tx in shep.transactions.iterate(status="unclassified"):   # follows pages for you
    ...
changed = shep.transactions.iterate(updated_since="2026-10-01T00:00:00Z")   # keep a copy in sync
```

## One set of books per customer (platform accounts)

```python
shep.workspaces.create("cust_4821", "Acme Corp")
acme = shep.with_workspace("cust_4821")        # every call on `acme` reads and writes that customer only
acme.statements.income(start_date="2026-09-01")
```

## Keys and safe retries

```python
key = shep.api_keys.create(label="reporting", scope="read", expires_in_days=90)   # admin key needed
shep.api_keys.rotate(key["id"], grace_hours=24)
shep.accounts.create("acct_123", idempotency_key="create-acct_123")   # POSTs with a key retry safely
```

## Errors

Every failure raises a `ShepherdError` subclass with `.status`, `.message`, `.code`, `.request_id`, `.body`:
`InvalidRequestError` (400), `AuthenticationError` (401), `PermissionDeniedError` (403), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429, with `.retry_after`), `ServerError` (5xx).
GET requests, and POSTs sent with an `idempotency_key`, retry automatically on 429/502/503 and honour `Retry-After`.

## Resources

`wallets`, `transactions`, `accounts`, `jobs`, `statements`, `entities`, `opening_balances`, `accruals`, `classification_rules`, `chart_of_accounts`, `periods`, `reconciliation`, `audit_log`, `api_keys`, `workspaces`, `webhooks`, `sandbox`. See the [API reference](https://getshepherd.co/developers-docs.html#reference).
