Serve as an API
Turn agents into an HTTP service with streaming, sessions, and auth.
Product teams can connect web, mobile, and server-side clients to the same AgentOS backend. Registered agents become FastAPI services with streaming run routes, while AgentOS manages sessions, memory, knowledge, traces, evaluations, and approvals through the same API.
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS
db = PostgresDb(db_url="postgresql+psycopg://ai:ai@localhost:5532/ai")
agent = Agent(
id="customer-agent",
model=OpenAIResponses(id="gpt-5.5"),
db=db,
add_history_to_context=True,
enable_agentic_memory=True,
)
agent_os = AgentOS(
agents=[agent],
db=db,
cors_allowed_origins=[
"http://localhost:3000",
"https://app.yourproduct.com",
"https://os.agno.com",
],
)
app = agent_os.get_app()
if __name__ == "__main__":
agent_os.serve(app="customer_agent:app", port=7777)Supplying cors_allowed_origins replaces the AgentOS defaults. Include every browser and AgentOS UI origin that needs to call the backend.
Run the API
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activateInstall dependencies
uv pip install -U "agno[os]" openai sqlalchemy "psycopg[binary]"Export your OpenAI API key
export OPENAI_API_KEY="your_openai_api_key_here"Run PgVector
docker run -d \
-e POSTGRES_DB=ai \
-e POSTGRES_USER=ai \
-e POSTGRES_PASSWORD=ai \
-e PGDATA=/var/lib/postgresql \
-v pgvolume:/var/lib/postgresql \
-p 5532:5432 \
--name pgvector \
agnohq/pgvector:18Start AgentOS
Save the code as customer_agent.py, then run:
python customer_agent.pyCalling it from a surface
Every run endpoint takes the same shape, whether the caller is a browser widget or a backend job.
curl -X POST http://localhost:7777/agents/customer-agent/runs \
-F 'message=Summarize this thread' \
-F 'user_id=sarah@acme.com' \
-F 'session_id=thread-42' \
-F 'stream=false'{
"run_id": "run_abc123",
"session_id": "thread-42",
"user_id": "sarah@acme.com",
"agent_id": "customer-agent",
"status": "COMPLETED",
"content": "..."
}After enabling authorization, a browser widget can stream with a JWT:
async function askCustomerAgent(message, threadId, jwt) {
const body = new FormData();
body.append("message", message);
body.append("session_id", threadId);
body.append("stream", "true");
const res = await fetch("https://os.yourproduct.com/agents/customer-agent/runs", {
method: "POST",
headers: { "Authorization": `Bearer ${jwt}` },
body,
});
return res.body; // SSE stream into the UI
}| Want | Pass |
|---|---|
| Token stream for a live UI | stream=true (Server-Sent Events, the default) |
| A single JSON response | stream=false |
| Long job, poll later | background=true and stream=false |
| User and thread attribution | user_id and a per-thread session_id |
AgentOS endpoints
| Endpoint group | Covers |
|---|---|
| Runs | Create, stream, cancel, run in background, resume disconnected streams |
| Sessions | Create, list, rename, delete, and pull every run in a session. Per-user enforcement requires user isolation. |
| Memory | Create, update, delete, search user memories |
| Knowledge | Add, update, search, and delete indexed content |
| Traces and metrics | Per-run spans when tracing is enabled, plus token usage and model metrics |
| Evaluations | Run and retrieve agent and team evaluation results |
| Approvals | List and resolve paused approval requests |
| Schedules | Create, update, trigger, enable, disable, and delete recurring runs |
Browse the live OpenAPI spec at the /docs endpoint of your running AgentOS.
Custom routes
AgentOS is a FastAPI app. Add routes for webhooks, dashboards, or product-specific endpoints. The agent is a regular Python object you can call from anywhere.
A public Stripe webhook must read the raw request body and verify its Stripe-Signature with the endpoint secret before parsing or passing an event to the agent. Never accept an unverified decoded dictionary. See Stripe's webhook signature guide.
Auth
Set authorization=True to require a valid JWT on protected central routes. Set JWT_VERIFICATION_KEY or JWT_JWKS_FILE before building the app. Add user_isolation=True to filter user-scoped data for non-admin callers.
from agno.os.config import AuthorizationConfig
agent_os = AgentOS(
agents=[agent],
db=db,
authorization=True,
authorization_config=AuthorizationConfig(user_isolation=True),
cors_allowed_origins=[
"http://localhost:3000",
"https://app.yourproduct.com",
"https://os.agno.com",
],
)AgentOS validates the token before agent code runs. The JWT sub pins user_id, and the scopes claim drives RBAC. An optional session_id claim overrides the form value; otherwise the client supplies the session ID with the request.
Next steps
| Task | Guide |
|---|---|
| Add Slack or browser surfaces | Interfaces |
| Lock down endpoints | Security and auth |