> ## 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.

# AgentHttpServer

> The HTTP front door for an AgentSocketServer — the agent's streams over Server-Sent Events, and its RPC surface over POST, reachable from a plain page with no SDK.

`AgentHttpServer` *(SDK ≥ 0.11.0)* is the HTTP front door for an
[AgentSocketServer](/docs/agent-sdk/api-reference/agent-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. Construct one over the socket server the actor's `webSocket`
delegates to, and route HTTP requests from the actor's `fetch` into
[`fetch()`](#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.

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.

## fetch()

> **fetch**(`req`): `Promise`\<`Response`>

Route one HTTP request: `POST …/rpc/<method>` dispatches an RPC, any
other GET opens an SSE stream, anything else is refused with 405.

**Parameters**

| Parameter | Type      | Description          |
| --------- | --------- | -------------------- |
| `req`     | `Request` | The inbound request. |

**Returns**

`Promise`\<`Response`>

The SSE stream, the RPC answer, or an error response.

## handleRpc()

> **handleRpc**(`req`, `method?`): `Promise`\<`Response`>

Dispatch one RPC over POST (see the class doc for the URL contract). The
request body is the argument list: a plain JSON array, or the SDK codec's
encoded form of one (`{"json": […], …}`) when rich values need to
round-trip. The answer is the `result` or `error` frame — the same frame
a WebSocket caller would receive — with the HTTP status mapped from the
error code (the code strings themselves are unchanged).

The call runs through the socket server's own gates, in bootstrap order
— `onConnect`, `authorize` (`Authorization: Bearer` preferred, `token`
query parameter otherwise), `onAttach`, then the `"rpc"` claim gate —
but it holds NO connection: no snapshot source is read for it, it is
never counted as a watcher, and broadcasts never reach it. A gate that
refuses the call answers exactly like a refused connection (see the
class doc's rejection shapes).

**Parameters**

| Parameter | Type      | Description                                                                        |
| --------- | --------- | ---------------------------------------------------------------------------------- |
| `req`     | `Request` | The inbound POST request.                                                          |
| `method?` | `string`  | The method name; parsed from a trailing `/rpc/<method>` path segment when omitted. |

**Returns**

`Promise`\<`Response`>

The call's answer frame as JSON.

## handleSse()

> **handleSse**(`req`): `Promise`\<`Response`>

Serve the agent's streams as one SSE response (see the class doc for the
URL contract and the event format). The connection is negotiated through
the socket server's own bootstrap: `authorize` resolves the token from
the query string (or an `Authorization` header), `onAttach` may veto, and
the subscribed streams replay their backlog past the `Last-Event-ID`
cursors before live pushes begin. A rejected connection answers with the
error frame as JSON (401 for a rejected credential) instead of a stream.

When the response is torn down — the `EventSource` closed, the request
aborted, the network dropped — the subscription is released immediately:
the connection leaves the watcher set and broadcasts stop targeting it.

**Parameters**

| Parameter | Type      | Description              |
| --------- | --------- | ------------------------ |
| `req`     | `Request` | The inbound GET request. |

**Returns**

`Promise`\<`Response`>

A `text/event-stream` response, or an error response.
