Create your own
Lesson illustration

Authenticating Jev API Requests with the JavaScript SDK

Hello. In the previous lesson, you separated deterministic application responsibilities from narrow Jev judgments: code owns facts, policy, side effects, and branching; Jev supplies bounded semantic decisions over the state you provide.

Now you will make that boundary concrete. By the end of this lesson, you will have a small server-side TypeScript project that loads a TypeSafe API key safely, creates a TypeSafeClient, and sends an authenticated Jev request through the JavaScript SDK. The example uses a single support-ticket classification question so that the first live call stays small and inspectable.


The minimum shape of a Jev call

A Jev request has three conceptual parts:

  1. State: the evidence Jev should judge.
  2. Questions: bounded, typed decisions about that evidence.
  3. Answers: structured results returned by the SDK.
A Jev call sends a state plus one or more typed questions and returns structured answers. Choice answers include a selected option and probabilities; Score and Noul use different typed result shapes.

For this first request, the application will send a short ticket as state and ask one choice question: which department should own it? This follows the partitioning discipline from the previous lesson. Your code chooses the available departments and decides what to do later; Jev classifies the supplied text into that bounded set.

Authentication is deliberately not part of the request object. The SDK client reads the TYPESAFE_API_KEY environment variable from the server process.


Create and protect an API key

Use the TypeSafe console to obtain the API key associated with your early-access account. The console home page includes API Keys in the left navigation.

The TypeSafe AI console home page, with the API Keys navigation item on the left. Use this area to manage the credential that your server will supply as `TYPESAFE_API_KEY`.

Treat this key like a password for an external service:

  • Use it only in server-side code: a Node service, API route, worker, or backend-for-frontend.
  • Never place it in browser JavaScript, a mobile app, a committed config file, a screenshot, or a support ticket.
  • Never log the key or all of process.env.
  • Store it as a deployment secret in your hosting provider when you deploy. A local .env file is for local development only.

Create a local project directory and verify that you are using Node.js 20 or newer:

mkdir jev-first-call
cd jev-first-call

node --version
npm init -y

npm pkg set private=true
npm pkg set type=module
npm pkg set scripts.start="tsx src/route-ticket.ts"
npm pkg set scripts.check="tsc --noEmit"

npm install @typesafe-ai/sdk dotenv
npm install --save-dev typescript tsx @types/node

The TypeSafe JavaScript SDK requires Node.js 20 or newer. tsx gives the project a direct TypeScript execution command for this learning project; your eventual production build can use the build tooling already standard in your application.

Read the official SDK repository’s short quickstart before creating the files below. It establishes the exact package name, environment-variable name, client construction, and systemOne call pattern.

JavaScript and TypeScript SDK for TypeSafe AI. - GitHub

Read the Quickstart in the official TypeSafe AI JavaScript SDK repository. It confirms the minimal authenticated SDK pattern that you will adapt into a local TypeScript project.

In the README’s Quickstart section, read the full quickstart. Follow the installation and credential setup first, then focus on how the TypeSafeClient is constructed and how the choice helper defines a typed question. Notice that the question ID becomes the key used to access the answer.

Create this minimal project structure:

jev-first-call/
  .env
  .env.example
  .gitignore
  package.json
  tsconfig.json
  src/
    route-ticket.ts

Add a strict TypeScript configuration in tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "noEmit": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

Then create .env locally, substituting your actual key:

TYPESAFE_API_KEY=replace_this_with_your_real_key

The variable name matters: the SDK expects TYPESAFE_API_KEY.

Create .env.example as a safe onboarding template for a future clone of the repository:

TYPESAFE_API_KEY=

Finally, add this to .gitignore:

.env
.env.*
!.env.example
node_modules

The exception keeps .env.example shareable while ignoring files that may contain real environment-specific secrets. Before your first commit, run:

git status

You should see source files and .env.example, but not .env.

If a real credential is ever committed, removing the file in a later commit is not enough: assume the key is exposed, revoke or rotate it in the TypeSafe console, then replace it in your deployment secrets and local .env.


Send an authenticated request

Create src/route-ticket.ts:

import "dotenv/config";
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

async function main() {
  if (!process.env.TYPESAFE_API_KEY) {
    throw new Error(
      "Missing TYPESAFE_API_KEY. Add it to .env before running this script."
    );
  }

  const client = new TypeSafeClient();

  const response = await client.systemOne({
    state: {
      ticket: "I was charged twice. Please fix this as soon as possible.",
      customerPlan: "annual",
    },
    questions: {
      category: choice("What is this ticket about?", {
        billing: "Payment, refund, or subscription issue",
        technical: "Product bug or integration issue",
        account: "Login or account access issue",
        other: "None of the listed categories",
      }),
    },
  });

  console.log({
    category: response.answers.category.choice,
  });
}

main().catch((error: unknown) => {
  const message =
    error instanceof Error ? error.message : "Unknown request failure";

  console.error(`Jev request failed: ${message}`);
  process.exitCode = 1;
});

Run a type check, then run the request:

npm run check
npm run start

A successful call prints an object containing one of your allowed category values. For this ticket, billing is the expected practical classification, but the important first success criterion is that the application makes an authenticated request and receives a bounded answer without exposing the credential.

A few details in this small file carry most of the integration pattern:

Code elementResponsibility
import "dotenv/config"Loads local development variables from .env into process.env before the client is created.
process.env.TYPESAFE_API_KEY checkFails clearly before a network call if local setup is incomplete.
new TypeSafeClient()Creates the SDK client, which uses the configured environment credential.
stateContains the evidence relevant to the decision.
choice(...)Declares a closed set of allowed outputs for one semantic judgment.
response.answers.categoryAccesses the answer using the question ID, category.

The return type is inferred from the question declaration. Because the question is a choice with four literal option keys, TypeScript can understand that response.answers.category.choice is one of billing, technical, account, or other, rather than an arbitrary string.

That inference becomes valuable when you connect the result to deterministic routing code:

type Department =
  | "billing"
  | "technical"
  | "account"
  | "other";

function selectQueue(category: Department): string {
  switch (category) {
    case "billing":
      return "support-billing";
    case "technical":
      return "support-technical";
    case "account":
      return "support-account";
    case "other":
      return "support-general";
  }
}

Do not add this routing call to the first script yet. Keep the first live integration focused on proving four things independently:

  1. The package is installed.
  2. The local credential is loaded but not hardcoded.
  3. The SDK can authenticate successfully.
  4. Jev returns a typed answer whose ID matches your declared question.

Diagnose the common first-call failures

Most initial failures are setup issues rather than model issues. Diagnose them in this order.

SymptomLikely causeAction
The script reports Missing TYPESAFE_API_KEY.env is absent, misspelled, in the wrong directory, or uses a different variable nameConfirm the file is in the project root and contains TYPESAFE_API_KEY=... with no placeholder remaining.
Cannot find package '@typesafe-ai/sdk'Dependencies were installed in another directory or installation failedRun npm install from the directory containing package.json.
TypeScript cannot find Node globals such as processNode type declarations are missing or TypeScript configuration is not being readConfirm @types/node is installed and run npm run check from the project root.
Authentication is rejectedThe key is invalid, revoked, copied incompletely, or associated with an account lacking accessRe-copy the key from the console or create a replacement according to the console’s instructions. Do not paste the key into logs or issue trackers.
A browser integration appears to work locally but exposes the key in DevTools or a bundleThe SDK call is running on the clientMove the Jev call behind a backend endpoint or server action. The browser should send only the ticket data it is authorized to provide.

A useful local separation is:

  • Frontend: gathers user input and displays approved application results.
  • Backend: authenticates the user, retrieves trusted state, calls Jev, applies policy, and performs side effects.
  • Jev: answers the declared bounded question from the supplied state.

For example, a browser can post a ticket message to your API route. The API route may add the authenticated customer’s plan from your database, call Jev, and return only the category that the frontend needs. The browser never receives the TypeSafe API key.


Keep the first request deliberately narrow

It is tempting to turn the example into one large request immediately:

“Read this ticket, decide whether the refund is valid, issue it if necessary, and write a reply.”

That would erase the architectural boundary you established in the prior lesson. Instead:

  • Jev can classify the ticket’s topic or determine whether it explicitly asks for a refund.
  • Your backend verifies transactions, applies refund policy, and decides whether automation is allowed.
  • A template or a separate generation system may draft a reply after the decision is made.

For this lesson, the choice question is enough. It demonstrates the complete live path without claiming that a category alone authorizes any action.

In the next lesson, you will inspect the returned answer more carefully: the selected value, the distribution over options, and the confidence signal. That is where a successful API call becomes a confidence-aware application decision rather than a simple classifier invocation.


Key takeaways

You now have the smallest safe JavaScript integration pattern for Jev:

  • Install @typesafe-ai/sdk in a Node.js 20+ server-side TypeScript project.
  • Keep TYPESAFE_API_KEY in local environment configuration, not in source code.
  • Ignore .env, but commit a credential-free .env.example.
  • Construct TypeSafeClient and call client.systemOne with state plus typed questions.
  • Use question IDs as the typed keys for returned answers.
  • Treat a returned answer as input to application policy, never as permission for an uncontrolled side effect.

Next, you will interpret the Jev response in detail, distinguishing a selected choice from its option probabilities and confidence.

Can't find a good explanation? Sign up and we'll make it for you

Sign up