TL;DR

  • The official JavaScript SDK requires Node.js 20 or newer.
  • Question IDs become typed keys on the returned answers.
  • Keep side effects in code and use confidence to decide when to review.
  • Pin a tested model version before production deployment.

A small Jev integration needs three things: state, typed questions, and code that decides what to do with the answers. This TypeScript quickstart builds a support-ticket router because the output has a clear closed set and an obvious human fallback.

The example uses TypeSafe’s official JavaScript SDK. It is an integration pattern, not a benchmark or a claim that the sample thresholds will fit another production system.

Install the TypeSafe SDK

The JavaScript SDK documentation requires Node.js 20 or newer.

npm install @typesafe-ai/sdk
export TYPESAFE_API_KEY="your-api-key"

Keep the key on the server. Do not expose it in browser code or commit it to the repository.

Send state and typed questions

Create route-ticket.ts:

import {
  choice,
  noul,
  score,
  TypeSafeClient,
} from "@typesafe-ai/sdk";

const client = new TypeSafeClient();

const response = await client.systemOne({
  model: "jev-latest",
  state: {
    ticket: "I was charged twice and need this fixed today.",
    plan: "annual",
  },
  questions: {
    department: choice("Which team should handle `ticket`?", {
      billing: "Payment, refund, or subscription issue",
      technical: "Product bug or integration issue",
      account: "Login or account access issue",
      other: "None of the listed categories",
    }),
    urgency: score("How urgent is `ticket`?", [
      "Can wait",
      "Needs attention soon",
      "Needs attention today",
    ]),
    asksForRefund: noul("Does `ticket` ask for a refund?"),
  },
});

console.log(response.answers.department.choice);
console.log(response.answers.department.probabilities);
console.log(response.answers.urgency.score);
console.log(response.answers.asksForRefund.noul);

The SDK infers the answer types from the question map. department is a Choice result, urgency is a Score result, and asksForRefund is a Noul result.

Including other protects against an incomplete department list. It does not solve every taxonomy problem, but it gives Jev a valid outcome when the named queues do not fit.

Route with confidence in code

Do not let the model perform the side effect. Let it make the semantic judgment, then keep the action policy in TypeScript.

const department = response.answers.department;
const urgency = response.answers.urgency;

if (department.confidence < 0.6) {
  await sendToHumanReview({
    ticketId: "ticket_123",
    reason: "uncertain_department",
  });
} else {
  await assignQueue("ticket_123", department.choice);
}

if (urgency.confidence >= 0.8 && urgency.score >= 2.5) {
  await setPriority("ticket_123", "urgent");
}

Those values are placeholders for a test harness. The confidence guide says thresholds should reflect the consequence of a wrong answer. A reversible queue assignment can tolerate a different threshold from a refund, account lock, or payment action.

For a full production approach, see how to use Jev confidence and thresholds.

Ask independent questions together

TypeSafe says questions in one request are evaluated independently and in parallel. That makes one call a good fit for multiple facts about the same ticket.

It does not create a decision chain. The urgency answer cannot depend on the selected department inside the same request. If step two needs the result of step one, make the first call, update the state, and make another call.

This distinction keeps the request easier to reason about:

  • Parallel questions inspect the same state from different angles.
  • Sequential calls represent a dependency between decisions.
  • Application code owns the branch between them.

Handle errors and overload

The HTTP API reference documents authentication, validation, rate-limit, and overload responses. The SDK includes retries and backoff defaults, but an application still needs an explicit failure path.

try {
  const result = await client.systemOne(request);
  return routeFrom(result);
} catch (error) {
  logger.error({ error }, "TypeSafe request failed");
  return sendToHumanReview({
    ticketId: request.state.ticketId,
    reason: "model_unavailable",
  });
}

A retry is not a fallback policy. Decide what the product should do if the service remains unavailable. For a support router, holding the item in a manual queue may be acceptable. For a customer-facing interaction, a safe default path may be better.

Pin the model after evaluation

jev-latest currently points to the stable release listed on TypeSafe’s models page. The alias can move later. That means the same application code can receive different model behavior after an update.

Before production:

  1. Build a labeled set from real tickets.
  2. Measure errors by queue and customer consequence.
  3. Tune criteria and thresholds.
  4. Pin the versioned model ID used for that evaluation.
  5. Log the response’s model field with every decision.
  6. Re-run the evaluation before moving versions.

A model version belongs in the same operational conversation as a changed rule or dependency. Treating an alias as permanent hides that change.

Avoid broad questions

The fastest way to make this integration brittle is to ask one question that contains the entire support policy.

Avoid:

noul("Should we automatically solve this customer's issue?")

That mixes eligibility, risk, intent, account state, and the meaning of “solve.” Ask the semantic parts separately, then use exact account data and policy rules in code.

TypeSafe’s Jev 1.13 limitations also recommend keeping arithmetic, counting, and date comparison outside the model. If a refund window closes after 30 days, calculate the dates in TypeScript and pass the resulting eligibility state to Jev only if a separate semantic judgment remains.

What to build next

Once the router works on a local sample, add an evaluation command that runs every labeled case and reports false automation separately from unnecessary review. That tradeoff is more useful than optimizing one headline accuracy number.

Then inspect the full probability distributions for recurring ambiguity. Two queues that stay close together may need better criteria, a different taxonomy, or a deliberate shared review path.

Sources

FAQ

How do I install the Jev JavaScript SDK?
Use npm install @typesafe-ai/sdk on Node.js 20 or newer, then set TYPESAFE_API_KEY in the environment.
Does the Jev TypeScript SDK infer answer types?
Yes. The SDK infers the result types from the Choice, Score, and Noul questions supplied in the request.
Should production code use jev-latest?
It is useful while starting, but TypeSafe recommends pinning a versioned model after thresholds have been tested because aliases can move.