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.

customer_agent.py
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/activate

Install 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:18

Start AgentOS

Save the code as customer_agent.py, then run:

python customer_agent.py

Calling 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
}
WantPass
Token stream for a live UIstream=true (Server-Sent Events, the default)
A single JSON responsestream=false
Long job, poll laterbackground=true and stream=false
User and thread attributionuser_id and a per-thread session_id

AgentOS endpoints

Endpoint groupCovers
RunsCreate, stream, cancel, run in background, resume disconnected streams
SessionsCreate, list, rename, delete, and pull every run in a session. Per-user enforcement requires user isolation.
MemoryCreate, update, delete, search user memories
KnowledgeAdd, update, search, and delete indexed content
Traces and metricsPer-run spans when tracing is enabled, plus token usage and model metrics
EvaluationsRun and retrieve agent and team evaluation results
ApprovalsList and resolve paused approval requests
SchedulesCreate, 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

TaskGuide
Add Slack or browser surfacesInterfaces
Lock down endpointsSecurity and auth

Developer Resources