Build with TypeSafe Jev

How to use Jev

Start with one support ticket. Ask three narrow questions, inspect the returned probabilities, and let ordinary code decide where the ticket goes.

Checked October 4, 2026 · Independent developer guide by JevPlay

Why no text generation

I use Jev when the useful result is a typed branch, not a paragraph to paste somewhere else. The System One model evaluates one state against the questions I define and returns structured answers. That keeps the final routing rule in ordinary code, where I can review it and change it without changing the question.

TypeSafe documents three useful question shapes. A Choice selects a category, a Score places the state on ordered descriptive levels, and a Noul question answers a single proposition such as urgency. See the System One guide for the model and question structure.

Jev question types used in this guide
QuestionUse it forInspect in the response
ChoiceOne category from named criteriachoice, probabilities, confidence
ScoreAn ordered rubric with descriptive levelsscore, probabilities, confidence
NoulOne proposition, such as whether a ticket asks for action todaynoul

Get a Jev API key

Create or copy a key from the TypeSafe keys dashboard. I keep it in the server environment under TYPESAFE_API_KEY; the JavaScript client reads that variable when it is constructed.

Set your TypeSafe key in Bash
read -rsp 'TypeSafe API key: ' TYPESAFE_API_KEY
echo
export TYPESAFE_API_KEY

The prompt accepts the value without echoing it. I run the example programs from the same shell and never replace the placeholder in the source code with a real key.

Try the Playground

The official quickstart is a good first pass because it makes the state/question split visible before I write code. Follow these four operations in the Playground:

  1. Open the Playground and log in.
  2. Paste any text as the state.
  3. Add a question. The quickstart uses a Noul question: Does this message express urgency?
  4. Add more questions. Mix Noul, Choice, and Score in one call and see all results at once.
Noul question from the quickstart
{
  "urgency": {
    "type": "noul",
    "instructions": "Does this message express urgency?"
  }
}

For a hands-on Jev example inside JevPlay, try the Chat Intent Analyzer. It is a practical way to see structured intent signals before wiring the same idea into your own endpoint.

Install the JavaScript SDK

The official JavaScript SDK supports Node.js 20 or newer. In this guide I pin the documented client version and use ESM files with a .mjs suffix so the runnable examples have one clear module format.

Install from your project directory
npm install @typesafe-ai/sdk@0.6.0

The SDK documentation covers the client and its typed answers at the JavaScript SDK reference.

Make the first call

Save the following as first-call.mjs. It sends one support ticket as state and asks one Choice question. The client reads TYPESAFE_API_KEY from the environment; no key belongs in this file.

first-call.mjs
import { TypeSafeClient, choice } from "@typesafe-ai/sdk";

// Reads TYPESAFE_API_KEY from your server's environment.
const client = new TypeSafeClient();
const ticket = "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.";

const response = await client.systemOne({
  model: "jev-latest",
  state: ticket,
  questions: {
    department: choice(
      "Which team should handle this support ticket?",
      {
        "billing": "Charges, invoices, or refunds",
        "technical": "Broken software, outages, or integration failures",
        "sales": "Buying, upgrading, or product questions",
        "other": "None of these teams is a clear match"
      }
    )
  }
});

console.log(JSON.stringify(response, null, 2));
Run the first call
node first-call.mjs

The captured one-question call returned HTTP 200 and resolved the request alias to jev-1.13.0. Its response is reproduced here so I can compare the fields my branch will consume:

Observed first-call response
{
  "model": "jev-1.13.0",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "confidence": 1,
      "probabilities": {
        "billing": 0,
        "technical": 1,
        "other": 0,
        "sales": 0
      }
    }
  },
  "usage": {
    "input_tokens": 377,
    "output_tokens": 45
  }
}

Build the full triage request

Next I keep the same state and ask three independent questions: which team should handle the ticket, how quickly it needs attention, and whether the customer explicitly asks for action today. The request and executable program below are the same example in two forms.

triage-request.json
{
  "model": "jev-latest",
  "state": "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this support ticket?",
      "criteria": {
        "billing": "Charges, invoices, or refunds",
        "technical": "Broken software, outages, or integration failures",
        "sales": "Buying, upgrading, or product questions",
        "other": "None of these teams is a clear match"
      }
    },
    "priority": {
      "type": "score",
      "instructions": "How quickly does this ticket need attention?",
      "criteria": [
        "Routine: no immediate disruption",
        "Soon: impaired work, but a workaround is available",
        "Now: customers cannot complete a critical task"
      ]
    },
    "is_urgent": {
      "type": "noul",
      "instructions": "Does the customer explicitly request action today?"
    }
  }
}
triage.mjs
import { TypeSafeClient, choice, score, noul } from "@typesafe-ai/sdk";

const client = new TypeSafeClient();
const ticket = "Our checkout integration has failed since this morning. Customers cannot pay. Please fix it today.";

const response = await client.systemOne({
  model: "jev-latest",
  state: ticket,
  questions: {
    department: choice(
      "Which team should handle this support ticket?",
      {
        "billing": "Charges, invoices, or refunds",
        "technical": "Broken software, outages, or integration failures",
        "sales": "Buying, upgrading, or product questions",
        "other": "None of these teams is a clear match"
      }
    ),
    priority: score(
      "How quickly does this ticket need attention?",
      [
        "Routine: no immediate disruption",
        "Soon: impaired work, but a workaround is available",
        "Now: customers cannot complete a critical task"
      ]
    ),
    is_urgent: noul("Does the customer explicitly request action today?")
  }
});

console.log(JSON.stringify(response, null, 2));
const { department, priority, is_urgent } = response.answers;

// Example policy thresholds, not TypeSafe defaults.
const needsReview = department.choice === "other"
  || department.confidence < 0.8
  || priority.confidence < 0.8;

if (needsReview) {
  console.log({ queue: "manual-review", model: response.model });
} else {
  const expedite = is_urgent.noul >= 0.8 || priority.score >= 1.5;
  console.log({
    queue: department.choice,
    priority: expedite ? "high" : "normal",
    model: response.model
  });
}

Run it with node triage.mjs. The three answers are evaluated independently against the same state; then the code branches on each typed result. For this captured HTTP 200 response, the branch prints:

Example policy output
{
  queue: 'technical',
  priority: 'high',
  model: 'jev-1.13.0'
}
Observed triage response
{
  "model": "jev-1.13.0",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "confidence": 1,
      "probabilities": {
        "billing": 0,
        "sales": 0,
        "other": 0,
        "technical": 1
      }
    },
    "priority": {
      "type": "score",
      "score": 2,
      "confidence": 1,
      "legend": {
        "0": "Routine: no immediate disruption",
        "1": "Soon: impaired work, but a workaround is available",
        "2": "Now: customers cannot complete a critical task"
      },
      "probabilities": {
        "0": 0,
        "1": 0,
        "2": 1
      }
    },
    "is_urgent": {
      "type": "noul",
      "noul": 0.98
    }
  },
  "usage": {
    "input_tokens": 451,
    "output_tokens": 78
  }
}

The 0.8 confidence checks and 1.5 Score cutoff are example policy thresholds from this program. They are not TypeSafe defaults. The response also includes token usage, which I can use when reviewing a run and planning limits; see the Jev API reference for request fields, pricing, and limits.

When independent answers disagree

Contradictory answers

Two Noul questions might ask whether a ticket must be handled today and whether it is safe to leave until next week. Each is evaluated independently. If both answers are high, my code sends the ticket to review. When the alternatives must be mutually exclusive, I use one Choice question instead of expecting separate questions to enforce that constraint. High confidence still describes the distribution, not a guarantee that the answer is correct; the confidence guide explains that distinction.

Score is a rubric position

Score is an ordered, zero-based rubric that I define. With three levels the positions run from 0 to 2. The returned score is the probability-weighted mean of those level numbers, so it is a position on the rubric, not a percentage or a measurement of the world. Read the probabilities and confidence beside it; different distributions can produce the same score. The official Score reference gives the full response shape.

Keep each answer's policy independent
AnswerExample checkApplication action
departmentchoice === "technical"Select the technical queue only after the review checks pass
priorityscore >= 1.5Mark the ticket high priority under this example policy
is_urgentnoul >= 0.8Expedite when the application policy says to do so

Exact arithmetic belongs in code

Compute totals, counts, timestamps, thresholds, and weights with ordinary code. A Score rates text against descriptions; it is not a calculator. In this flow, Jev supplies the three judgments and JavaScript applies the review and priority rules.

Before production

  1. Keep TYPESAFE_API_KEY in the server environment and run the example with a real key only from that environment.
  2. Test representative tickets, including each Choice criterion, every Score level, and Noul cases that should not expedite.
  3. Inspect the full answer objects, especially probabilities and confidence, rather than storing only the winning label or number.
  4. Write and test the review branch before enabling an automatic queue branch. Treat the thresholds in the sample as local policy to verify, not defaults to copy blindly.
  5. Decide what to retain from model, answers, and usage so a later policy change can be explained.

Frequently asked questions

Does Jev write a reply to the customer?

No. The System One examples here ask for typed decisions: a choice, an ordered score, or a Noul probability. My application owns the next action and any customer-facing text.

What does jev-latest mean in a request?

It is the model name used by these examples. Read the model field in the response to see the concrete version returned for that call; the captured examples resolve to jev-1.13.0.

Can I ask several questions in one request?

Yes. The quickstart shows Noul, Choice, and Score questions mixed in one call. Each answer remains its own typed field, so your code can apply separate policies.

Does a Noul answer include confidence?

The Noul response in this guide contains a noul value. It does not add the confidence field shown on Choice and Score answers, so branch on the value your application defines.

Is a high confidence value a correctness guarantee?

No. Confidence describes how concentrated the returned probability distribution is. It is a useful signal for a review policy, but it does not prove that an answer is correct.

Are 0.8 and 1.5 TypeSafe thresholds?

No. Those numbers are example policy choices in the triage program: 0.8 for review checks and 1.5 for the Score branch. Choose and test thresholds for your own workflow.

Is a Score a measurement such as a percentage?

No. Score uses the ordered levels you provide. Its zero-based result is a probability-weighted position on that rubric, so the level descriptions and the probability distribution matter.

What should happen when the answers point in different directions?

Keep the answers separate, apply explicit code rules, and send ambiguous or consequential cases to review. Do not let one convenient field silently override the others.

Keep building

Jev APIA practical reference for TypeSafe’s System One endpoint, with a tested request, the exact response, and the decisions your code needs to make next.