Show HN: TanStack Start and TanStack AI Jev Integration TanStack AI's `decide()` evaluation API can be wired into a TanStack Start server route using the `@tanstack/ai-vercel-gateway` adapter and the `typesafe-ai/jev` model ID to return constrained routing decisions, according to a Show HN post. The integration installs `@tanstack/ai`, `@tanstack/ai-vercel-gateway`, `@vercel/oidc` and `zod`, authenticates through Vercel AI Gateway via OIDC (local tokens expire after 12 hours) or an `AI_GATEWAY_API_KEY`, and keeps credentials server-side so the browser only submits the ticket and receives the result. The example routes an unexpected-invoice message to a billing, technical support or review queue, with the assignment only giving the team a place to begin investigating. Use Jev https://vercel.com/i/what-is-jev in TanStack Start by calling TanStack AI's decide from a server route. The @tanstack/ai-vercel-gateway adapter sends the evaluation through AI Gateway. Your route supplies the evidence and questions, then returns a decision that the interface can display or the application can act on. Copy link to heading what-does-each-part-of-the-integration-do What does each part of the integration do? The Vercel Gateway adapter https://vercel.com/kb/guide/tanstack-ai-vercel-ai-gateway exposes createVercelGatewayDecider for evaluation with an explicit credential. Pair it with decide and the model ID typesafe-ai/jev . Jev returns constrained decisions that the form can display once the evaluation completes. Copy link to heading how-should-you-define-the-routing-decision How should you define the routing decision? Suppose a customer submits a message about an unexpected invoice. The application needs to choose a support queue before anyone investigates the charge. Give Jev the subject and message as evidence, then define the destinations in terms of the work each team owns. Billing handles charges and invoice questions, while technical support investigates product failures. The review option covers requests with unclear or overlapping responsibilities, such as a message that reports a payment error and asks for help restoring product access. Ask which team should handle the request first. The assignment gives the support team a place to begin investigating; it does not establish the cause of the problem or confirm that the customer's account has been checked. Copy link to heading how-do-you-connect-jev-to-a-tanstack-start-application How do you connect Jev to a TanStack Start application? Copy link to heading 1.-install-tanstack-ai-and-the-gateway-adapter 1. Install TanStack AI and the Gateway adapter In an existing TanStack Start React project, install the evaluation packages, the Vercel OIDC helper, and Zod for request validation: npm install @tanstack/ai @tanstack/ai-vercel-gateway @vercel/oidc zod The core package provides decide and the choice helper used below. The Gateway adapter handles authentication and the evaluation request, so this integration does not require a separate TypeSafe API key. Copy link to heading 2.-configure-ai-gateway-authentication 2. Configure AI Gateway authentication Vercel deployments can use OIDC authentication https://vercel.com/docs/ai-gateway/authentication-and-byok/oidc without managing an API key. For local development, link the project and pull an OIDC token into its environment file: vercel linkvercel env pull .env.local Local tokens expire after 12 hours, so refresh the file when the token expires. Alternatively, create an AI Gateway API key and set AI GATEWAY API KEY in your application's environment. An existing API key takes precedence over OIDC; updating the token will not fix a request still using an invalid key. The adapter's vercelGatewayDecider convenience factory reads OIDC tokens only from the environment, which can retain an expired token in a long-running Node.js process. The example instead calls getVercelOidcToken within each request to obtain the current token from Vercel's request context, falling back to VERCEL OIDC TOKEN for local development. It passes the resolved credential to createVercelGatewayDecider . Keep the evaluation in the server route. The browser submits the ticket and receives the result without receiving either credential. Copy link to heading 3.-define-the-question-and-the-routing-rule 3. Define the question and the routing rule Create src/lib/route-ticket.server.ts for the model call. TanStack AI's evaluation API https://tanstack.com/ai/latest/docs/evaluate/evaluate takes an adapter, shared state, and named questions. Each question name becomes a property on the returned result. js import { choice, decide } from '@tanstack/ai';import { createVercelGatewayDecider } from '@tanstack/ai-vercel-gateway';import { getVercelOidcToken } from '@vercel/oidc'; export type Ticket = { subject: string; body: string;}; export async function routeTicket ticket: Ticket { const credential = process.env.AI GATEWAY API KEY || await getVercelOidcToken ; const result = await decide { adapter: createVercelGatewayDecider 'typesafe-ai/jev', credential , state: ticket, questions: { queue: choice { instructions: 'Which team should handle this request first?', options: { billing: 'Invoice amounts, charges, and payment questions', technical: 'Product errors, outages, and integration failures', needs review: 'Unclear requests or overlapping responsibilities', }, } , }, abortSignal: AbortSignal.timeout 10 000 , } ; const { value, probability } = result.queue; const reviewRequired = value === 'needs review' || probability < 0.85; return { proposedQueue: value, selectedProbability: probability, destination: reviewRequired ? 'needs review' : value, reviewRequired, };} Here, result.queue.value is the selected option key. Its probability describes that selected option. The code requires review when Jev selects the review category or when the selected queue falls below the example threshold. The 0.85 cutoff and ten-second deadline are application choices. Establish your threshold with labeled requests and choose a deadline that fits the form's expected response time. Even a high probability for needs review sends the ticket to review, which is why the explicit category check remains separate from the cutoff. The result preserves both the proposed queue and the destination chosen by application code. That distinction lets a support agent see when the model proposed billing but the application required review because the probability was below the threshold. Copy link to heading 4.-add-a-validated-server-route 4. Add a validated server route Create src/routes/api.triage.ts . The TanStack Start server-route convention https://tanstack.com/start/latest/docs/framework/react/guide/server-routes exposes this file at /api/triage and lets the handler return a standard Response . js import { createFileRoute } from '@tanstack/react-router';import { z } from 'zod';import { routeTicket } from '../lib/route-ticket.server'; const ticketSchema = z.object { subject: z.string .trim .min 1 .max 200 , body: z.string .trim .min 1 .max 5 000 ,} ; export const Route = createFileRoute '/api/triage' { server: { handlers: { POST: async { request } = { let input: unknown; try { input = await request.json ; } catch { return Response.json { error: 'Expected a JSON request.' }, { status: 400 } ; } const parsed = ticketSchema.safeParse input ; if parsed.success { return Response.json { error: 'Enter a subject and message within the limits.' }, { status: 400 } ; } try { return Response.json await routeTicket parsed.data ; } catch { return Response.json { error: 'Routing is unavailable. Try again.' }, { status: 503 } ; } }, }, },} ; The schema restricts the model's input to the two fields the question needs. The character limits belong to this form design and do not describe Jev's context capacity. Keep the queue definitions and threshold in application code so a submission cannot replace them. Malformed input returns 400 , and an evaluation failure returns 503 . Completed evaluations return 200 , including those with reviewRequired: true , so the interface can distinguish a request that needs a person's judgment from one that failed to run. This route proposes a destination without saving the ticket. When connecting it to your support system, apply the existing access controls and persist the submission before telling the customer it has been received. If routing is unavailable, that saved ticket can wait for review without forcing the customer to re-enter their message. Copy link to heading 5.-call-the-route-from-a-form 5. Call the route from a form The interface only needs to send the input and display the returned destination. This component can sit inside an existing TanStack Start page: js import { useState, type FormEvent } from 'react'; export function TicketForm { const pending, setPending = useState false ; const message, setMessage = useState '' ; async function submit event: FormEvent