---
title: eve
description: The default HTTP API for an agent, covering session routes, auth, and customization.
---

# eve



The eve channel is the framework's default HTTP API. It's what the terminal UI, [`useEveAgent`](../guides/frontend/overview), `curl`, and any SDK client talk to when they start sessions, send messages, and stream events. The selected `channels/eve.ts` source owns the complete `/eve/v1` surface, including health, inspection, callbacks, task input, and session routes. eve supplies that source when `agent/channels/eve.ts` does not exist.

Every running eve app exposes its own API. `eve.dev` publishes framework documentation; it is not a shared API, authorization server, MCP server, or A2A server. Each deployment supplies its own host and authentication policy.

Reach for it when something needs HTTP access to your agent, including local tooling, a browser frontend, the terminal UI, or another API client. Most apps never write this file. Add `agent/channels/eve.ts` only to override the defaults, usually the route auth policy.

```ts title="agent/channels/eve.ts"
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";

export default eveChannel({
  auth: [vercelOidc(), localDev()],
});
```

## Routes

The default eve channel inspects the agent, creates sessions, accepts callbacks and task input, controls sessions, and streams events. Its public routes include:

* `GET /eve/v1/health` (check whether the application is reachable)
* `GET /eve/v1/info` (inspect the agent)
* `POST /eve/v1/session` (start a session and send its first message)
* `POST /eve/v1/session/:sessionId` (send a follow-up)
* `POST /eve/v1/session/:sessionId/cancel` (cancel the in-flight turn)
* `POST /eve/v1/session/:sessionId/clear` (clear the session's model history)
* `POST /eve/v1/session/:sessionId/compact` (compact the session's context)
* `POST /eve/v1/session/:sessionId/reset` (retire the session)
* `GET /eve/v1/session/:sessionId/stream` (stream events as NDJSON)

The session routes use only durable session IDs. Create a session explicitly, then put its returned ID in every follow-up, control, and stream path.

`GET /eve/v1/health` is public and returns `{ ok: true, status: "ready", workflowId: string }`. `GET /eve/v1/info` uses the channel's auth policy and returns agent-info version 4. The TypeScript client validates both successful payloads: malformed health JSON throws `HealthResponseError`, malformed inspection JSON throws `AgentInfoResponseError`, and a non-success response from either route throws `ClientError`.

### Start and continue a session

Start a session with an initial message, then use the returned `sessionId` for every follow-up and control operation:

```bash
curl -X POST https://<deployment>/eve/v1/session \
  -H "Content-Type: application/json" \
  -d '{"message":"What is the weather in Paris?"}'
# {"ok":true,"sessionId":"wrun_A","status":"accepted"}
```

The `202` response means Workflow accepted the durable run. The route does not wait for command
inbox or continuation-token ownership. The inbox may still be starting, so an immediate follow-up
or control request can report that the session is not active. The event-stream client retries the
brief window before the new run becomes readable.

Authenticated callers that may retry a create request can pass their own `operationId` for
create-once semantics. Once the operation owner is active, the same operation under the same
authenticated principal returns that session instead of dispatching the input again. The create
route does not wait for a concurrently starting request to publish ownership: simultaneous
requests can receive different accepted candidate IDs, while only the candidate that claims the
operation runs its first turn. Retry the operation after startup when you need its canonical
session ID. Anonymous callers cannot use `operationId`, and operation ownership expires when the
session is no longer resumable.

```bash
curl -X POST https://<deployment>/eve/v1/session \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"message":"What is the weather in Paris?","operationId":"order-4213-research"}'
# {"ok":true,"sessionId":"wrun_A","status":"accepted"}
```

The first request requires `message`. A follow-up request accepts exactly one of
`message` or `inputResponses`; use the latter to answer a pending HITL request:

```bash
curl -X POST https://<deployment>/eve/v1/session/wrun_A \
  -H "Content-Type: application/json" \
  -d '{"inputResponses":[{"requestId":"req_A","optionId":"approve"}]}'
```

Follow-up messages use `turnPolicy: "steer"` by default. If a turn is active, eve buffers the message before cooperatively cancelling that turn, then starts the follow-up as a replacement turn with a new turn ID. Set `turnPolicy: "queue"` on `eveChannel(...)` when follow-ups should wait for active turns to finish. `inputResponses` never steer.

Sending a message to an unknown, terminal, or not-yet-active session ID returns `409` with
`{"code":"session_not_active","error":"The session is no longer active.","ok":false}`.
TypeScript clients expose the stable code as `ClientError.code`. The route never
creates or follows a replacement session.

### Stream events

Stream a session as newline-delimited JSON from `GET /eve/v1/session/:sessionId/stream`. The [session protocol](../concepts/sessions-runs-and-streaming#stream-a-session) defines the event set, envelopes, cursors, and reconnection behavior.

### Cancel a turn

Post to `/eve/v1/session/:sessionId/cancel` to request cancellation of the active turn. You can include the observed `turnId` to keep a late request from cancelling a newer turn. Include `tasks: true` to also cancel every background task owned by the session, including while the session is parked. Cancellation is asynchronous; confirm the turn boundary on the stream as `turn.cancelled` followed by `session.waiting`, and inspect task state in a later turn to confirm task cancellation.

See [Cancel the in-flight turn](../concepts/sessions-runs-and-streaming#cancel-the-in-flight-turn) for response statuses, HTTP status codes, subagent cancellation, and race behavior.

### Clear context

Post to `/eve/v1/session/:sessionId/clear` to remove model-message history while preserving the session ID, system prompt, tools, skills, durable state, limits, and sandbox. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).

### Compact context

Post to `/eve/v1/session/:sessionId/compact` to summarize context without sending a user message. The operation waits for an active turn to settle and reports its result on the stream. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).

### Reset a session

Post to `/eve/v1/session/:sessionId/reset` to terminally retire that session. Reset never replaces the ID automatically; create a new session explicitly for a fresh conversation. See [Compact, clear, and reset](../concepts/sessions-runs-and-streaming#compact-clear-and-reset).

## Replace or disable the defaults

An authored `agent/channels/eve.ts` replaces the complete default eve channel. An `eveChannel(...)` replacement keeps the standard route set with your options; a custom `defineChannel(...)` replacement exposes only the routes you declare. Health, inspection, and callbacks do not reappear through a hidden host fallback.

Disable the complete surface by exporting `disableRoute()` at that slot:

```ts title="agent/channels/eve.ts"
import { disableRoute } from "eve/channels";

export default disableRoute();
```

The default home page is a separate `channels/home.ts` source that serves `GET /` and `HEAD /`. Author `agent/channels/home.ts` to replace it or export `disableRoute()` there to remove it without affecting `/eve/v1`.

## CORS

The eve channel leaves CORS untouched by default. Pass `cors: true` to enable
permissive browser CORS with preflight handling, or pass an options object to
narrow origins, methods, and headers. Route auth still runs on the actual
session requests.

Enable or narrow CORS only when browser clients call the channel directly:

```ts title="agent/channels/eve.ts"
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";

export default eveChannel({
  auth: [vercelOidc(), localDev()],
  cors: {
    origin: "https://app.example.com",
    methods: ["GET", "POST"],
    allowedHeaders: ["authorization", "content-type"],
  },
});
```

## Authentication

The `auth` option decides who can call `/eve/v1/info` and the session routes. The built-in helpers cover development and trusted infrastructure:

* `localDev()` accepts requests during local development.
* `vercelOidc()` lets the local CLI reach a deployed agent, and lets other internal deployments from your team call it.

Neither admits browser users or external clients in production. For a public app, wire the channel to your own auth (Clerk, Auth.js, your own OIDC/JWT verification, an API-key verifier, or any custom `AuthFn`). Vercel OIDC is optional; use it only when Vercel-issued deployment tokens are part of your trust model.

`eve init` scaffolds an `agent/channels/eve.ts` with a production placeholder so you replace it before going live. The generated channel checks Vercel OIDC before falling back to localhost access, and includes `placeholderAuth()`, which returns a setup-focused 401 in production until you swap it for real auth. Delete the file and eve selects its default channel source with `[vercelOidc(), localDev(), placeholderAuth()]`, which rejects all production traffic.

For the full auth model and helper list, see [Auth & route protection](../guides/auth-and-route-protection).

## Audience

The eve channel classifies who can observe a session when that session is created:

| Session creator                           | Default audience |
| ----------------------------------------- | ---------------- |
| Anonymous caller                          | `unknown`        |
| `user`, `service`, or `runtime` principal | `private`        |
| Any other principal type                  | `unknown`        |

Anonymous HTTP surfaces are `unknown` by default because reachability does not establish that their content is safe to record. Authenticated `user`, `service`, and `runtime` sessions are private because they belong to one identified party. Other authenticated principal types also remain `unknown`: trace consumers record metadata but omit content by default in preview and production. Set an explicit `audience: "public"` only for intentionally public traffic.

Pass a constant or a function to override the default:

```ts title="agent/channels/eve.ts"
import { none, vercelOidc } from "eve/channels/auth";
import { eveChannel } from "eve/channels/eve";

export default eveChannel({
  auth: [vercelOidc()],
  audience: "private",
});
```

```ts title="agent/channels/eve.ts"
import { vercelOidc } from "eve/channels/auth";
import { eveChannel } from "eve/channels/eve";

export default eveChannel({
  auth: [vercelOidc(), none()],
  audience({ caller, environment }) {
    if (caller.type === "anonymous") return environment === "development" ? "public" : "unknown";
    return "private";
  },
});
```

The function receives `caller`, channel, run mode, and deployment environment. `caller` is either `{ type: "anonymous" }` or a principal projection with `kind`, `authenticator`, and `attributes`. Classification is fixed at session creation; a continuation turn from a different caller does not reclassify the session. When another eve deployment forwards a request without an accepted forwarded trace assertion, the calling deployment's principal is used for classification, so the session remains `private`. A custom [trace policy](../guides/instrumentation-providers#control-inputs-and-outputs) can still admit or deny content independently.

## Customization

Use `onMessage` to add request-specific context before the agent sees the user message, and `events` to observe stream events from sessions this channel created:

```ts title="agent/channels/eve.ts"
import { eveChannel, defaultEveAuth } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";

export default eveChannel({
  auth: [vercelOidc(), localDev()],
  onMessage(ctx, message) {
    const callerId = ctx.eve.caller?.principalId ?? "anonymous";
    return {
      auth: defaultEveAuth(ctx),
      context: [`HTTP caller ${callerId} sent: ${message}`],
    };
  },
  events: {
    "message.completed"(eventData, _channel, ctx) {
      console.log("eve response completed", {
        sessionId: ctx.session.id,
      });
    },
  },
});
```

`onMessage` must return an auth result. Return `title` alongside `auth` to set the title when the dispatch starts a run. A successful canonical eve HTTP message always dispatches and therefore always produces or continues a session.

## Clients

The browser side of this API lives in the [Frontend](../guides/frontend/overview) docs, where `useEveAgent` drives the eve channel from React UI.

For scripts, server-to-server calls, evals, tests, and custom clients, use the [Client SDK](../guides/client/overview). It wraps the ID-addressed session routes, stream cursor, and reconnect loop.

## What to read next

* [Frontend](../guides/frontend/overview): drive the eve channel from browser UI with `useEveAgent`
* [Client SDK](../guides/client/overview): call the eve channel from TypeScript
* [Auth & route protection](../guides/auth-and-route-protection): the route auth policy
* [Sessions, runs & streaming](../concepts/sessions-runs-and-streaming): the routes this channel exposes


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)