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

# HTTP API

> REST endpoints for the engine and the conversation layer.

## Two APIs

<CardGroup cols={2}>
  <Card title="Engine API" icon="bolt">
    Stateless. Started by `agentctl serve`. Port 8090. Takes a message, returns a response. No session management.
  </Card>

  <Card title="Conversation API" icon="database">
    Stateful. Started by `agent-manager`. Port 8100. Manages conversations, history, and SSE streaming. Also serves the widget.
  </Card>
</CardGroup>

***

## Engine API

Base URL: `http://localhost:8090`

### `GET /health`

```json theme={null}
{ "status": "ok", "system": "Enterprise Knowledge Assistant" }
```

### `POST /invoke`

Send a message and get a response.

```bash theme={null}
curl -X POST http://localhost:8090/invoke \
  -H "Content-Type: application/json" \
  -d '{ "message": "Where is my order #12345?" }'
```

Response:

```json theme={null}
{
  "system_name": "Enterprise Knowledge Assistant",
  "answer": "Your order #12345 shipped yesterday and is arriving Thursday.",
  "visited": ["router", "router/orders_agent"],
  "used_tools": [
    { "name": "get_order_status", "provider": "local", "status": "succeeded", "agent_id": "orders_agent" }
  ]
}
```

Two request headers are recognized:

* `X-Request-ID` — adopted (after sanitization) as the request id for log
  correlation and echoed back in the `X-Request-ID` response header. Minted
  automatically when absent.
* `X-Session-ID` — carried into the run as the conversation id for tracing
  metadata (e.g. the Langfuse session).

### `POST /stream`

Same as `/invoke` but streams Server-Sent Events.

```bash theme={null}
curl -X POST http://localhost:8090/stream \
  -H "Content-Type: application/json" \
  -d '{ "message": "Hello" }'
```

Events (fields with `null` values are omitted from each payload):

```
data: {"type": "answer_delta", "content": "Your ", "used_tools": []}
data: {"type": "answer_delta", "content": "order ", "used_tools": []}
data: {"type": "route", "route": ["router", "router/orders_agent"], "used_tools": []}
data: {"type": "final", "content": "Your order is on the way.", "route": ["router", "router/orders_agent"], "system_name": "Enterprise Knowledge Assistant", "used_tools": [], "input_tokens": 812, "output_tokens": 96}
```

On failure a `{"type": "error", "detail": "..."}` event is emitted.

***

## Conversation API

Base URL: `http://localhost:8100`

Manages multi-turn conversations with history persisted in SQLite (or Postgres via `DATABASE_URL`).

### `POST /conversations`

Create a new conversation. The body is optional; `session_id` and `user_id`
may be supplied.

```bash theme={null}
curl -X POST http://localhost:8100/conversations \
  -H "Content-Type: application/json" \
  -d '{ "user_id": "user-123" }'
```

Response:

```json theme={null}
{ "conversation_id": "0d5a…", "session_id": "0d5a…" }
```

### `POST /conversations/{id}/messages`

Send a message. Prior history is assembled automatically.

```bash theme={null}
curl -X POST http://localhost:8100/conversations/0d5a…/messages \
  -H "Content-Type: application/json" \
  -d '{ "message": "Where is my order?" }'
```

Response:

```json theme={null}
{
  "answer": "Your order shipped yesterday.",
  "visited": ["router", "router/orders_agent"],
  "used_tools": [
    { "name": "get_order_status", "provider": "local", "status": "succeeded", "agent_id": "orders_agent" }
  ]
}
```

Errors: `404` unknown conversation, `429` conversation token budget exceeded.

### `POST /conversations/{id}/messages/stream`

Same as above but streams via SSE. Preferred by the widget. Each event carries
an `event:` name matching its `type`; `null` fields are omitted.

```bash theme={null}
curl -X POST http://localhost:8100/conversations/0d5a…/messages/stream \
  -H "Content-Type: application/json" \
  -d '{ "message": "Where is my order?" }'
```

Events:

```
event: answer_delta
data: {"type": "answer_delta", "content": "Your order "}

event: route
data: {"type": "route", "route": ["router", "router/orders_agent"]}

event: final
data: {"type": "final", "content": "Your order shipped yesterday.", "route": ["router", "router/orders_agent"], "system_name": "…"}

event: done
data: [DONE]
```

On failure an `event: error` with `{"type": "error", "error": "..."}` is
emitted before `done`.

<Note>
  The event schema also declares `tool_started` / `tool_succeeded` /
  `tool_failed` types for per-tool progress; the engine does not emit them yet.
</Note>

### `GET /conversations/{id}/messages`

Retrieve message history for a conversation.

```bash theme={null}
curl http://localhost:8100/conversations/0d5a…/messages
```

Response:

```json theme={null}
[
  { "role": "user", "content": "Where is my order?", "created_at": "2026-07-02T10:00:00Z" },
  { "role": "assistant", "content": "Your order shipped yesterday.", "created_at": "2026-07-02T10:00:03Z" }
]
```

***

## Passing context to plugins

The HTTP layer currently forwards a deliberately small amount of caller
context into the run:

* `X-Session-ID` (Engine API) becomes the run's conversation id, visible to
  tracing.
* `user_id` (Conversation API request bodies) is attached to the run context
  and persisted with the conversation.

Populating `run_context.auth_context` (scopes, roles, inbound access token)
from request headers is part of the security/context gate that is not wired
up yet — see the roadmap. Hooks such as `on_run_start` can inject a modified
`RunContext` in the meantime.
