> For the complete documentation index, see [llms.txt](/llms.txt). Every page on this site is also available as markdown at `<path>.md`.

# Quickstart

Create your first Flow AI runtime in five to ten minutes.

This quickstart builds a minimal coordinator and specialist, runs the native
runtime with the deterministic testing interpreter, and prints the event stream.
It does not require provider credentials.

## Before you begin

- Install Python 3.11 or newer.
- Use an environment with access to the private preview package.
- No Anthropic, OpenAI, or other provider API key is required for this example.

## Install

<Callout type="warn" title="Private preview access">

`flowai-harness` is not currently available on the public PyPI registry.
To get access to the preview release, contact
[aaro@flow-ai.com](mailto:aaro@flow-ai.com) or
[karolus@flow-ai.com](mailto:karolus@flow-ai.com) before running the
install command below.

</Callout>

```bash
pip install flowai-harness
```

## What you build

You will build one runtime with:

- A tenant identity for runtime-owned state.
- A coordinator that receives the user request.
- A specialist that can be routed to by the coordinator.
- A deterministic no-network mock response for local testing.

## Create `hello_flowai.py`

Save the following as `hello_flowai.py`. It is one complete, runnable script:

```python
import asyncio

from flowai_harness import (
    TestingConfig,
    create_runtime,
    define_coordinator,
    define_runtime,
    define_specialist,
    define_tenant,
)

async def main() -> None:
    tenant = define_tenant("acme", "v1")

    specialist = define_specialist(
        name="greeter",
        model="claude-haiku-4-5",
        prompt="You greet the user politely.",
    )
    coordinator = define_coordinator(
        name="hello_coordinator",
        model="claude-sonnet-4-6",
        routes=["greeter"],
        prompt="Route greeting requests to the greeter specialist.",
    )

    runtime_spec = define_runtime(
        tenant=tenant,
        agents=[coordinator, specialist],
        providers={"anthropic": {"apiKey": "unused"}},
    )

    runtime = create_runtime(
        runtime_spec,
        testing=TestingConfig(mock_response="hello from the Rust runtime"),
    )

    async for event in runtime.query("Say hello", thread_id="thread-1"):
        print(event)

asyncio.run(main())
```

<Callout type="info" title="Why `providers=` when no key is used">

Every agent model resolves to a provider, and `create_runtime` validates
that the provider is declared in `RuntimeSpec.providers` — even when the
deterministic testing interpreter never calls it. The placeholder
`{"apiKey": "unused"}` satisfies validation without making any network
request.

</Callout>

## Run it

```bash
python hello_flowai.py
```

## Expected output

The runtime prints a short stream of event dictionaries. Identifiers such as
`toolInvocationId` differ on every run, but the shape looks like this:

```text
{'agentName': 'hello_coordinator', 'state': 'call', 'toolInvocationId': 'inv-1955407c-815d-4f47-a49c-99f719900160', 'type': 'tool-agent'}
{'type': 'step-start'}
{'text': 'Received: Say hello\n\n', 'type': 'text'}
{'text': 'hello from the Rust runtime', 'type': 'text'}
{'data': {'hadTimeout': False, 'phases': {'llmCalls': 1, 'llmTimeMs': 0, 'subAgentTimeMs': 0, 'toolTimeMs': 0}, 'retryCount': 0, 'toolTimings': [], 'totalDurationMs': 0}, 'type': 'data-latency-summary'}
{'finishReason': 'stop', 'type': 'finish', 'usage': {'cacheCreationInputTokens': 0, 'cacheReadInputTokens': 0, 'completionTokens': 25, 'promptTokens': 50, 'totalTokens': 75}}
{'agentName': 'hello_coordinator', 'state': 'result', 'toolInvocationId': 'inv-1955407c-815d-4f47-a49c-99f719900160', 'type': 'tool-agent'}
```

## What happened

- `define_tenant("acme", "v1")` created the tenant identity that keys all
  runtime-owned state.
- `define_specialist` and `define_coordinator` built two validated Pydantic
  agent specs; the coordinator routes greeting requests to the specialist.
  Coordinators with `routes=[...]` receive the built-in `call_agent` tool by
  default; you do not need to list `toolkits=["agents"]`.
- `define_runtime` assembled the specs into a `RuntimeSpec`, including the
  provider declaration that every agent model resolves against.
- `create_runtime(..., testing=TestingConfig(...))` selected the deterministic
  testing interpreter, so no API key or network access was needed.
- `runtime.query(...)` streamed events from the embedded Rust runtime, ending
  with the mock response text.

The testing interpreter returns a fixed response, so routing and specialist
logic are stubbed: the events show `hello_coordinator`, not `greeter`. You are
verifying wiring here, not behavior — swap in a live interpreter to see routing
and specialists actually fire.

## Common errors

| Error | Fix |
| ----- | --- |
| `ValueError: agent 'hello_coordinator' references provider 'anthropic' for model 'claude-sonnet-4-6', but no such provider is declared in RuntimeSpec.providers` | Add `providers={"anthropic": {"apiKey": "unused"}}` to `define_runtime(...)`. The testing interpreter never calls the provider, but the spec must declare it. |
| `ValueError: create_runtime accepts either testing or a non-default interpreter, not both` | Pass either `testing=TestingConfig(...)` or `interpreter="..."` to `create_runtime`, never both — they are mutually exclusive modes. |
| `ModuleNotFoundError: No module named 'flowai_harness'` | Install the package in the same virtual environment that runs the script. |
| Native extension import error | Use Python 3.11+ and reinstall the wheel for the active interpreter. |
| Agent route validation fails | Make sure every coordinator `routes=[...]` entry matches a registered agent name. |

## Next steps

- Build the full [Acme scenario-planning tutorial](/docs/tutorials/acme-scenario-agent).
- Read the [Concepts](/docs/concepts) section for the mental model.
- Browse the [Guides](/docs/guides) for task-focused how-tos.
