Deploy to any runtime

Deploy the CopilotKit runtime on any backend framework — Node.js, Express, Hono, Bun, Deno, Cloudflare Workers, and more.

Copy a prompt that guides your coding agent through CopilotKit setup.

The CopilotKit runtime is framework-agnostic. At its core, it's a pure Fetch handler — a function that takes a Request and returns a Response. This means it runs natively on any platform that supports the Fetch API, and thin adapters are provided for Node.js-based frameworks like Express and Hono.

Quick Overview#

RuntimeImport PathKey Function
Fetch-native (Bun, Deno, CF Workers, Next.js App Router)@copilotkit/runtime/v2createCopilotRuntimeHandler
Node.js HTTP@copilotkit/runtime/v2/nodecreateCopilotNodeHandler / createCopilotNodeListener
Express@copilotkit/runtime/v2/expresscreateCopilotExpressHandler
Hono@copilotkit/runtime/v2/honocreateCopilotHonoHandler

Multi-Route vs Single-Route#

The runtime supports two endpoint modes:

  • Multi-route (default) — exposes individual URL endpoints:

    • GET {basePath}/info
    • POST {basePath}/agent/:agentId/run
    • POST {basePath}/agent/:agentId/connect
    • POST {basePath}/agent/:agentId/stop/:threadId
    • POST {basePath}/transcribe
  • Single-route — a single POST {basePath} endpoint that accepts a JSON envelope:

    { "method": "agent/run", "params": { "agentId": "default" }, "body": { ... } }

Both modes use the same Runtime handlers. A current client and Runtime also carry Intelligence thread, memory, and annotation operations through the single endpoint. They negotiate this support through Runtime info, so older clients and servers keep their existing behavior.

Single-route mode is useful when your platform only allows one route handler, or when you prefer a simpler URL structure.

Fetch-Native Runtimes#

If your platform supports the Fetch API natively (Bun, Deno, Cloudflare Workers, etc.), you can use createCopilotRuntimeHandler directly — no adapter needed.

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

// 1. Create the runtime with your agent(s)
const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// 2. Create a Fetch handler — takes Request, returns Response
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// 3. Serve it (Bun example — works the same with Deno.serve, CF Workers, etc.)
Bun.serve({ fetch: handler, port: 4000 });

For framework-specific examples, see the sections below.

Bun#

server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

// Configure the runtime with your agent(s)
const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Create a Fetch handler with multi-route endpoints (GET /info, POST /agent/:id/run, etc.)
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// Bun natively supports Fetch handlers
Bun.serve({ fetch: handler, port: 4000 });
server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route mode: one POST endpoint that dispatches via JSON envelope
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
  cors: true,
});

Bun.serve({ fetch: handler, port: 4000 });
server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const copilotHandler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

Bun.serve({
  port: 4000,
  fetch(request) {
    const url = new URL(request.url);

    // Route CopilotKit requests to the handler
    if (url.pathname.startsWith("/api/copilotkit")) {
      return copilotHandler(request);
    }

    // Your other routes
    if (url.pathname === "/health") {
      return Response.json({ status: "healthy" });
    }

    return Response.json({ status: "ok" });
  },
});

Deno#

server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// Deno.serve natively accepts a Fetch handler
Deno.serve({ port: 4000 }, handler);
server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route mode: one POST endpoint that dispatches via JSON envelope
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
  cors: true,
});

Deno.serve({ port: 4000 }, handler);
server.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const copilotHandler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

Deno.serve({ port: 4000 }, (request) => {
  const url = new URL(request.url);

  // Route CopilotKit requests to the handler
  if (url.pathname.startsWith("/api/copilotkit")) {
    return copilotHandler(request);
  }

  // Your other routes
  if (url.pathname === "/health") {
    return Response.json({ status: "healthy" });
  }

  return Response.json({ status: "ok" });
});

Cloudflare Workers#

src/index.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

export interface Env {
  OPENAI_API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const runtime = new CopilotRuntime({
      agents: {
        default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
      },
    });

    // Workers use the Fetch API natively — handler plugs in directly
    const handler = createCopilotRuntimeHandler({
      runtime,
      basePath: "/api/copilotkit",
      cors: true,
    });

    return handler(request);
  },
};
src/index.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

export interface Env {
  OPENAI_API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const runtime = new CopilotRuntime({
      agents: {
        default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
      },
    });

    // Single-route: one POST endpoint with JSON envelope dispatch
    const handler = createCopilotRuntimeHandler({
      runtime,
      basePath: "/api/copilotkit",
      mode: "single-route",
      cors: true,
    });

    return handler(request);
  },
};
src/index.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

export interface Env {
  OPENAI_API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Route CopilotKit requests to the handler
    if (url.pathname.startsWith("/api/copilotkit")) {
      const runtime = new CopilotRuntime({
        agents: {
          default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
        },
      });

      const handler = createCopilotRuntimeHandler({
        runtime,
        basePath: "/api/copilotkit",
        cors: true,
      });

      return handler(request);
    }

    // Your other routes
    if (url.pathname === "/health") {
      return Response.json({ status: "healthy" });
    }

    return Response.json({ status: "ok" });
  },
};

Next.js App Router#

app/api/copilotkit/[...path]/route.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// Export every method used by the multi-route Runtime.
export { handler as GET, handler as POST, handler as PATCH, handler as DELETE };
app/api/copilotkit/[[...slug]]/route.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route: only POST needed, no catch-all [...path] required
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
});

export { handler as POST };

In single-route mode, no [...path] catch-all is needed — everything goes through a single POST /api/copilotkit.

No cors: true needed for Next.js — same-origin requests don't require CORS headers.

React Router (Framework Mode)#

React Router v7 in framework mode uses file-based routing with Fetch API handlers — the CopilotKit handler works directly as a resource route.

app/routes/api.copilotkit.$.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";
import type { Route } from "./+types/api.copilotkit.$";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// loader handles GET requests (e.g. /info)
export function loader({ request }: Route.LoaderArgs) {
  return handler(request);
}

// action handles POST requests (e.g. /agent/:id/run)
export function action({ request }: Route.ActionArgs) {
  return handler(request);
}

The $ splat segment in the filename (api.copilotkit.$.ts) captures all subpaths under /api/copilotkit/, allowing the CopilotKit router to handle /info, /agent/:id/run, etc.

app/routes/api.copilotkit.ts
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";
import type { Route } from "./+types/api.copilotkit";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route: only action (POST) needed, no splat segment required
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
});

export function action({ request }: Route.ActionArgs) {
  return handler(request);
}

In single-route mode, no $ splat is needed — the filename is just api.copilotkit.ts and all dispatch happens via the JSON envelope.

TanStack Start#

TanStack Start uses API routes that receive standard Request objects and return Response objects.

app/routes/api/copilotkit/$.ts
import { createAPIFileRoute } from "@tanstack/react-start/api";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// The $ splat matches all subpaths and forwards every Runtime method.
export const APIRoute = createAPIFileRoute("/api/copilotkit/$")({
  GET: ({ request }) => handler(request),
  POST: ({ request }) => handler(request),
  PATCH: ({ request }) => handler(request),
  DELETE: ({ request }) => handler(request),
});

The $ in the filename is TanStack Start's splat parameter, matching all subpaths under /api/copilotkit/.

app/routes/api/copilotkit.ts
import { createAPIFileRoute } from "@tanstack/react-start/api";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route: only POST needed, no $ splat required
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
});

export const APIRoute = createAPIFileRoute("/api/copilotkit")({
  POST: ({ request }) => handler(request),
});

In single-route mode, no $ splat is needed — the file is copilotkit.ts (not $.ts) and only POST is required.

Node.js HTTP#

For vanilla Node.js HTTP servers, use the /node subpath which bridges Fetch to Node's IncomingMessage/ServerResponse.

server.ts
import { createServer } from "node:http";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// createCopilotNodeListener returns a Node request listener (req, res) => void
// that bridges to the Fetch handler internally
const listener = createCopilotNodeListener({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

createServer(listener).listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import { createServer } from "node:http";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const listener = createCopilotNodeListener({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
  cors: true,
});

createServer(listener).listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import { createServer } from "node:http";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotNodeHandler } from "@copilotkit/runtime/v2/node";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// 1. Create the Fetch handler
const copilotHandler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// 2. Wrap it as a Node (req, res) handler
const copilotNodeHandler = createCopilotNodeHandler(copilotHandler);

// 3. Use it in your existing server with manual routing
const server = createServer(async (req, res) => {
  const url = new URL(req.url ?? "/", `http://${req.headers.host}`);

  // Route CopilotKit requests to the handler
  if (url.pathname.startsWith("/api/copilotkit")) {
    return copilotNodeHandler(req, res);
  }

  // Your other routes
  if (url.pathname === "/health") {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({ status: "healthy" }));
    return;
  }

  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify({ status: "ok" }));
});

server.listen(4000, () => {
  console.log("Listening at http://localhost:4000");
});

Express#

The Express adapter returns an Express Router that you mount with app.use().

npm install express cors
server.ts
import express from "express";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotExpressHandler } from "@copilotkit/runtime/v2/express";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const app = express();

// Mount the CopilotKit router — creates Express routes under /api/copilotkit
app.use(
  createCopilotExpressHandler({
    runtime,
    basePath: "/api/copilotkit",
    cors: true,
  }),
);

app.listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import express from "express";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotExpressHandler } from "@copilotkit/runtime/v2/express";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const app = express();

// Single-route: one POST endpoint that dispatches via JSON envelope
app.use(
  createCopilotExpressHandler({
    runtime,
    basePath: "/api/copilotkit",
    mode: "single-route",
    cors: true,
  }),
);

app.listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import express from "express";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotExpressHandler } from "@copilotkit/runtime/v2/express";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const app = express();

// Your existing routes
app.get("/", (req, res) => {
  res.json({ status: "ok" });
});

app.get("/health", (req, res) => {
  res.json({ status: "healthy" });
});

// Mount CopilotKit alongside your existing routes
app.use(
  createCopilotExpressHandler({
    runtime,
    basePath: "/api/copilotkit",
    cors: true,
  }),
);

app.listen(4000, () => {
  console.log("Listening at http://localhost:4000");
});

Express Options#

OptionTypeDefaultDescription
runtimeCopilotRuntimerequiredThe runtime instance
basePathstringrequiredURL path prefix (e.g. "/api/copilotkit")
mode"multi-route" | "single-route""multi-route"Multi-route exposes individual endpoints; single-route uses a JSON envelope
corsboolean | CorsOptionsfalseCORS configuration. true for permissive defaults, or pass a cors options object
hooksCopilotRuntimeHooksLifecycle hooks

The Express adapter is compatible with express.json() body parsing. If you have app.use(express.json()) before the CopilotKit router, the adapter will detect the pre-parsed body and handle it correctly.

Hono#

The Hono adapter returns a Hono app that you mount with app.route().

npm install
server.ts
import { serve } from "@hono/node-server";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotHonoHandler } from "@copilotkit/runtime/v2/hono";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Returns a Hono app with CopilotKit routes mounted at basePath
const endpoint = createCopilotHonoHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// Serve the Hono app directly
serve({ fetch: endpoint.fetch, port: 4000 }, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import { serve } from "@hono/node-server";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotHonoHandler } from "@copilotkit/runtime/v2/hono";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route: one POST endpoint that dispatches via JSON envelope
const endpoint = createCopilotHonoHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
});

serve({ fetch: endpoint.fetch, port: 4000 }, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});
server.ts
import { Hono } from "hono";
import { serve } from "@hono/node-server";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotHonoHandler } from "@copilotkit/runtime/v2/hono";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const app = new Hono();

// Your existing routes
app.get("/", (c) => c.json({ status: "ok" }));
app.get("/health", (c) => c.json({ status: "healthy" }));

// Mount CopilotKit as a sub-app via app.route()
app.route("/", createCopilotHonoHandler({ runtime, basePath: "/api/copilotkit" }));

serve({ fetch: app.fetch, port: 4000 }, () => {
  console.log("Listening at http://localhost:4000");
});

Elysia (Bun)#

Elysia runs on Bun and supports the Fetch API natively, so you use createCopilotRuntimeHandler directly.

bun add elysia
server.ts
import { Elysia } from "elysia";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Create a Fetch handler — Elysia passes the raw Request through
const handler = createCopilotRuntimeHandler({ runtime, cors: true });

new Elysia()
  .all("/api/copilotkit/*", ({ request }) => handler(request))
  .listen(4000);
server.ts
import { Elysia } from "elysia";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Single-route: one POST endpoint, no wildcard needed
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  mode: "single-route",
  cors: true,
});

new Elysia()
  .post("/api/copilotkit", ({ request }) => handler(request))
  .listen(4000);
server.ts
import { Elysia } from "elysia";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({ runtime, cors: true });

new Elysia()
  // Your existing routes
  .get("/", () => ({ status: "ok" }))
  .get("/health", () => ({ status: "healthy" }))
  // Mount CopilotKit
  .all("/api/copilotkit/*", ({ request }) => handler(request))
  .listen(4000);

Lifecycle Hooks#

All adapters support lifecycle hooks for cross-cutting concerns like authentication, logging, and response modification.

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  hooks: {
    // Before routing — auth, correlation IDs
    onRequest: async ({ request, path, runtime }) => {
      const token = request.headers.get("authorization");
      if (!token) {
        throw new Response("Unauthorized", { status: 401 });
      }
    },

    // After routing — route-specific authorization
    onBeforeHandler: async ({ request, route }) => {
      console.log(`Handling ${route.method} for agent ${route.agentId}`);
    },

    // After handler — modify response headers
    onResponse: async ({ response, request }) => {
      const headers = new Headers(response.headers);
      headers.set("x-request-id", crypto.randomUUID());
      return new Response(response.body, {
        status: response.status,
        headers,
      });
    },

    // On error — custom error responses
    onError: async ({ error, request }) => {
      console.error("Handler error:", error);
    },
  },
});
HookWhenCan modify
onRequestBefore routingThrow Response to short-circuit
onBeforeHandlerAfter routing, before handlerAccess route info (method, agentId, threadId)
onResponseAfter handlerReturn a new Response to replace it
onErrorOn unhandled errorLog or produce a custom error response

CORS Configuration#

Fetch-native runtimes#

Pass cors: true for permissive defaults (Access-Control-Allow-Origin: *), or provide a config object:

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});

Express#

Pass cors: true for permissive defaults, or pass a cors options object:

createCopilotExpressHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});

Hono#

Pass a cors config with explicit origin:

createCopilotHonoHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});

When using credentials: true, you must specify an explicit origin — wildcard (*) is not allowed by the CORS spec.

Connecting Your Frontend#

Once your runtime is running, point your frontend at it:

src/app/app.config.ts
provideCopilotKit({
  runtimeUrl: "http://localhost:4000/api/copilotkit",
})

For same-origin deployments, use a relative path:

src/app/app.config.ts
provideCopilotKit({
  runtimeUrl: "/api/copilotkit",
})