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

# The HTTP Server

`AgentHttpServer` *(SDK ≥ 0.11.0)* is the HTTP front door for an
[AgentSocketServer](/docs/agent-sdk/internals/the-socket-server): the agent's
streams over Server-Sent Events (**GET** answers a `text/event-stream` response),
and its RPC surface over **POST** to `…/rpc/<method>`. It is a second encoder over
the same server — same frames, same `authorize` / `onAttach` policy, same dispatch
as the WebSocket path — so a plain page with an `EventSource` and `fetch()` needs
no SDK at all.

> [!WARNING]
>
> **SSE arrives in batches on deployed functions.** A streamed response cannot cross the actor boundary on the deployed platform. Each SSE response carries the frames available when the handler returns, waiting up to 200 ms for the first, and ends with `retry: 1000`. The `EventSource` then reconnects and resumes from `Last-Event-ID`, so a deployed agent's SSE behaves as a poll about once a second. The RPC path over POST is unaffected, and SSE streams live in local development.
>
> The WebSocket path is the live one for production. It carries the same frames under the same `authorize` and `onAttach` policy, and `mountAgents` serves it on the same address. The change is `new WebSocket(url)` in place of `new EventSource(url)`, or [AgentClient](/docs/agent-sdk/client-sdk/constructor), which reconnects and resumes on its own.

Construct one over the socket server the actor's [`webSocket`](/docs/edge-compute/stateful-actors/api-reference/base#websocket-ws-req) delegates to, and
route HTTP requests from the actor's [`fetch`](/docs/edge-compute/stateful-actors/api-reference/base#fetch-req) into [`fetch()`](/docs/agent-sdk/internals/http/fetch):

```ts theme={null}
import { Agent, type Env } from "@telnyx/edge-runtime";
import { AgentSocketServer, type AgentServerSocket } from "@telnyx/edge-runtime/agent-socket";
import { AgentHttpServer } from "@telnyx/edge-runtime/agent-http";

export class Conversation extends Agent<Env> {
  private desk = new AgentSocketServer<Record<string, unknown>>(this, {
    getState: () => this.getState(),
    getMessages: () => this.messages.all(),
    authorize: (token) => (token === "secret" ? ["read", "rpc"] : ["read"]),
  });
  private door = new AgentHttpServer(this.desk);

  async webSocket(ws: AgentServerSocket, req: Request): Promise<void> {
    await this.desk.attach(ws, req);
  }

  override fetch(req: Request): Promise<Response> {
    return this.door.fetch(req);
  }
}
```

On the SSE path the credential rides the `token` query parameter — an
`EventSource` cannot set headers — and `subscribe` picks the streams
(`subscribe=state,messages`; omitted = every stream the server offers). Each
server frame becomes one SSE event, and the `id:` carries resume cursors so the
browser's automatic reconnect replays only what it missed. On the RPC path the
credential rides `Authorization: Bearer <token>` (preferred) or the same query
parameter; the JSON-array request body is the argument list, and the response
body is the same `result` / `error` frame a WebSocket caller would receive.

`mountAgents` hands a mounted agent's `GET` and `POST` requests to the actor's `fetch`
on `/agents/<mount>/<name>`. The override above answers them there, and the function
needs no routing code of its own. See [`mountAgents`](/docs/agent-sdk/internals/the-mount).

Two limits, by design: SSE is one-way, so there are no server→client calls
mid-stream (RPC is client-initiated POST only), and there is no ping/pong —
an `EventSource` detects a dropped stream and reconnects by itself.

## The Fetch Entry

`AgentHttpServer.fetch` is the body of the actor's `fetch` entry. It chooses between the RPC and the stream by method and address.

```ts theme={null}
// agent-client/http.ts
async fetch(req) {
  const url = new URL(req.url);
  const parsed = rpcMethodFromPath(url.pathname);
  if (parsed !== undefined) {
    // …
    if (req.method !== "POST") {
      return frameResponse(syntheticError("protocol_error", "RPC requires POST"), 405);
    }
    return this.handleRpc(req, parsed.method);
  }
  if (req.method === "GET")
    return this.handleSse(req);
  return frameResponse(syntheticError("protocol_error", `unsupported method ${req.method}`), 405);
}
```

* [`fetch(req)`](/docs/edge-compute/stateful-actors/api-reference/base#fetch-req) on the actor passes the request the mount forwarded, with its URL and query string intact. `Agent` inherits the base `fetch`, which answers `404`. The example above overrides it.
* [`ActorFetchTimeoutError`](/docs/edge-compute/stateful-actors/api-reference/errors) ends a `fetch` that runs past its 30-second budget. An RPC over `POST` runs inside that `fetch` and shares the budget.
