This Jev AI API guide takes you from an empty project to a working call. It covers the four ways to reach the API, the exact request and response shapes, limits and errors, and a step-by-step TypeScript walkthrough. The request builder below writes TypeScript, curl, or Python for your own questions.

Four ways to reach the Jev AI API

Your code can reach Jev through four doors: TypeSafe API at api.typesafe.ai/v1/systemone, Vercel AI Gateway through the AI SDK or /v1/evaluate, OpenRouter's /api/alpha/decisions, and Cloudflare Workers AI. All lead to the same Jev model.
Four doors, one model. Pick by stack and billing; the answers are the same shape.
RouteEndpointModel idKeyYes/no type
TypeSafe APIPOST api.typesafe.ai/v1/systemonejev-latest or jev-1.13.0TypeSafe keynoul
Vercel AI GatewayAI SDK experimental_evaluate, or POST ai-gateway.vercel.sh/v1/evaluatetypesafe-ai/jevVercel OIDC token or gateway keyboolean
OpenRouterPOST openrouter.ai/api/alpha/decisionstypesafe/jev-1.13OpenRouter keynoul
Cloudflare Workers AIenv.AI.run('typesafe/jev', …)typesafe/jevWorker bindingnoul

The step-by-step walkthrough below uses the AI SDK through Vercel AI Gateway, because it gives you typed answers in TypeScript with no extra client. If you use another language or route, jump to calling the API without TypeScript.

Before you start

You need:

  • Node.js 22 or later and a package manager. The examples use pnpm, but npm, yarn, and bun work too.
  • A Vercel account and the Vercel CLI: npm i -g vercel.
  • A Next.js project that uses the App Router. The examples use route handlers, but the evaluate call works in any server-side TypeScript code.

Call Jev from server code only, such as a route handler, a server action, or a script. Code that runs in the browser would expose your credentials.

Jev at a glance

Model IDtypesafe-ai/jev
How to call itexperimental_evaluate from ai, version 7.0.105 or later
Question typeschoice, score, boolean
Input sizeUp to 32,000 tokens of state (64,000 per request in total)
Price$0.042 per 1M input tokens, with no charge for output tokens
PrivacyZero Data Retention and No Training can be required per request
Not supportedText generation, streaming, and OpenAI-compatible endpoints

Step 1: Install AI SDK

Jev needs AI SDK 7.0.105 or later, which added experimental_evaluate.

Terminal
pnpm add ai
# or: npm install ai

The experimental_ prefix means the API can change, even in a patch release. Pin the exact version in package.json and read the release notes before you upgrade.

Step 2: Connect to AI Gateway

If you pass a plain model ID such as 'typesafe-ai/jev', AI SDK sends the call through Vercel AI Gateway. You don’t need a TypeSafe account or key, but the Gateway needs to know who is calling:

Where your code runsHow it signs inWhat you do
A Vercel deploymentOIDC tokenNothing. Vercel adds the token automatically.
Your computerShort-lived OIDC tokenRun vercel link, then vercel env pull.
Another host, a script, or CIAPI keyCreate a key in the AI Gateway dashboard and set AI_GATEWAY_API_KEY.
Terminal
vercel link      # connect this folder to a Vercel project
vercel env pull  # writes VERCEL_OIDC_TOKEN to .env.local

Step 3: Ask your first question

Start with one yes-or-no question. The function below asks whether a customer message is a cancellation request:

lib/wants-to-cancel.ts
import { experimental_evaluate as evaluate } from 'ai';

export async function wantsToCancel(message: string) {
  const result = await evaluate({
    model: 'typesafe-ai/jev',
    state: message,
    questions: {
      cancel: {
        type: 'boolean',
        instructions: 'Does the customer want to cancel their subscription?',
        criteria: {
          true: 'The customer asks to cancel, close, or stop renewing their plan.',
          false: 'The customer asks a question, complains, or wants a different plan.',
        },
      },
    },
  });

  return result.answers.cancel.probability; // a number from 0 to 1
}

We sent “Hi, please cancel my subscription before the next billing date. Thanks.” and got this back:

result.answers
{ "cancel": { "type": "boolean", "probability": 0.99 } }

Three things to notice:

  • state is what Jev looks at. It can be a string, a JSON object, or a JSON array. There is no system prompt.
  • Each key in questions becomes a key in answers. Here, cancel goes in and cancel comes out, fully typed.
  • criteria is optional for booleans, but worth writing. It tells Jev exactly what counts as true and false. Step 5 shows how much this can change an answer.

A boolean probability is the chance that the statement is true. Near 1 means a strong yes. Near 0 means a strong no, not “unsure.” Values near 0.5 mean the model can’t tell.

Step 4: Pick the right question type

Every question has a type, an instructions string, and usually criteria. The type decides the shape of the answer:

TypeUse it whencriteriaYou get back
choiceYou need one option from a list: a queue, a category, a toolAn object of option keys and descriptions, with up to 255 optionschoice and probabilities for every option
scoreYou need a level on an ordered scale: urgency, quality, riskAn array of 2 to 10 level descriptions, from lowest to highestscore and probabilities for every level
booleanYou need to know if a statement is trueOptional true and false descriptionsprobability that the statement is true

A few details that are easy to miss:

  • choice is typed as your option keys. With options billing and technical, TypeScript sees 'billing' | 'technical', so a typo like choice === 'tech' fails at compile time. The selected option always has the highest probability.
  • Add an other option. Jev must pick one of your options. Without a catch-all, a message that fits nothing still lands in a real queue.
  • A score starts at 0 and can be a decimal. It is the probability-weighted average of the levels, so 2.69 means “between level 2 and level 3, closer to 3.” The probabilities keys are strings: "0", "1", and so on.
  • A score is not a probability. Dividing 3.2 by the top level 4 gives 0.8, but that does not mean an 80% chance of anything. It is still a position on your scale.

Describe options instead of naming them

Jev matches the state against your descriptions, so a bare label gives it very little to work with:

WeakBetter
high: 'high'blocking: 'The customer cannot do their work and has no workaround'
billing: 'billing'billing: 'Charges, invoices, refunds, and plan prices'

Descriptions can also be JSON objects or arrays. For example, you can give each option a list of example phrases.

Step 5: Ask several questions in one request

Jev answers every question in a request separately, in parallel, and against the same state. That has two effects:

  • Extra questions are cheap. Adding a question barely changes latency, and it does not change the answers to other questions.
  • Questions cannot see each other. If question B depends on the answer to A, make two calls with your own code in between.

The function below reads a support ticket and asks three questions at once. The model parameter has a default, so you can swap in a fake model for tests in Step 7.

lib/triage.ts
import {
  experimental_evaluate as evaluate,
  type Experimental_EvaluationModel as EvaluationModel,
} from 'ai';

export type Ticket = {
  subject: string;
  message: string;
  plan: string;
};

export async function askJev(
  ticket: Ticket,
  model: EvaluationModel = 'typesafe-ai/jev',
) {
  return evaluate({
    model,
    state: ticket, // objects work; no need to JSON.stringify
    questions: {
      team: {
        type: 'choice',
        instructions: 'Which team should handle this ticket?',
        criteria: {
          billing: 'Charges, invoices, refunds, and plan prices',
          technical: 'Bugs, errors, outages, and broken integrations',
          account: 'Login, passwords, team members, and permissions',
          other: 'Anything that does not fit the teams above',
        },
      },
      urgency: {
        type: 'score',
        instructions: 'How badly is this blocking the customer?',
        criteria: [
          'Not blocking: a question or a small visual issue',
          'Annoying, but there is a workaround',
          'Blocking: the customer cannot do their work',
          'Blocking and causing lost money or lost data',
        ],
      },
      wantsRefund: {
        type: 'boolean',
        instructions: 'Is the customer asking for money back?',
      },
    },
    providerOptions: {
      gateway: { zeroDataRetention: true }, // optional
    },
  });
}

Add a route handler so you can call it over HTTP:

app/api/triage/route.ts
import { askJev, type Ticket } from '@/lib/triage';

export async function POST(request: Request) {
  const ticket = (await request.json()) as Ticket;
  const result = await askJev(ticket);
  return Response.json(result.answers);
}

Start the dev server with pnpm dev, then send a ticket:

Terminal
curl -X POST http://localhost:3000/api/triage \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "CSV export broken for 3 days",
    "message": "Every CSV export fails with error 500. Our finance team cannot close the month. If this is not fixed today I want this month refunded.",
    "plan": "pro"
  }'

This is a real response from our test run:

Response
{
  "team": {
    "type": "choice",
    "choice": "technical",
    "probabilities": { "billing": 0, "technical": 1, "account": 0, "other": 0 }
  },
  "urgency": {
    "type": "score",
    "score": 2.69,
    "probabilities": { "0": 0, "1": 0, "2": 0.31, "3": 0.69 }
  },
  "wantsRefund": { "type": "boolean", "probability": 0.95 }
}

team and urgency look right. wantsRefund needs a closer look. The customer did not ask for a refund. They said they would want one if the bug is not fixed today. Our question was vague, so Jev read it broadly.

We added criteria to the same question and ran it again:

lib/triage.ts (wantsRefund only)
wantsRefund: {
  type: 'boolean',
  instructions: 'Is the customer asking for money back?',
  criteria: {
    true: 'The customer asks for money back right now, with no conditions.',
    false: 'No refund request, or the refund is only a threat that depends on something else happening first.',
  },
},

The probability dropped from 0.95 to 0.24. Neither answer is wrong. They answer different questions. When a result surprises you, check the wording of your question before you blame the model.

Step 6: Turn answers into decisions

Jev gives you signals. Your code makes the decision. Keep thresholds in your code, not in the question. That way you can tune them without changing what you ask, and without calling the model again.

For choice and score answers, there are two useful numbers:

  • The selected option’s probability, team.probabilities[team.choice]. How likely is this answer?
  • TypeSafe’s confidence, result.providerMetadata.typesafe.confidence, keyed by question ID. It measures how focused the whole distribution is, from 0 (spread evenly) to 1 (all on one option). It is not returned for booleans, and other providers do not have it.

Add a triageTicket function to lib/triage.ts. It assigns clear tickets and sends everything else to a person:

lib/triage.ts (add below askJev)
export async function triageTicket(
  ticket: Ticket,
  model: EvaluationModel = 'typesafe-ai/jev',
) {
  const result = await askJev(ticket, model);
  const { team, urgency, wantsRefund } = result.answers;

  // How likely is the chosen team? (undefined = unknown, never 0)
  const teamProbability = team.probabilities?.[team.choice];

  // How focused is the whole distribution? (TypeSafe only)
  const confidence = result.providerMetadata?.typesafe?.confidence as
    | Record<string, number>
    | undefined;
  const teamConfidence = confidence?.team;

  if (teamProbability == null || teamConfidence == null) {
    return { action: 'review' as const, reason: 'missing probability data' };
  }
  if (teamProbability < 0.7 || teamConfidence < 0.6) {
    return { action: 'review' as const, reason: 'team is unclear' };
  }

  return {
    action: 'assign' as const,
    team: team.choice, // 'billing' | 'technical' | 'account' | 'other'
    urgency: urgency.score, // 0 to 3, can be a decimal
    refund: readBoolean(wantsRefund.probability),
  };
}

// Booleans get three bands. The middle band means "not sure".
function readBoolean(probability: number) {
  if (probability >= 0.8) return 'yes' as const;
  if (probability <= 0.2) return 'no' as const;
  return 'unsure' as const;
}

Then change the route handler to return the decision instead of the raw answers:

app/api/triage/route.ts
import { triageTicket, type Ticket } from '@/lib/triage';

export async function POST(request: Request) {
  const ticket = (await request.json()) as Ticket;
  return Response.json(await triageTicket(ticket));
}

Why the code is written this way:

  • A missing value is not zero. AI SDK marks choice and score probabilities as optional, because some providers do not return them. If you replace a missing value with 0 or 1, you invent a prediction. Send those cases to review.
  • Booleans need a middle band. If you only check ≥ 0.8, a confident “no” (0.03) and a coin flip (0.5) end up in the same branch.
  • Classifying is not approving. refund: 'yes' means the customer asked. Whether they get the money depends on your refund policy and their account, and Jev sees neither. Check those in a separate step.

Choose thresholds for your own data

The 0.7, 0.6, and 0.8 above are starting points. Set stricter thresholds when a wrong action costs more:

What the answer triggersExampleStarting point
Something easy to undoShow a hint, pick an internal queueAbout 0.7
Something a customer seesSend an automatic replyHigher, plus spot checks
Something hard to undoDelete data, issue money, run a command0.9 or more, and a confirmation step below it

To find your own numbers:

  1. Collect a few hundred real examples and have people label the right answer themselves, without seeing Jev’s suggestion. Include hard and ambiguous cases.
  2. Run them through the exact same questions.
  3. Group the results by probability (0.7–0.8, 0.8–0.9, and so on) and count how often each group was right.
  4. Compare cutoffs. A higher cutoff makes fewer automatic mistakes but sends more work to people. Pick the balance your team can handle.

Check each question on its own. Independent tests found that Jev’s probabilities can be too confident for some question types and not confident enough for others. What Jev does and when to use it covers those results.

Step 7: Test your logic without calling Jev

Your thresholds are normal code, so test them like normal code. AI SDK includes a fake evaluation model in ai/test. It returns whatever answers you give it, with no network call and no cost.

Terminal
pnpm add -D vitest
lib/triage.test.ts
import { Experimental_EvaluationMockModelV4 as MockEvaluationModel } from 'ai/test';
import { describe, expect, it } from 'vitest';
import { triageTicket, type Ticket } from './triage';

type Answers = Awaited<ReturnType<MockEvaluationModel['doEvaluate']>>['answers'];

const ticket: Ticket = {
  subject: 'CSV export fails',
  message: 'Every export has failed since Monday.',
  plan: 'pro',
};

// A fake Jev that always returns the answers you give it.
function fakeJev(answers: Answers, confidence?: Record<string, number>) {
  return new MockEvaluationModel({
    doEvaluate: async () => ({
      answers,
      warnings: [],
      ...(confidence && { providerMetadata: { typesafe: { confidence } } }),
    }),
  });
}

const clearAnswers: Answers = {
  team: {
    type: 'choice',
    choice: 'technical',
    probabilities: { billing: 0.04, technical: 0.94, account: 0.02, other: 0 },
  },
  urgency: {
    type: 'score',
    score: 1.9,
    probabilities: { 0: 0, 1: 0.2, 2: 0.7, 3: 0.1 },
  },
  wantsRefund: { type: 'boolean', probability: 0.05 },
};

describe('triageTicket', () => {
  it('assigns the ticket when the team is clear', async () => {
    const decision = await triageTicket(ticket, fakeJev(clearAnswers, { team: 0.9 }));

    expect(decision).toEqual({
      action: 'assign',
      team: 'technical',
      urgency: 1.9,
      refund: 'no',
    });
  });

  it('sends the ticket to review when two teams are close', async () => {
    const decision = await triageTicket(
      ticket,
      fakeJev(
        {
          ...clearAnswers,
          team: {
            type: 'choice',
            choice: 'billing',
            probabilities: { billing: 0.48, technical: 0.45, account: 0.07, other: 0 },
          },
        },
        { team: 0.7 },
      ),
    );

    expect(decision.action).toBe('review');
  });

  it('sends the ticket to review when confidence is missing', async () => {
    const decision = await triageTicket(ticket, fakeJev(clearAnswers));

    expect(decision.action).toBe('review');
  });
});
Terminal
pnpm vitest run lib/triage.test.ts

 ✓ lib/triage.test.ts (3 tests)
 Test Files  1 passed (1)
      Tests  3 passed (3)

Get ready for production

Set a timeout and give every failure a destination

A call either returns an answer for every question or throws an error. There is no partial result. Decide in advance what happens when a call fails. In most apps, the safe default is “send it to a person.”

lib/triage.ts
// In askJev, next to model and state:
//   abortSignal: AbortSignal.timeout(5_000), // give up after 5 seconds
//   maxRetries: 1,                           // default is 2

export async function safeTriage(ticket: Ticket) {
  try {
    return await triageTicket(ticket);
  } catch (error) {
    console.error('Jev evaluation failed', error);
    return { action: 'review' as const, reason: 'evaluation failed' };
  }
}

Retries use extra time. In a user-facing request, one quick retry followed by a fallback is usually better than a long wait.

Errors you may see

ErrorWhat it usually meansFix
401 / 403Your token expired, the folder is not linked, or the project has no Gateway accessRun vercel link and vercel env pull, or check your API key
NoSuchModelErrorThe model ID is wrong, or the provider cannot evaluateUse typesafe-ai/jev and call through AI Gateway
Experimental_EvaluationUnsupportedQuestionTypeErrorThe model does not support a question type. The whole call fails.Use only choice, score, and boolean
InvalidArgumentErrorYour input is invalid, such as a score with one level or a value that is not JSONCheck your state and criteria
InvalidResponseDataErrorThe provider returned an invalid answer, such as a missing optionRetry. If it keeps happening, report the request to the provider.

Log enough to learn from later

Save the answers, result.response.modelId, result.usage, and a version name for your questions and thresholds. When you edit a description or change a cutoff, run your labeled examples again. A small change in wording can shift the probabilities, as Step 5 showed.

Probabilities are rounded to two decimal places, and result.rounding tells you the precision. A distribution may add up to 0.99. That is expected, so don’t renormalize it.

Limits you should know

Request size64,000 tokens per request in total; 32,000 for the state plus the longest single question
Choice options2 to 255 per question (this site’s Playground allows 10 to keep the editor manageable)
Score levels2 to 10, ordered lowest first
InputText only: a string, a JSON object, or an array of text. No images, audio, or video.
Rate limits250,000 tokens per second and 1,200 requests per minute on the TypeSafe API, “adjusting dynamically” under heavy demand
Modelsjev-1.13.0; aliases jev-latest and jev-preview point to it today. GET /v1/models lists what your key can use.
OutputProbabilities are rounded to 0.01 and are often exactly 0 or 1

Aliases move when TypeSafe ships a new model, and answers can shift with them. Pin a versioned id in production and log the model field each response reports.

The rate limits are real. When we ran our Jev benchmark with four parallel workers through Vercel AI Gateway, most calls failed with an upstream rate-limit error; one call at a time with a short pause worked reliably.

Handle errors

A table of status codes: 401 bad key, fix config; 422 invalid request, fix the question; 429 rate limit, retry with backoff; 529 overloaded, retry with backoff; timeout, fall back to human review or a rule.
Only rate limits and overload are worth retrying. Everything else needs a fix or a fallback.

The native TypeSafe API returns 401 for a bad key, 422 when the request doesn’t validate, 429 when you hit a rate limit, and 529 when the service is overloaded. The official SDKs retry the last two with backoff and honour the retry-after header. Through the AI SDK, those surface as the errors in the production table above.

Estimate the cost

The Jev AI API charges for input tokens only, at $0.042 per 1M. Output is free, so cost depends on the size of your state, questions, and descriptions, not on the length of the answer.

CallInput tokensCost per callCost per 1M calls
Step 3: one boolean, short message335$0.000014about $14
Step 5: three questions, one ticket517$0.000022about $22

Read the exact count from each response: result.usage.inputTokens in the AI SDK, usage.input_tokens on the native API, or usage.cost on OpenRouter. For your own volume, use the calculator on the Jev pricing page.

Call the Jev AI API without TypeScript

TypeSafe API with curl

The native endpoint takes the same three fields. Note the yes/no type is noul here:

Terminal
curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-latest",
    "state": "I was charged twice for my subscription. Please refund one.",
    "questions": {
      "refund": { "type": "noul", "instructions": "Is the customer asking for money back?" },
      "team": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": { "billing": "Payments and refunds", "technical": "Bugs and errors" }
      }
    }
  }'

The response names the model version that answered and reports usage (the numbers here are illustrative):

Response
{
  "model": "jev-1.13.0",
  "answers": {
    "refund": { "type": "noul", "noul": 0.98 },
    "team": {
      "type": "choice",
      "choice": "billing",
      "confidence": 0.94,
      "probabilities": { "billing": 0.97, "technical": 0.03 }
    }
  },
  "usage": { "input_tokens": 312, "output_tokens": 24 }
}

Score answers look similar: a score between 0 and the top level, probabilities keyed by level index, a legend mapping each index to your level text, and confidence.

Python with the official SDK

Terminal
pip install typesafe-sdk   # or: uv add typesafe-sdk
triage.py
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

with TypeSafeClient() as client:  # reads TYPESAFE_API_KEY
    response = client.system_one(
        state={"document": "I was charged twice. Please fix this ASAP."},
        questions={
            "billing": Noul(instructions="Is this ticket about billing?"),
            "tone": Choice(instructions="What is the customer's tone?",
                           criteria={"calm": None, "frustrated": None, "angry": None}),
            "urgency": Score(instructions="How urgent is this ticket?",
                             criteria=["can wait", "this week", "today"]),
        },
    )

print(response.nouls["billing"].noul)
print(response.choices["tone"].choice)
print(response.scores["urgency"].score)

There is also an async client (AsyncTypeSafeClient) and a JavaScript SDK (npm install @typesafe-ai/sdk) with the same shape.

OpenRouter

OpenRouter serves the same request and response format on its Decisions endpoint, which is still under an /alpha/ path. Each response adds usage.cost in dollars:

Terminal
curl https://openrouter.ai/api/alpha/decisions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "model": "typesafe/jev-1.13", "state": "…", "questions": { … } }'

Vercel AI Gateway over HTTP

Without the AI SDK, post to https://ai-gateway.vercel.sh/v1/evaluate with model: "typesafe-ai/jev". This endpoint uses the AI SDK question names (boolean) and returns the cost in providerMetadata.gateway.cost. Add "providerOptions": { "gateway": { "zeroDataRetention": true } } to require zero data retention. Vercel also offers a TypeSafe-compatible API, so an existing TypeSafe client only needs a new base URL.

Build a request for your own questions

Write your state and questions below and copy working code for TypeScript, curl, or Python. It checks the limits above as you type and converts boolean to noul where the route needs it. Nothing is sent anywhere.

Tool · Jev AI API request builder

Build a Jev request and copy the code

1 State: what Jev should read

Plain text, or a JSON object or array. Sent as a string.

2 Questions: what Jev should decide

Optional. Two lines: “true: …” and “false: …”

One option per line, as “name: description”. 2–255 options.

One level per line, lowest first. 2–10 levels.

TypeScript uses the AI SDK through Vercel AI Gateway (typesafe-ai/jev).

import { experimental_evaluate as evaluate } from 'ai';

const state = "Hi, I was charged twice for my March invoice and I can't log in to download the receipt. Please refund the duplicate today.";
const questions = {
  "wants_refund": {
    "type": "boolean",
    "instructions": "Is the customer asking for money back?",
    "criteria": {
      "true": "The customer asks for a refund, credit, or chargeback.",
      "false": "The customer asks a question or reports a problem without asking for money back."
    }
  },
  "team": {
    "type": "choice",
    "instructions": "Which team should handle this message?",
    "criteria": {
      "billing": "Payments, invoices, refunds",
      "technical": "Bugs, outages, errors",
      "account": "Login, profile, permissions"
    }
  },
  "urgency": {
    "type": "score",
    "instructions": "How urgent is this message?",
    "criteria": [
      "Can wait for a normal reply",
      "Should be handled today",
      "Blocking the customer right now"
    ]
  }
};

const result = await evaluate({
  model: 'typesafe-ai/jev',
  state,
  questions,
  maxRetries: 0,
  providerOptions: { gateway: { zeroDataRetention: true } },
});

console.log(result.answers);

Keys stay in environment variables; nothing you type here leaves your browser.

Common mistakes

MistakeDo this instead
Calling Jev with generateText or an OpenAI-compatible clientUse experimental_evaluate or /v1/evaluate
Asking one big question, such as “Is this ticket a priority?”Ask small questions (urgency, impact, customer tier) and combine them in code
Asking about a fact that is not in the stateAdd the fact to the state. Jev cannot say “I don’t know,” so it will still answer.
Asking Jev to do math, count, or compare datesDo it in code and pass the result, such as overLimit: true
Passing an array of 50 tickets to classify them allAn array is one state. Make one call per ticket.
Reading a missing probabilities as 0Treat it as unknown and send the case to review
Reading a boolean 0.05 as “unsure”It is a confident no. Uncertainty is near 0.5.
Using one threshold for every questionTest each question and each action separately
Letting a true answer approve an actionClassify with Jev, then check permissions and policy in code

Frequently asked questions

What is the Jev AI API endpoint?

TypeSafe's own endpoint is POST https://api.typesafe.ai/v1/systemone with a Bearer API key. Jev is also available on Vercel AI Gateway (model typesafe-ai/jev), OpenRouter's Decisions endpoint (typesafe/jev-1.13), and Cloudflare Workers AI (typesafe/jev).

Is the Jev AI API compatible with the OpenAI API?

No. Jev returns typed answers instead of chat completions, so OpenAI-compatible clients and generateText cannot call it. Use the TypeSafe SDKs, the AI SDK's experimental_evaluate, or plain HTTP.

How do I get a Jev API key?

Create one at console.typesafe.ai/keys (TypeSafe was in early access in September 2026). Alternatively, use an OpenRouter key, or call Jev through Vercel AI Gateway with your Vercel project's OIDC token or a gateway API key.

What are the Jev AI API rate limits?

TypeSafe lists 250,000 tokens per second and 1,200 requests per minute, and says limits are adjusting dynamically during high demand. Over the limit you get HTTP 429; retry with exponential backoff.

Why is the yes/no question type called noul?

Noul is TypeSafe's name for a question that returns the probability a statement is true. The AI SDK and Vercel's HTTP API call the same type boolean and return it as probability instead of noul.

Sources

Example responses, token counts, and the refund-criteria comparison come from live calls we made on 2026-09-24 with ai@7.0.107. Your numbers will differ. This site is not affiliated with TypeSafe AI or Vercel.