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

# Server: Agents HTTP

> Deploy agents as HTTP API servers using praisonai --serve or Agent.launch()

Deploy single or multi-agent systems as HTTP REST API servers.

## Quick Start

<Steps>
  <Step title="Install Dependencies">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    pip install "praisonaiagents[os]"
    ```
  </Step>

  <Step title="Set API Key">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    export OPENAI_API_KEY="your-key"
    ```
  </Step>

  <Step title="Initialize Agents">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai --init "helpful assistant"
    ```
  </Step>

  <Step title="Start Server (localhost)">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai serve agents --port 8000
    ```

    **Expected Output:**

    ```
    📄 Loading workflow from: agents.yaml
    🚀 Starting PraisonAI API server...
       Host: 127.0.0.1
       Port: 8000
    🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/agents
    ✅ FastAPI server started at http://127.0.0.1:8000
    📚 API documentation available at http://127.0.0.1:8000/docs
    ```
  </Step>

  <Step title="Start Server (public bind)">
    A non-localhost host requires an API key:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    export PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)"
    praisonai serve agents --port 8000 --host 0.0.0.0
    ```
  </Step>

  <Step title="Verify">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    curl http://localhost:8000/health
    ```
  </Step>
</Steps>

## Security

`praisonai serve agents` drives YAML-defined tools (including `execute_command`), so binding it to a non-localhost interface without authentication is refused.

<Warning>
  Binding to any host other than `127.0.0.1` / `localhost` **requires** an API key. The server exits immediately (`SystemExit`) if neither `--api-key` nor `PRAISONAI_SERVE_API_KEY` is set.
</Warning>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Localhost — no key required
praisonai serve agents --port 8000

# Remote — key required (via env var)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# Or via flag
praisonai serve agents --port 8000 --host 0.0.0.0 --api-key "your-secret"
```

Clients then send the key in the `Authorization: Bearer …` header (mirrors `jobs/server.py`).

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://SERVER_IP:8000/agents \
  -H "Authorization: Bearer your-secret" \
  -H "Content-Type: application/json" \
  -d '{"query": "What is AI?"}'
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Start[praisonai serve agents] --> Host{Host is<br/>localhost?}
    Host -->|Yes| Run[Start — no key needed]
    Host -->|No| Key{--api-key or<br/>PRAISONAI_SERVE_API_KEY?}
    Key -->|Set| Auth[Start — Bearer auth enforced]
    Key -->|Missing| Exit[SystemExit — refuse to bind]

    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef stop fill:#8B0000,stroke:#7C90A0,color:#fff

    class Host,Key decision
    class Run,Auth ok
    class Exit stop
```

## Python - Single Agent

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are a helpful assistant.",
    llm="gpt-4o-mini"
)
agent.launch(path="/ask", port=8000, host="127.0.0.1")  # loopback default; set "0.0.0.0" to expose (requires a token)
```

**Expected Output:**

```
🚀 Agent 'Assistant' available at http://127.0.0.1:8000
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docs
🔌 Available endpoints: /ask
```

### Cleaning up `.launch()` endpoints

Call `Agent.close()` when you're done serving an agent — the launched HTTP endpoint is torn down, its route is removed from the FastAPI app, and the cached OpenAPI schema is invalidated so `/docs` and `/openapi.json` no longer advertise it. Only the agent's own `POST` route on that path is dropped; other verbs bound to the same path (e.g. the built-in `GET /health`) are preserved.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(name="Assistant", instructions="You are a helpful assistant.", llm="gpt-4o-mini")
agent.launch(path="/ask", port=8000)

# ... serve for a while ...

agent.close()   # /ask stops accepting requests; /health, /docs still work
```

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant App as FastAPI app
    participant Agent
    participant Router as app.router

    Agent->>App: launch(path="/ask", port=8000)
    App->>Router: POST /ask registered
    Note over Agent,Router: serving...
    Agent->>App: close()
    App->>Router: drop POST /ask only
    App->>App: invalidate openapi_schema
    Router-->>App: /health, /docs preserved

    %% classDef app fill:#189AB4,stroke:#7C90A0,color:#fff
```

<Note>
  Prior to PR [#3810](https://github.com/MervinPraison/PraisonAI/pull/3810), `Agent.close()` cleaned an unrelated registry — the launched endpoint stayed live and the request handler kept the agent object graph alive.
</Note>

## Python - Multi-Agent

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, AgentTeam

researcher = Agent(name="Researcher", instructions="Research topics", llm="gpt-4o-mini")
writer = Agent(name="Writer", instructions="Write content", llm="gpt-4o-mini")

agents = AgentTeam(agents=[researcher, writer])
agents.launch(path="/content", port=8000, host="127.0.0.1")  # loopback default; set "0.0.0.0" to expose (requires a token)
```

**Expected Output:**

```
🚀 Multi-Agent HTTP API available at http://127.0.0.1:8000/content
📊 Available agents for this endpoint (2): Researcher, Writer
🔗 Per-agent endpoints: /content/researcher, /content/writer
✅ FastAPI server started at http://127.0.0.1:8000
📚 API documentation available at http://127.0.0.1:8000/docs
```

<Note>
  Multiple `Agent` / `Agents` instances may call `.launch(port=N)` concurrently from different threads — registration is atomic. If two launch calls use the same path on the same port, the second gets an auto-suffixed path (`/path_abc123`) and a warning is logged. Server readiness is signalled deterministically (no fixed sleep); `.launch()` returns only after the port is accepting connections. The wait defaults to **5 seconds** and is configurable via the `PRAISONAI_SERVER_READY_TIMEOUT` environment variable. If the server doesn't become ready in time, `.launch()` still returns and a warning is logged — check server logs for startup errors.
</Note>

## agents.yaml

```yaml theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
framework: praisonai
topic: research and write content
roles:
  researcher:
    role: Researcher
    goal: Research topics thoroughly
    backstory: Expert researcher
    tasks:
      research_task:
        description: Research the topic
        expected_output: Research findings
  writer:
    role: Writer
    goal: Write engaging content
    backstory: Expert writer
    tasks:
      write_task:
        description: Write based on research
        expected_output: Written content
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export PRAISONAI_SERVE_API_KEY="$(openssl rand -hex 32)"
praisonai serve agents --port 8000 --host 0.0.0.0
```

## CLI Commands

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# Start agents server (localhost default — no key needed)
praisonai serve agents --port 8000

# With custom host for remote access (key required — see Security)
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# With agents file
praisonai serve agents --file agents.yaml --port 8000

# Give the server more time on slow machines
export PRAISONAI_SERVER_READY_TIMEOUT=15
praisonai serve agents --port 8000
```

| Option      | Default       | Description                                                                                                                  |
| ----------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `--port`    | `8000`        | Server port                                                                                                                  |
| `--host`    | `127.0.0.1`   | Server host (use `0.0.0.0` for remote)                                                                                       |
| `--file`    | `agents.yaml` | Agents YAML file                                                                                                             |
| `--reload`  | `false`       | Enable hot reload                                                                                                            |
| `--api-key` | -             | API key. **Required when `--host` is not `127.0.0.1` / `localhost` / `::1`.** Can also be set via `PRAISONAI_SERVE_API_KEY`. |

## launch() Parameters

| Parameter  | Type | Default     | Description                                                                                                |
| ---------- | ---- | ----------- | ---------------------------------------------------------------------------------------------------------- |
| `path`     | str  | `/`         | API endpoint path                                                                                          |
| `port`     | int  | `8000`      | Server port                                                                                                |
| `host`     | str  | `127.0.0.1` | Server host. Loopback by default; set `"0.0.0.0"` to expose, which requires `PRAISONAI_LAUNCH_AUTH_TOKEN`. |
| `debug`    | bool | `False`     | Debug mode                                                                                                 |
| `protocol` | str  | `http`      | `http` or `mcp`                                                                                            |

## Endpoints

| Endpoint               | Method | Description                                                                                                             |
| ---------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `/agents`              | POST   | Send query to all agents — runs through the cached generator (ToolResolver, tool\_timeout, approval, guardrails, retry) |
| `/agents/{agent_name}` | POST   | Call a specific agent through the **same** pipeline as `/agents`. Returns `404` for an unknown name                     |
| `/{path}`              | POST   | Send query to agent(s)                                                                                                  |
| `/{path}/list`         | GET    | List available agents                                                                                                   |
| `/{path}/{agent_id}`   | POST   | Call specific agent                                                                                                     |
| `/health`              | GET    | Health check                                                                                                            |
| `/docs`                | GET    | Swagger UI                                                                                                              |

<Note>
  `/agents` and `/agents/{agent_name}` share one pipeline. The named-agent route previously used a hand-rolled agent that dropped every safety/reliability field; both routes now apply identical YAML lowering.
</Note>

## Example Request/Response

**Request:**

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://localhost:8000/ask \
  -H "Content-Type: application/json" \
  -d '{"query": "What is AI?"}'
```

**Response:**

```json theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
{
  "response": "Artificial intelligence (AI) refers to..."
}
```

## Remote Access

Use `host="0.0.0.0"` to allow remote connections. For `praisonai serve agents`, a non-localhost bind **requires** an API key (see [Security](#security)):

<Warning>
  `praisonai serve agents --host 0.0.0.0` without `--api-key` / `PRAISONAI_SERVE_API_KEY` exits immediately with `SystemExit`. Set a key before binding remotely.
</Warning>

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# CLI — key required for non-localhost
export PRAISONAI_SERVE_API_KEY="your-secret"
praisonai serve agents --port 8000 --host 0.0.0.0

# Python launch() — set a token before exposing publicly
export PRAISONAI_LAUNCH_AUTH_TOKEN="$(openssl rand -base64 32)"
agent.launch(path="/ask", port=8000, host="0.0.0.0")  # binding non-loopback without a token auto-generates one
```

Connect from remote (include the Bearer key when serving `agents`):

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
curl -X POST http://SERVER_IP:8000/ask \
  -H "Authorization: Bearer your-secret" \
  -H "Content-Type: application/json" \
  -d '{"query": "Hello"}'
```

## How It Works

The `agents` server builds one shared generator per app at startup, then builds a lightweight per-request generator for each call that borrows the cached generator's warm pieces.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Client
    participant App as FastAPI (lifespan)
    participant Cached as Cached Generator
    participant PerReq as Per-request Generator

    App->>Cached: build once at startup<br/>(warm adapter, config_list,<br/>tool_resolver, tool-timeout pool)
    Client->>App: POST /agents or /agents/{name}
    App->>PerReq: build per-request<br/>(own cli_config, borrows cached pieces)
    PerReq->>PerReq: run through ToolResolver, tool_timeout,<br/>approval, guardrails, retry
    PerReq-->>Client: response
    App->>Cached: close() at shutdown<br/>(owns tool-timeout pool)

    classDef app fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef cached fill:#10B981,stroke:#7C90A0,color:#fff
    classDef perreq fill:#F59E0B,stroke:#7C90A0,color:#fff

    class App app
    class Cached cached
    class PerReq perreq
```

* **Generator cached per app.** A FastAPI `lifespan` builds a single `AgentsGenerator` at startup and `close()`s it at shutdown — no per-request YAML re-parse, framework re-resolution, or fresh 32-worker tool-timeout pool on every call.
* **Truly concurrent.** Each request builds a lightweight per-request `AgentsGenerator` that carries its own `cli_config` and borrows the cached generator's warm, immutable pieces (adapter, `config_list`, `tool_resolver`, tool-timeout thread pool). Concurrent requests never share mutable state and are **not** serialised — throughput now scales with the event loop.
* **Cached pool ownership.** The cached generator still owns and shuts down the tool-timeout thread pool at app shutdown; per-request generators treat it as borrowed and delegate leak accounting / recycling back to the owner (`_get_tool_timeout_executor`, `_note_leaked_worker`, `_timeout_owner_key`).
* **Graceful fallback.** If the cached generator can't be built, the server falls back to the per-request `praisonai.arun` path.
* **Route convergence.** Both `/agents` and `/agents/{agent_name}` run through the same YAML lowering (`ToolResolver`, `tool_timeout`, `approval`, `guardrails`, retry policy). `POST /agents/{agent_name}` returns `404` for an unknown name.

<Note>
  **What changed in PR [#3812](https://github.com/MervinPraison/PraisonAI/pull/3812).** The per-app `asyncio.Lock` that used to serialise every request on the shared `cli_config` is gone. Each request now runs on its own per-request `AgentsGenerator` that borrows the cached generator's warm pieces, so concurrent requests are no longer serialised. Behaviour is otherwise unchanged — same routes, same YAML pipeline.
</Note>

## Environment Variables

| Variable                         | Default | Description                                                                                                                                                                                                                                          |
| -------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PRAISONAI_SERVE_API_KEY`        | -       | API key required to bind `praisonai serve agents` on a non-localhost host. The server exits with `SystemExit` if this is unset and no `--api-key` is passed when `--host` is not localhost. Clients authenticate with `Authorization: Bearer <key>`. |
| `PRAISONAI_SERVER_READY_TIMEOUT` | `5.0`   | Seconds to wait for the FastAPI server to become ready after `.launch()` / `praisonai serve agents`. A warning is logged if exceeded; startup continues.                                                                                             |

## Troubleshooting

| Issue                                                                       | Fix                                                                                                                                                                      |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--api-key ... is required when binding to a non-localhost host` at startup | You bound to a non-localhost host without a key. Set `PRAISONAI_SERVE_API_KEY=...` (or `--api-key ...`), or bind to `127.0.0.1` / `localhost` for a private-only server. |
| Port in use                                                                 | `lsof -i :8000` then kill process                                                                                                                                        |
| No agents.yaml                                                              | `praisonai --init "topic"`                                                                                                                                               |
| Missing API key                                                             | `export OPENAI_API_KEY="your-key"`                                                                                                                                       |
| Server exits immediately on `--host 0.0.0.0`                                | Non-localhost bind needs auth: `export PRAISONAI_SERVE_API_KEY="your-secret"` (or pass `--api-key`)                                                                      |
| Missing deps                                                                | `pip install "praisonaiagents[os]"`                                                                                                                                      |
| Connection refused                                                          | Use `host="0.0.0.0"` for remote                                                                                                                                          |
| Firewall blocking                                                           | Open port in firewall                                                                                                                                                    |
| Agent server slow to start / "did not become ready" warning                 | Set `PRAISONAI_SERVER_READY_TIMEOUT=10` (seconds) before launching, or check server logs for the underlying startup error.                                               |

## Related

* [Agents API Reference](../api/agents-api) - Full API documentation
* [Agents MCP](./agents-mcp) - Deploy agents as MCP server
* [Tools MCP](./tools-mcp) - Deploy tools as MCP server
* [Deploy CLI](../cli/index) - Deploy using praisonai deploy
