Studio previewSelf-host Studio locally.Run it

OpenRouter Provider

Updated 3 hours ago • September 22, 2026

@kortyx/openrouter connects Kortyx to OpenRouter's multi-provider model catalog through OpenRouter's official TypeScript SDK.

The application-facing shape is the same as the other Kortyx providers:

import { openrouter } from "@kortyx/openrouter"; import { useReason } from "kortyx"; const result = await useReason({ model: openrouter("anthropic/claude-sonnet-4.6"), input: "Summarize this support ticket.", });

1. Install the package

pnpm add @kortyx/openrouter

2. What the package exports

import { MODELS, PROVIDER_ID, createOpenRouter, openrouter, } from "@kortyx/openrouter";
  • openrouter: default provider selector
  • createOpenRouter(...): factory for explicit application-owned settings
  • PROVIDER_ID: the provider id, currently "openrouter"
  • MODELS: intentionally empty because OpenRouter's catalog changes continuously; the selector accepts arbitrary non-empty model ids

The package also exports jevOutputSchema(...). That helper is specific to TypeSafe Jev and is covered in TypeSafe Jev through OpenRouter.

3. Same usage shape as OpenAI

Only the provider selector and model id change:

import { openai } from "@kortyx/openai"; import { openrouter } from "@kortyx/openrouter"; const directOpenAI = openai("gpt-5.6-luna"); const routedOpenAI = openrouter("openai/gpt-5.6-luna"); const routedClaude = openrouter("anthropic/claude-sonnet-4.6"); const routedGemini = openrouter("google/gemini-3.1-pro");

All are regular Kortyx model refs passed to useReason(...). Normalized call fields keep the same shape:

const result = await useReason({ model: openrouter("anthropic/claude-sonnet-4.6"), system: "Answer concisely.", input: ticket, temperature: 0.2, maxOutputTokens: 800, reasoning: { effort: "high" }, tools, outputSchema, stream: true, emit: true, });

The chosen OpenRouter model and provider route must support the features used by the request. OpenRouter broadens model access; it does not make every model support tools, structured output, reasoning, or media identically.

4. Credentials and explicit configuration

The default openrouter export reads credentials on first use from:

  • OPENROUTER_API_KEY
  • KORTYX_OPENROUTER_API_KEY

Use createOpenRouter(...) when the application owns configuration:

import { createOpenRouter } from "@kortyx/openrouter"; export const openrouter = createOpenRouter({ apiKey: process.env.OPENROUTER_API_KEY, appTitle: "Wolly", httpReferer: "https://workfully.com", });

Supported settings are apiKey, baseUrl, appTitle, httpReferer, appCategories, and a custom fetch implementation. Keep all credentials and provider construction on the server.

5. OpenRouter routing controls

Put OpenRouter-native request fields under providerOptions.openrouter:

const model = openrouter("anthropic/claude-sonnet-4.6", { providerOptions: { openrouter: { models: ["google/gemini-3.1-pro"], provider: { zdr: true, allowFallbacks: true, }, sessionId: "support-session-42", }, }, });

Generic Kortyx fields take precedence for normalized concerns such as temperature, maxOutputTokens, reasoning, responseFormat, and tools.

When tools or a native JSON schema are present, Kortyx defaults OpenRouter's provider.requireParameters to true unless the caller sets it explicitly. This prevents a route from silently selecting an endpoint that does not accept the required parameters.

6. Structured output and tools

Use the normal Kortyx APIs:

import { z } from "zod"; const Summary = z.object({ category: z.string(), summary: z.string(), }); const result = await useReason({ model: openrouter("openai/gpt-5.6-luna"), input: ticket, tools: [lookupCustomer], outputSchema: Summary, stream: false, }); result.output;

OpenRouter tool calls, streamed arguments, structured output, usage, and finish reasons are normalized to the shared Kortyx contracts. Provider-native routing, cost, cache, BYOK, and service-tier details remain available in result.providerMetadata and result.raw.

When OpenRouter reports cost, Kortyx records that provider-reported USD amount as the authoritative generation cost. Kortyx Studio then aggregates it across the run without maintaining a separate rate card for every routed model.

7. Reasoning continuation

OpenRouter reasoning details are retained as private continuation state for later model passes, including tool rounds. Application code should use the normalized result fields instead of manually replaying provider-native reasoning payloads.

8. Validation, warnings, and retries

  • The official OpenRouter SDK validates request and response payloads.
  • Kortyx reports normalized compatibility information through result.warnings.
  • The SDK's independent retry loop is disabled so Kortyx owns retry policy.
  • OpenRouter routing and fallback behavior remains controlled by the request's OpenRouter provider settings.

Kortyx intentionally does not publish a static model-capability registry. Use OpenRouter's current model metadata and test the exact model and provider route used in production.

Supported scope

The adapter supports chat-model text generation through useReason(...), including streaming, function tools, structured output, reasoning controls, and normalized usage when supported by the selected route.

Embeddings, image generation, audio, transcription, file APIs, and arbitrary OpenRouter-hosted endpoints are not exposed by this provider adapter.

TypeSafe Jev is the provider-native exception: it uses OpenRouter's System One endpoint while preserving the normal useReason(...) call shape. Continue with TypeSafe Jev through OpenRouter for its different schema and compatibility rules.

Next steps