26 sept 2026

Cómo llamar a la API de Jev desde curl, Python y JavaScript

Una primera solicitud a /v1/systemone de Jev AI que puedes pegar en una terminal y luego en una app — con una respuesta real, latencia medida y la tabla completa de errores.

Antes de tu primera llamada

Jev responde decisiones tipadas: envías un estado más preguntas con nombre, y devuelve una de las respuestas que declaraste, con una probabilidad. Optio es una pasarela operada de forma independiente que reenvía la solicitud a Jev — creas una cuenta, abres el dashboard y creas una clave de API. La clave completa se muestra exactamente una vez y empieza por jev_live_; guárdala en un gestor de secretos del lado del servidor o en una variable de entorno, nunca en código de frontend ni en un repositorio Git.

El endpoint es el mismo para todos los clientes:

POST https://jevai.website/api/v1/systemone
Authorization: Bearer $JEV_API_KEY
Content-Type: application/json
Endpoint y cabecera de autenticación

La solicitud útil más pequeña

Un cuerpo de solicitud tiene dos partes: state (de qué trata la decisión) y questions (lo que quieres saber). El state puede ser una cadena simple, un array de cadenas o un objeto JSON, hasta 100.000 caracteres. Questions es un mapa de 1 a 8 preguntas con nombre; el nombre debe ser un identificador válido (letras, dígitos, guiones bajos — se convierte en la clave de la respuesta).

curl https://jevai.website/api/v1/systemone \
  -H "Authorization: Bearer $JEV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "The payments webhook failed twice overnight. Should I wake up the on-call engineer?",
    "questions": {
      "page": {
        "type": "noul",
        "instructions": "Should someone be paged right now?",
        "criteria": {
          "true": "Revenue-impacting or data loss",
          "false": "Can wait until morning"
        }
      }
    }
  }'
curl — una pregunta sí/no

Los valores de criteria son tu propia redacción — escríbelos como líneas de política. Son aquello contra lo que el modelo juzga, así que una pregunta como «¿Debería avisar a alguien?» con definiciones true/false explícitas es mucho más repetible que la misma pregunta sin ellas.

Cómo es la respuesta

Una respuesta 200 real de este endpoint se ve así (capturada contra la pasarela en producción; los recuentos de tokens son de la solicitud real):

{
  "request_id": "req_fe17d05411092c4f7f97947a",
  "model": "jev-1.14.0",
  "answers": {
    "page": { "type": "noul", "noul": 0.91 }
  },
  "usage": {
    "input_tokens": 128,
    "output_tokens": 21,
    "credits": 1,
    "credits_remaining": 4
  },
  "latency_ms": 432
}
Respuesta 200, abreviada

Las respuestas van indexadas por los nombres de tus preguntas, así que ramificar en código es trivial: if (answers.page.noul < 0.8) escalate(). Cada respuesta también lleva un request_id — citándolo cuando escribas a soporte, y regístralo en tu lado para la conciliación.

Los tres tipos de pregunta

  • noul — sí/no. Devuelve noul: la probabilidad (0–1) de que la respuesta sea sí. criteria con descripciones true/false es opcional pero recomendable.
  • choice — elige una de 2–20 opciones etiquetadas. Devuelve choice (la etiqueta seleccionada), probabilities por opción y un valor de confianza.
  • score — sitúa el estado en una escala ordenada de 2–10 niveles, p. ej. [Calm, Frustrated, Very angry]. Devuelve un score continuo (0 = primer nivel), probabilities sobre los niveles, un legend que mapea los rangos de score de vuelta a etiquetas, y confidence.

Hasta ocho preguntas viajan en una sola solicitud — eso importa para el coste, porque la facturación es por token de entrada con un mínimo de 1 crédito, no por pregunta. Tres preguntas sobre el mismo estado cuestan lo mismo que una, siempre que el total se mantenga por debajo de 1.000 tokens.

Parsear la respuesta en Python y JavaScript

import os, requests

resp = requests.post(
    "https://jevai.website/api/v1/systemone",
    headers={"Authorization": f"Bearer {os.environ['JEV_API_KEY']}"},
    json={
        "state": {
            "ticket": {"subject": "Refund duplicate charge", "order": "A-104"},
            "refund_policy": "Duplicate charges are eligible for a full refund.",
        },
        "questions": {
            "policy_supports": {"type": "noul", "instructions": "Does the policy support this refund?"},
            "frustration": {"type": "score", "instructions": "How frustrated is the customer?",
                            "criteria": ["Calm", "Frustrated", "Very angry"]},
        },
    },
    timeout=30,
)
resp.raise_for_status()
answers = resp.json()["answers"]
if answers["policy_supports"]["noul"] > 0.9:
    print("auto-approve; frustration =", answers["frustration"]["legend"])
Python
const resp = await fetch("https://jevai.website/api/v1/systemone", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.JEV_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    state: "Charged twice for order A-104, please refund the duplicate.",
    questions: {
      route: {
        type: "choice",
        instructions: "Which team handles this?",
        criteria: {
          billing: "Payments, payouts, invoicing, refunds",
          technical: "Bugs, outages, integrations",
          sales: "Pricing, upgrades, new accounts",
        },
      },
    },
  }),
});
const { answers, usage, latency_ms } = await resp.json();
console.log(answers.route.choice, answers.route.probabilities, latency_ms);
JavaScript / TypeScript

Códigos de error y reintentos

Los errores siempre usan una misma forma: {error: {code, message, request_id}}. Ramifica por code, no por el texto del mensaje. La tabla:

  • 401 missing_api_key / invalid_api_key — no hay cabecera Bearer, o la clave fue revocada. No reintentar.
  • 402 insufficient_credits — el saldo del workspace no cubrió la reserva. No reintentar; recarga créditos.
  • 422 invalid_request — cuerpo malformado, nombres de pregunta inválidos, cero preguntas, estado de más de 100.000 caracteres. No reintentar; corrige la solicitud.
  • 429 rate_limited — se alcanzó el techo de concurrencia o de solicitudes por minuto del plan. Reintenta con backoff exponencial; la respuesta lleva las cabeceras Retry-After y X-RateLimit-Remaining.
  • 502 upstream_error (también upstream_timeout, upstream_unreachable) — el proveedor del modelo falló o expiró la ventana upstream de 15 segundos. Reintenta con backoff; no se te cobró.
  • 529 upstream_overloaded — capacidad del proveedor saturada. Mismo tratamiento que 502.
import time

def decide(body, max_retries=3):
    for attempt in range(max_retries + 1):
        resp = requests.post(URL, headers=HEADERS, json=body, timeout=30)
        if resp.status_code in (429, 502, 529):
            time.sleep(min(2 ** attempt, 8))
            continue
        resp.raise_for_status()
        return resp.json()
    raise RuntimeError("upstream kept failing; give up or degrade")
Bucle de reintentos en Python que merece la pena copiar

Los créditos se reservan antes de que el modelo se ejecute y se liquidan después contra el recuento real de tokens; si el proveedor falla, la reserva se devuelve automáticamente. Una solicitud fallida nunca deja un cargo en el ledger.

Cuánto cuesta y qué tan rápido responde

Un crédito son 1.000 tokens de entrada; el cargo es ceil(tokens de entrada / 1.000), mínimo 1, y los tokens de salida son gratis. En la prueba anterior, una solicitud de 401 tokens con dos preguntas costó exactamente 1 crédito. Con el pack Starter ($10 = 100.000 créditos) eso son $0,0001 por decisión.

Latencia: a lo largo de 16 llamadas en vivo sucesivas en un mismo día laborable, la latency_ms reportada se agrupó en torno a 400–560 ms tanto para solicitudes de una pregunta como de dos, con una mediana cercana a 445 ms. La primera llamada tras un periodo de inactividad registró algún arranque en frío ocasional de ~3 s; no se observaron colas más largas con este tamaño de muestra. Fija tu timeout a partir del p99 de tu propia región en lugar de fiarte de esta única medición.

¿Puedo hacer más de 8 preguntas en una solicitud?

No — el límite es de 8 preguntas por solicitud. Divide el resto en una segunda solicitud sobre el mismo estado, o mueve las preguntas de menor valor a una vía por lotes más barata.

¿Dónde consigo mis primeros créditos gratis?

Cada workspace nuevo empieza con 5 créditos gratis, y el playground es gratis en el navegador (30 ejecuciones al día de forma anónima, 50 con sesión iniciada). Cinco créditos bastan para validar una decisión con datos reales antes de pagar nada.

¿Es este el endpoint oficial de TypeSafe?

No. Optio es una pasarela independiente para llamar a Jev y no está afiliada a TypeSafe. La forma de la solicitud sigue el mismo contrato /v1/systemone, así que los clientes existentes pueden apuntar hacia ella.

Prueba el playground de Jev AI para enviar tu primera decisión gratis cuando termines esta guía.