Python SDK
The AgentFlow Python SDK exposes a single namespaced root client: AgentFlow for synchronous code and AsyncAgentFlow for async applications. One client represents one tenant and one principal.
Installation
uv add agentflow --default-index https://<package-index>/simple/
# or
python -m pip install --index-url https://<package-index>/simple/ agentflow
The public PyPI package named agentflow is not the private AgentFlow SDK
unless your organization has published it there. Use your onboarding package
source, private package index, or internal wheel.
Authenticate
Create a profile with the CLI:
agentflow login \
--endpoint https://your-instance.agentflow.ai \
--profile prod \
--method device \
--client-id <auth0-client-id> \
--audience <auth0-api-audience> \
--auth0-domain <auth0-domain>
Use --method m2m for service clients and CI. Use --method dev only against
a local backend started with DEV_AUTH_BYPASS=true.
Quick start
from agentflow import AgentFlow
with AgentFlow.from_profile("prod") as client:
agent_id = client.agents.by_name("MainAgent").id
result = client.agents.run(agent_id=agent_id, message="What meetings do I have this week?")
print(result.text)
Async mirror:
from agentflow import AsyncAgentFlow
async with AsyncAgentFlow.from_profile("prod") as client:
agent_id = (await client.agents.by_name("MainAgent")).id
result = await client.agents.run(agent_id=agent_id, message="What meetings do I have this week?")
print(result.text)
Core resources
The SDK is organized around resources that map to /api/v1 backend routes:
| Resource | Purpose | Example |
|---|
client.agents | Agent CRUD, runs, context blocks | client.agents.run(...) |
client.agents.tools | Tools scoped to an agent | client.agents.tools.create(agent_id, ...) |
client.agents.subagents | Dynamic sub-agent management | client.agents.subagents.create(parent_id, ...) |
client.agents.knowledge_bases | Agent-scoped KB assignment and search | client.agents.knowledge_bases.search(...) |
client.prompt_blocks | Prompt block management and previews | client.prompt_blocks.create(...) |
client.conversations | Multi-turn conversation objects | client.conversations.create(agent_id=agent_id) |
client.memory | Persistent core and archival memory | client.memory.recall("Acme renewal") |
client.completions | Sync classification, extraction, summarization, enrichment, and custom runs | client.completions.classify(...) |
client.cached_results | Conversation-scoped cached tool artifacts | client.cached_results.retrieve(conversation_id, cache_id) |
client.knowledge_bases | Global KB management and search | client.knowledge_bases.list() |
client.approvals | Tool approval workflows | client.approvals.list() |
client.artifacts | Artifact retrieval and actions | client.artifacts.retrieve(artifact_id) |
client.batches | Async batch jobs, file jobs, polling, and output downloads | client.batches.submit(...) |
client.attachments | File upload/download | client.attachments.upload(...) |
client.background | Inspect and cancel background tasks | client.background.cancel(task_id) |
client.cancellations | Cancel active calls, run IDs, and conversation/message pairs | client.cancellations.cancel_run(run_id) |
client.models | Model catalog and BYO LLM admin policy | client.models.list() |
client.system | Health and metadata | client.system.health() |
Explicit credentials
Profiles are the preferred path. If you already have an AgentFlow bearer token,
pass it directly:
from agentflow import AgentFlow
client = AgentFlow(
endpoint="https://your-instance.agentflow.ai",
access_token="<agentflow-access-token>",
)
export AGENTFLOW_ENDPOINT=https://your-instance.agentflow.ai
export AGENTFLOW_ACCESS_TOKEN=<agentflow-access-token>
Machine clients can use client credentials with delegated-user headers. Request the scopes for the user-owned APIs you need:
from agentflow import AgentFlow
client = AgentFlow(
endpoint="https://your-instance.agentflow.ai",
client_id="<auth0-client-id>",
client_secret="<auth0-client-secret>",
tenant="company-a",
on_behalf_of="svc-bot",
scope="agentflow:conversations agentflow:cancellations",
audience="<auth0-api-audience>",
auth0_domain="<auth0-domain>",
)
Running agents
from agentflow import AsyncAgentFlow, InlinePromptBlock, MemoryPolicy, RetrievalOptions, RunOptions
async with AsyncAgentFlow.from_profile("local") as client:
agent_id = (await client.agents.by_name("MainAgent")).id
result = await client.agents.run(
agent_id=agent_id,
message="Summarize the Acme account",
options=RunOptions(
verbose=True,
model="openai/gpt-5",
temperature=0.2,
max_tokens=4000,
knowledge_bases=["kb_product_docs"],
retrieval_options=RetrievalOptions(top_k=8, search_type="hybrid"),
memory_policy=MemoryPolicy(archival_mode="auto", archival_limit=5),
context_refs=[{"system": "crm", "type": "account", "id": "001ABC123"}],
prompt_blocks=[
InlinePromptBlock(
name="selected_account",
body="Acme Corp, ARR $120k, renewal in 31 days",
tags=["selection", "context"],
)
],
),
)
print(result.text)
RunOptions maps typed fields directly to POST /api/v1/agent/{agent_id}/chat, including knowledge base, bounded retrieval_options, per-run memory_policy, model, context refs, inline prompt_blocks, and streaming controls.
Streaming
from agentflow.events import FinalResponse, TextDelta
async with AsyncAgentFlow.from_profile("local") as client:
agent_id = (await client.agents.by_name("MainAgent")).id
async for event in client.agents.stream(agent_id=agent_id, message="Analyze our Q3 pipeline"):
if isinstance(event, TextDelta):
print(event.text, end="", flush=True)
elif isinstance(event, FinalResponse):
print(event.text)
Each typed event includes the original backend SSE payload on event.raw. For agent runs, this is the supported raw event view in the SDK.
async for event in client.agents.stream(agent_id=agent_id, message="Analyze our Q3 pipeline"):
print(event.type, event.raw)
Mutating SDK calls accept an idempotency_key option where the API supports request de-duplication. For direct tool runs, the key rejects duplicate in-flight submissions; it is not a persisted result replay key. For chat, pass a stable message_id when retrying the same user action.
Conversations
For multi-turn work, create a conversation and reuse it:
async with AsyncAgentFlow.from_profile("local") as client:
agent_id = (await client.agents.by_name("MainAgent")).id
conversation = await client.conversations.create(agent_id=agent_id)
first = await conversation.send("Show me the pipeline")
second = await conversation.send("Now filter to deals over $100K")