Open Kortyx on GitHub

Quickstart (Next.js API Route)

Updated 6 hours ago • May 22, 2026

This quickstart matches the current OSS implementation and mirrors examples/kortyx-nextjs-chat-api-route.

Good to know: Use this path when you need live token/chunk updates in the UI.

1. Create a workflow

src/workflows/general-chat.workflow.ts
import { defineWorkflow } from "kortyx"; import { google } from "@/lib/providers"; import { chatNode } from "@/nodes/chat.node"; export const generalChatWorkflow = defineWorkflow({ id: "general-chat", version: "1.0.0", description: "Single-node chat workflow", nodes: { chat: { run: chatNode, params: { model: google("gemini-2.5-flash"), temperature: 0.3, }, }, }, edges: [ ["__start__", "chat"], ["chat", "__end__"], ], });

2. Add provider entrypoint

src/lib/providers.ts
export { google } from "@kortyx/google";

Good to know: The default google export reads GOOGLE_API_KEY, GEMINI_API_KEY, GOOGLE_GENERATIVE_AI_API_KEY, KORTYX_GOOGLE_API_KEY, or KORTYX_GEMINI_API_KEY on first use. If you want explicit provider setup or custom transport settings, replace this with createGoogleGenerativeAI(...).

This src/lib/providers.ts file is a re-export only. In files where you call google("...") directly, import google from @/lib/providers or from @kortyx/google; do not expect export { google } from "@kortyx/google" to create a local variable in the same file.

3. Create a node

src/nodes/chat.node.ts
import type { ProviderModelRef } from "kortyx"; import { useReason } from "kortyx"; import { google } from "@/lib/providers"; export type ChatNodeParams = { model?: ProviderModelRef; temperature?: number; system?: string; }; export const chatNode = async ({ input, params, }: { input: unknown; params: ChatNodeParams; }) => { const { model = google("gemini-2.5-flash"), temperature = 0.3, system = "", } = params; const res = await useReason({ id: "chat", model, system: system || "You are a concise assistant.", input: String(input ?? ""), temperature, emit: true, // publish text events stream: true, // token-by-token output }); return { data: { text: res.text }, }; };

4. Wire an agent

src/lib/kortyx-client.ts
import { createAgent } from "kortyx"; import { generalChatWorkflow } from "@/workflows/general-chat.workflow"; export const agent = createAgent({ workflows: [generalChatWorkflow], defaultWorkflowId: "general-chat", });

5. Add an API route

src/app/api/chat/route.ts
import { createChatRouteHandler } from "kortyx"; import { agent } from "@/lib/kortyx-client"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; const handleChat = createChatRouteHandler({ agent }); export async function POST(request: Request): Promise<Response> { return handleChat(request); }

Good to know: Request body supports optional stream (default true). Send { stream: false } to receive buffered JSON { chunks, text, structured } instead of SSE. If you only want raw chunks, use collectStream(...) in a custom route.

6. Call /api/chat from client code

For React apps, the recommended client path is @kortyx/react.

src/app/page.tsx
"use client"; import { createRouteChatTransport, useChat } from "@kortyx/react"; import { ChatWindow } from "@/components/chat-window"; export default function Home() { const chat = useChat({ transport: createRouteChatTransport({ endpoint: "/api/chat", }), }); return <ChatWindow chat={chat} />; }

useChat(...) gives you:

  • messages for finalized chat history
  • streamContentPieces for the current in-flight assistant response
  • isStreaming
  • interrupt resume handling
  • default browser storage

Good to know: This is the same pattern used by examples/kortyx-nextjs-chat-api-route. If you need lower-level chunk wiring, see SSE (API Routes) or React Client.

7. Run

GOOGLE_API_KEY=your_key_here pnpm dev

What this gives you

  • Type-safe workflow definition
  • Explicit provider bootstrap at app level
  • Node-level model control via useReason(...)
  • React-first client chat state with useChat(...)
  • API-route transport with createRouteChatTransport(...)
  • Streaming chunks (text-start, text-delta, text-end, message, done)
  • Built-in interrupt/resume path when your nodes use useInterrupt

Next: