Quickstart
This page shows how to run Vela preflight checks in a Next.js server route and call AI SDK streamText() only when execution is allowed. The key point is that blocked / deny paths do not call the model runtime, and only the allow path uses the relay-backed model.
1. Packages
Section titled “1. Packages”pnpm add @vel4ai/sdk @vel4ai/ai-sdk ai@vel4ai/sdk is required. @vel4ai/ai-sdk is an optional adapter that bundles the provider and callbacks for AI SDK streamText().
2. Environment Variables
Section titled “2. Environment Variables”VELA_APP_ID=app_xxxVELA_APP_TOKEN=vela_sk_xxx# Only when using a local or preview APIVELA_BASE_URL=http://localhost:4000Do not make VELA_CUSTOMER_TENANT_ID and VELA_DELEGATION_ID required production environment variables. Resolve execution scope from the Hosted Connect return URL, database, session, or request body.
3. Route Handler
Section titled “3. Route Handler”import { createVelaStreamTextInput } from "@vel4ai/ai-sdk";import { createVelaClientFromEnvironment, createVelaEnvironmentDiagnosticsResponse, createVelaErrorResponse, createVelaIntegration, getVelaEnvironmentDiagnostics, resolveVelaExecutionScope,} from "@vel4ai/sdk";import { streamText } from "ai";
declare const process: { env: Record<string, string | undefined> };declare function toModelMessages(messages: unknown): Promise<unknown[]>;
const CHAT_ESTIMATED_COST_USD = 0.05;
export async function POST(req: Request): Promise<Response> { const environment = getVelaEnvironmentDiagnostics({ env: process.env });
if (!environment.ok) { return createVelaEnvironmentDiagnosticsResponse(environment); }
let reporter: | ReturnType<import("@vel4ai/sdk").PreparedExecutionAllowed["createResultReporter"]> | null = null;
try { const body = (await req.json()) as { messages?: unknown; vela?: unknown }; const executionScope = resolveVelaExecutionScope(body.vela);
if (!executionScope) { return Response.json( { error: "Vela execution scope is missing." }, { status: 400 }, ); }
const client = createVelaClientFromEnvironment({ env: process.env }); const vela = createVelaIntegration({ client, executionScope }); const prepared = await vela.prepareExecutionWithResponse({ estimatedCostUsd: CHAT_ESTIMATED_COST_USD, });
if (prepared.outcome !== "allow") { return prepared.response; }
reporter = prepared.createResultReporter({ onReportError(error) { console.error("Failed to record the Vela execution result", error); }, });
const result = streamText( createVelaStreamTextInput( prepared, { messages: await toModelMessages(body.messages) }, { reporter }, ), );
return result.toUIMessageStreamResponse(); } catch (error) { await reporter?.tryReportFailedWithError(error);
return createVelaErrorResponse(error, { fallbackMessage: "Vela chat failed unexpectedly.", }); }}4. Implementation Checks
Section titled “4. Implementation Checks”- Return missing env before reading the request body.
- Return 400 for requests without execution scope.
- Return
prepared.responseunchanged whenprepared.outcome !== "allow". - Call
streamText()only afterallow. - Share the same reporter between stream callbacks and the route setup
catch.