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

# Create and manage runs

> Start, stream, inspect, wait for, and cancel AIR runs from an application.

Install the Python SDK and use a project-scoped app key:

```bash theme={null}
python -m pip install papermap-air
export AIR_BASE_URL="https://dev.airapi.papermap.ai"
export AIR_APP_KEY="<your-app-key>"
```

## Create and wait

```python theme={null}
import asyncio
import os

from air import AirClient


async def main() -> None:
    async with AirClient(
        os.environ["AIR_BASE_URL"],
        os.environ["AIR_APP_KEY"],
    ) as client:
        run = await client.runs.create(
            agent_id="support-agent",
            input={"message": "Where is order 1042?"},
            context={"tenant_id": "acme"},
            webhook_url="https://example.com/hooks/air",
        )
        result = await client.runs.wait(
            run.id,
            timeout=120,
            poll_interval=0.5,
        )
        print(result.status, result.output)


asyncio.run(main())
```

`input` is the task sent to the agent. `context` carries trusted application
data such as a tenant ID; AIR passes it to tools that declare a `context`
parameter. `webhook_url` is optional.

## Consume structured output

When the active agent version has an output schema, `Run.output` is the parsed,
validated object described by that schema:

```python theme={null}
result = await client.runs.wait(run.id, timeout=120)

if result.status == "failed":
    raise RuntimeError(result.error)

answer = result.output["answer"]
needs_review = result.output["needs_human_review"]
```

When the agent version has no output schema, the response remains free-form
text and is returned as `result.output["text"]`.

<Warning>
  Treat a terminal `Run.output` or `run_succeeded` event as authoritative.
  Streaming model deltas are provisional text; for structured runs they contain
  the JSON response while it is still being generated and has not yet passed
  final validation.
</Warning>

## Stream output as it is generated

Use `stream()` when a UI or CLI should show the model response before the run
finishes:

```python theme={null}
run = await client.runs.create(
    agent_id="support-agent",
    input={"message": "Write a detailed account summary."},
)

text = ""
async for event in client.runs.stream(run.id):
    if event.output_delta is not None:
        text += event.output_delta
        print(event.output_delta, end="", flush=True)
    elif event.type == "model_output_reset":
        # AIR retried or used a fallback after a partial response.
        text = ""
        # Clear the partial text in your UI before showing the new attempt.

final = await client.runs.get(run.id)
print(f"\nRun status: {final.status}")
```

The async iterator ends when the run succeeds, fails, or is cancelled. Each
`RunEvent` includes `seq`, `type`, `data`, and `created_at`. If the connection
drops, the SDK reconnects and requests events after the last sequence number,
so missed output is replayed rather than skipped.

Use `after_seq` when you persist the last processed sequence number yourself:

```python theme={null}
async for event in client.runs.stream(run.id, after_seq=last_seq):
    last_seq = event.seq
```

After streaming, read the `Run` with `get()` when you need the authoritative
final status, validated structured output, error, or cost.

## Read or cancel

```python theme={null}
current = await client.runs.get(run.id)

if current.status not in {"succeeded", "failed", "cancelled"}:
    await client.runs.cancel(run.id)
```

The returned `Run` exposes `id`, `status`, `output`, `error`, `cost_usd`, and the
complete response as `raw`.

## Inspect the same run visually

Open **Runs** in the console to inspect the event sequence, model request and
response, token usage, cost, and configuration snapshot.

<Frame caption="The console trace for a run created through the same public API used by the SDK.">
  <img src="https://mintcdn.com/papermap/6FYSF5TTvsKzeVI6/air/images/run-trace.png?fit=max&auto=format&n=6FYSF5TTvsKzeVI6&q=85&s=e2721f2663b9d054aecacec88f394418" alt="AIR run trace showing a successful model response and usage metadata" width="2880" height="1800" data-path="air/images/run-trace.png" />
</Frame>

## Handle errors

```python theme={null}
from air import AirProtocolError, RunTimeoutError

try:
    result = await client.runs.wait(run.id, timeout=30)
except RunTimeoutError:
    # The server-side run may still be active.
    result = await client.runs.get(run.id)
except AirProtocolError as exc:
    print(exc.status_code)
    raise
```

<Warning>
  Treat `context` as security-sensitive input. A tool must still verify that
  every requested record belongs to the supplied tenant before reading or
  changing data.
</Warning>
