Guides

handleRequest

Expose an agent over HTTP with a single function. Handles session history, streaming, reconnection, and interruption automatically.

handleRequest creates an HTTP handler from your agent that routes incoming requests to the correct session operation. One function replaces four hand-written route handlers.

Quick Start

app/api/agents/my-agent/[[...path]]/route.ts
import { myAgent } from "@/agent";
import { handleRequest } from "experimental-agent";

const handler = handleRequest(myAgent);

export const GET = handler.fetch;
export const POST = handler.fetch;

The folder name (my-agent) must match the name passed to agent("my-agent", ...). This gives you four endpoints automatically:

MethodPathWhat it does
GET/api/agents/my-agent/:sessionIdFetch session message history
POST/api/agents/my-agent/:sessionIdSend a message
GET/api/agents/my-agent/:sessionId/reconnectReconnect to an active stream
POST/api/agents/my-agent/:sessionId/interruptInterrupt the running session

The base path defaults to /api/agents/{name} derived from the agent's name.

With Workflow

For durable execution that survives crashes and timeouts, pass a workflow function:

app/api/agents/my-agent/[[...path]]/route.ts
import { myAgent } from "@/agent";
import { handleRequest, type SessionSendArgs } from "experimental-agent";

function workflow(sessionId: string, ...args: SessionSendArgs<typeof myAgent>) {
  "use workflow";
  return myAgent.session(sessionId).send(...args);
}

const handler = handleRequest(myAgent, { workflow });

export const GET = handler.fetch;
export const POST = handler.fetch;

When workflow is provided, POST /:sessionId starts the workflow via start() from workflow/api and streams from its result. Without a workflow, send() runs in-process.

Overrides

Intercept any route to add auth, logging, or custom logic:

app/api/agents/my-agent/[[...path]]/route.ts
import { myAgent } from "@/agent";
import { handleRequest } from "experimental-agent";

const handler = handleRequest(myAgent, {
  overrides: {
    "session.get": async ({ params }, next) => {
      console.log("loading history for", params.sessionId);
      return await next();
    },
    "session.post": async ({ params, body }, next) => {
      return await next();
    },
    "interrupt.post": async ({ params, body }, next) => {
      return await next();
    },
  },
});

export const GET = handler.fetch;
export const POST = handler.fetch;

Each override receives the parsed params/body and a next function that calls the default handler. Return the result of next() or your own response.

Available override keys:

KeyRoute
session.getGET /:sessionId
session.postPOST /:sessionId
reconnect.getGET /:sessionId/reconnect
interrupt.postPOST /:sessionId/interrupt

Custom Base Path

Override the default /api/agents/{name} convention:

import { myAgent } from "@/agent";
import { handleRequest } from "experimental-agent";

const handler = handleRequest(myAgent, {
  basePath: "/api/v2/chat",
});

export const GET = handler.fetch;
export const POST = handler.fetch;

When using a custom base path, configure the client-side hooks to match — see React Hooks.

When to Use handleRequest vs. Manual Routes

Use handleRequest when you want convention-based routing with minimal setup. Use manual API routes when you need full control over each endpoint's request/response handling, or when your framework doesn't use a catch-all route pattern.

Next Steps

  • React HooksuseAgent, useSessionHistory, useInterruptSession
  • API Routes — Manual route patterns for full control
  • Streaming — Reconnection and status handling