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

# Python SDK

> Reference for the awaithumans Python package.

## Install

Pick one of two starting points based on what you're doing:

```bash theme={"system"}
pip install "awaithumans[server]"   # to RUN the server + dashboard locally
pip install awaithumans              # to CALL a server (slim SDK, 2 deps)
```

Most people want the first. Use the bare SDK install only when your agent runs in a lambda, edge runtime, or container where you just need an HTTP client talking to a server hosted elsewhere — bare install is `httpx` + `pydantic` and nothing else.

### Adapters and verifiers compose with either base

Extras stack. Comma-separate them in one bracket to install in a single command:

```bash theme={"system"}
pip install "awaithumans[server,temporal]"            # server + Temporal
pip install "awaithumans[server,verifier-claude]"     # server + Claude verifier
pip install "awaithumans[temporal,verifier-claude]"   # SDK + Temporal + Claude
pip install "awaithumans[server,temporal,verifier-claude]"  # all three
```

### All extras

| Extra             | Adds                                      |
| ----------------- | ----------------------------------------- |
| `server`          | FastAPI server, CLI, bundled dashboard    |
| `temporal`        | Temporal workflow adapter                 |
| `langgraph`       | LangGraph interrupt/resume adapter        |
| `verifier-claude` | Anthropic SDK (for Claude verification)   |
| `verifier-openai` | OpenAI SDK                                |
| `verifier-gemini` | Google Generative AI SDK                  |
| `verifier-azure`  | OpenAI SDK (used against Azure endpoints) |

Python 3.10+ required.

## await\_human / await\_human\_sync

The primitive. Async and sync flavors:

```python theme={"system"}
import asyncio
from pydantic import BaseModel
from awaithumans import await_human, await_human_sync


# Define the shape of the data your agent sends to the human...
class RefundPayload(BaseModel):
    amount: int
    customer: str


# ...and the shape of the response form they fill in.
class RefundDecision(BaseModel):
    approved: bool
    reason: str = ""


# Sync (from a regular Python script, Flask, Celery, etc.)
decision = await_human_sync(
    task="Approve refund?",
    payload_schema=RefundPayload,
    payload=RefundPayload(amount=250, customer="cus_123"),
    response_schema=RefundDecision,
    timeout_seconds=900,
)
print(decision.approved, decision.reason)


# Async (inside an existing asyncio context — agent frameworks, ASGI)
async def main() -> None:
    decision = await await_human(
        task="Approve refund?",
        payload_schema=RefundPayload,
        payload=RefundPayload(amount=250, customer="cus_123"),
        response_schema=RefundDecision,
        timeout_seconds=900,
    )
    print(decision.approved, decision.reason)


asyncio.run(main())
```

### Parameters

| Parameter         | Type                               | Required | Description                                                                      |
| ----------------- | ---------------------------------- | -------- | -------------------------------------------------------------------------------- |
| `task`            | `str`                              | yes      | Human-readable task description (shown in the dashboard / Slack / email).        |
| `payload_schema`  | `type[BaseModel]`                  | yes      | Pydantic class that defines the data the human reviews.                          |
| `payload`         | `BaseModel`                        | yes      | Instance of `payload_schema` — the actual data.                                  |
| `response_schema` | `type[BaseModel]`                  | yes      | Pydantic class for the response form. Drives the UI.                             |
| `timeout_seconds` | `int`                              | yes      | Min 60 (1 min), max 2,592,000 (30 days).                                         |
| `assign_to`       | `str \| list[str] \| dict \| None` | no       | Routing target. See [Routing](/routing/overview).                                |
| `notify`          | `list[str] \| None`                | no       | Channel destinations (`slack:#x`, `email:y@z`, …).                               |
| `verifier`        | `VerifierConfig \| None`           | no       | Server-side LLM verification. See [Verifier](/adapters/verifier).                |
| `idempotency_key` | `str \| None`                      | no       | Default: `sha256(task, payload)[:32]`. See [Idempotency](/concepts/idempotency). |
| `redact_payload`  | `bool`                             | no       | Default `False`. When true, audit log + verifier prompt are scrubbed.            |
| `server_url`      | `str \| None`                      | no       | Override `AWAITHUMANS_URL` env var / discovery file.                             |
| `api_key`         | `str \| None`                      | no       | Override `AWAITHUMANS_ADMIN_API_TOKEN` env var / discovery file.                 |

### Returns

An instance of `response_schema` — Pydantic-validated. If validation fails, raises `SchemaValidationError`.

### Raises

| Exception                      | When                                                          |
| ------------------------------ | ------------------------------------------------------------- |
| `TaskTimeoutError`             | Task hit `timeout_seconds` without completion.                |
| `TaskCancelledError`           | Agent or operator cancelled.                                  |
| `VerificationExhaustedError`   | Verifier rejected `max_attempts` times.                       |
| `SchemaValidationError`        | Response didn't match `response_schema`.                      |
| `TaskNotFoundError`            | Task ID disappeared (DB reset?).                              |
| `TaskCreateError`              | Server rejected `POST /api/tasks`.                            |
| `PollError`                    | Long-poll returned non-200.                                   |
| `ServerUnreachableError`       | Couldn't connect to `server_url`.                             |
| `MarketplaceNotAvailableError` | Used `assign_to={"marketplace": True}`. Reserved for Phase 3. |

All inherit from `AwaitHumansError`.

## Verifier helpers

```python theme={"system"}
from awaithumans.verifiers.claude import claude_verifier
from awaithumans.verifiers.openai import openai_verifier
from awaithumans.verifiers.gemini import gemini_verifier
from awaithumans.verifiers.azure_openai import azure_openai_verifier

verifier = claude_verifier(
    instructions="Reject any refund decision that lacks a clear reason.",
    model="claude-sonnet-4-5",         # optional, has default
    max_attempts=3,                    # default 3
    api_key_env="ANTHROPIC_API_KEY",   # default
)
```

Each returns a `VerifierConfig` you pass to `await_human(verifier=...)`. The actual LLM call runs server-side. See [Verifier](/adapters/verifier).

## Adapters

### Temporal

```python theme={"system"}
# Inside a Temporal workflow:
from temporalio import workflow
with workflow.unsafe.imports_passed_through():
    from awaithumans.adapters.temporal import await_human

# In your web server (separate process):
from awaithumans.adapters.temporal import dispatch_signal
```

See [Temporal](/adapters/temporal).

### LangGraph

```python theme={"system"}
# Inside a LangGraph node:
from awaithumans.adapters.langgraph import await_human

# Driver — run the graph:
from awaithumans.adapters.langgraph import drive_human_loop
```

See [LangGraph](/adapters/langgraph).

## CLI

```bash theme={"system"}
awaithumans dev               # boot server + auto-config for local dev
awaithumans add-user          # add a directory user (interactive)
awaithumans list-users        # list directory users
awaithumans version           # print version
```

`awaithumans dev` auto-generates `PAYLOAD_KEY`, `ADMIN_API_TOKEN`, and the SQLite DB if missing. Writes `~/.awaithumans-dev.json` so the SDK auto-discovers the running server.

## Configuration

The SDK reads configuration in this order (first match wins):

1. Function-call args (`server_url=`, `api_key=`)
2. Environment variables (`AWAITHUMANS_URL`, `AWAITHUMANS_ADMIN_API_TOKEN`)
3. Discovery file (`~/.awaithumans-dev.json`)
4. Defaults (`http://localhost:3001`, no token)

Production deployments should use environment variables. The discovery file is a dev affordance.

## Type stubs

The package ships with `py.typed`. mypy/pyright pick up the types automatically:

```python theme={"system"}
# (using the RefundPayload / RefundDecision models defined earlier)
decision: RefundDecision = await_human_sync(
    task="Approve refund?",
    payload_schema=RefundPayload,
    payload=RefundPayload(amount=250, customer="cus_123"),
    response_schema=RefundDecision,
    timeout_seconds=900,
)
# `decision` is typed as RefundDecision — IDE autocomplete + mypy enforcement.
```
