← Back to Blog
APIOpenRoutertutorialTypeScripterrors

Jev API Tutorial: Getting Started with OpenRouter (Keys, Requests, Errors, Retries)

·2 min read

Jev API Tutorial: Getting Started with OpenRouter

This Jev API tutorial takes you from zero to a typed response in about five minutes. Jev is served through OpenRouter, so one API key covers it alongside other models. Model ID: typesafe/jev-1.13.

TL;DR: POST /api/v1/chat/completions with content = { state, questions }. Read answers[].selected, confidence, and distribution. Retry 429; validate questions before send.


1. Get an OpenRouter API key

  1. Create an account at OpenRouter.
  2. Generate an API key (sk-or-...).
  3. Store it in an environment variable — never in frontend code or a git repo.
export OPENROUTER_API_KEY="sk-or-..."

No separate TypeSafe signup is required for this path.


2. Send your first request

curl https://openrouter.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-1.13",
    "messages": [{
      "role": "user",
      "content": {
        "state": "I was charged twice for the same order #4821. Please fix this.",
        "questions": [{
          "type": "choice",
          "text": "Which team should handle this?",
          "options": ["billing", "tech_support", "sales"]
        }]
      }
    }]
  }'

Request shape

Field Type Notes
state string Unstructured text (email, ticket, DOM snapshot…)
questions[].type choice | score | noul Question type
questions[].text string The decision, phrased for branching
questions[].options string[] For choice/score

You can send multiple questions in one call (e.g. intent + urgency).


3. Read the typed answer

{
  "answers": [{
    "type": "choice",
    "text": "Which team should handle this?",
    "selected": "billing",
    "confidence": 0.94,
    "distribution": {
      "billing": 0.94,
      "tech_support": 0.04,
      "sales": 0.02
    }
  }]
}
  • selected — top option (or weighted pick for score).
  • confidence — probability of the top option.
  • distribution — full probability mass.
type JevAnswer = {
  type: 'choice' | 'score' | 'noul';
  text: string;
  selected?: string;
  confidence: number;
  distribution?: Record<string, number>;
};

TypeScript client sketch

export async function runJev(state: string, questions: unknown[]) {
  const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'typesafe/jev-1.13',
      messages: [{ role: 'user', content: { state, questions } }],
    }),
  });
  if (!res.ok) throw new Error(`Jev HTTP ${res.status}`);
  const json = await res.json();
  return json.choices?.[0]?.message?.content?.answers ?? json.answers ?? json;
}

Validate questions locally before send in hot paths — missing options is a common 400.


Error handling and retries

Status Meaning Action
401 Bad/missing key Fail fast; check env
400 Invalid question payload Fix request; do not retry blindly
429 Rate limit Exponential backoff + jitter
5xx Upstream Retry with cap; fallback path
Timeout Network / slow 2s budget; fall back to human/queue
async function withRetry<T>(fn: () => Promise<T>, tries = 3): Promise<T> {
  let lastErr: unknown;
  for (let i = 0; i < tries; i++) {
    try { return await fn(); } catch (e) {
      lastErr = e;
      await new Promise((r) => setTimeout(r, 2 ** i * 200 + Math.random() * 100));
    }
  }
  throw lastErr;
}

Security notes for the Jev API

  • Call Jev from a server or worker, not the browser, if the key is secret.
  • If you must call from the client, use a proxy with auth and rate limits.
  • Redact PII from state when policy requires it.
  • Set an overall timeout (~2s) so product UX cannot hang.

Logging & observability

Log per call: model id, latency, selected, confidence, distribution hash, ticket/message id.
Alert on error rate and p95 latency; chart average confidence and entropy for drift.


Next steps


FAQ

Is there an official SDK?
Community TypeScript/Python wrappers exist; the HTTP contract is OpenRouter-compatible.

Can I stream?
Classification returns a small typed payload — streaming is rarely useful.

How do I pin a model version?
Use the full model id typesafe/jev-1.13 and watch release notes before bumping.


Related reading