Skip to content

Latest commit

 

History

History
349 lines (291 loc) · 13.4 KB

File metadata and controls

349 lines (291 loc) · 13.4 KB

API reference

This server mirrors the TypeSafe Jev HTTP API and adds a small set of extensions. Everything below is the behavior of this server, not of api.typesafe.ai.

Base URL:

  • Local development: http://127.0.0.1:8787
  • Production: your deployed worker URL, for example https://jeb.<account>.workers.dev

Authentication

When the API_KEY environment variable is set, every /v1/* request must send:

Authorization: Bearer <API_KEY>

When API_KEY is unset the server is open and any bearer token (or none) is accepted, which keeps drop-in SDK usage working out of the box. /health and / are never authenticated.

A missing or wrong key returns 401:

{
  "error": {
    "message": "Missing or invalid API key. Check the Authorization header.",
    "type": "authentication_error"
  }
}

Model provider

The JEB_PROVIDER environment variable chooses where model calls go; it does not affect the request or response shapes documented here.

  • openrouter (default) calls OpenRouter's chat-completions API. The model is OPENROUTER_MODEL (or the legacy MODEL_ID alias), defaulting to inception/mercury-2.5; the key is OPENROUTER_API_KEY.
  • inception calls Inception's direct OpenAI-compatible API (https://api.inceptionlabs.ai/v1, overridable with INCEPTION_BASE_URL). The model is INCEPTION_MODEL, defaulting to mercury-2.5; the key is INCEPTION_API_KEY.

Both routes send the same prompt and JSON Schema. Reasoning is mapped to each provider's native field: OpenRouter receives reasoning: { effort }, Inception receives an OpenAI-style reasoning_effort string (with none sent when reasoning is off). A missing key for the selected provider returns 500 with a message naming the environment variable to set.

CORS

Responses include access-control-allow-origin: *, and OPTIONS requests return 204 with GET, POST, OPTIONS allowed and Authorization, Content-Type permitted headers.

POST /v1/systemone

Evaluate a state against a map of typed questions. All questions are evaluated against the same state in a single model call; answers come back under the question ids you chose.

Request body

Field Type Required Notes
state any JSON value yes String, object, array, or null.
model string no Advisory. Any name resolves to the active provider's configured model (see below).
reasoning_effort string no Extension. One of none, low, medium, high.
questions map<string, Question> yes Non-empty. Question ids are arbitrary and never sent to the model.

reasoningEffort is accepted as an alias for reasoning_effort; when both are present the snake_case field wins.

Questions

Every question has a type and an optional instructions value. instructions may be any JSON value: a string, or an object/array that puts the question in one field and the data it references in others. TypeSafe documents structured questions in Advanced: structure.

Type Extra field Shape
noul criteria Optional object with true and/or false descriptions of the boundary.
choice criteria Required map of option name to description. 1–255 options.
score criteria Required ordered array of level descriptions, lowest first. 2–10 levels.

Criteria descriptions are also any JSON value, so an option or level can carry structured guidance (what, not_for, examples, and so on).

{
  "state": "I sent the shoes back a week ago. When do I get my money?",
  "questions": {
    "topic": {
      "type": "choice",
      "instructions": "Which returns topic is the customer asking about?",
      "criteria": {
        "return_policy": "Whether and how an item can be returned",
        "return_status": "Progress of a return already sent"
      }
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": ["Calm", "Frustrated but civil", "Very angry"]
    },
    "wants_refund": {
      "type": "noul",
      "instructions": "Does the customer want money back?"
    }
  }
}

Response body

Field Type Notes
model string The model that answered, for example inception/mercury-2.5 (OpenRouter) or mercury-2.5 (direct Inception API).
answers map<string, Answer> One answer per question, under the same ids you used.
usage { input_tokens, output_tokens } Token counts mapped from the provider's prompt/completion counts.

output_tokens includes reasoning tokens when reasoning is enabled.

Answer types

Every answer has a type matching its question. Choice and Score answers carry probabilities and confidence; Noul answers do not carry confidence.

noul:

{ "type": "noul", "noul": 0.87 }

choice:

{
  "type": "choice",
  "choice": "billing",
  "probabilities": { "billing": 0.88, "technical": 0.12, "sales": 0 },
  "confidence": 0.82
}

score:

{
  "type": "score",
  "score": 1.43,
  "legend": {
    "0": "Cosmetic; no impact to functionality",
    "1": "Broken or degraded feature, but workaround exists",
    "2": "Blocking issue; no workaround exists"
  },
  "probabilities": { "0": 0, "1": 0.57, "2": 0.43 },
  "confidence": 0.355
}

How the values are produced:

  • probabilities are clamped to non-negative, normalized to sum to 1, and rounded to six decimals with the rounding residue placed on the largest value, so they sum to exactly 1. If the model returns all zeros the distribution falls back to uniform.
  • choice is the option with the highest probability (first one wins a tie).
  • score is the probability-weighted mean of the level numbers, sum(i * probabilities[i]), and can land between levels.
  • legend maps each level number back to the description you supplied, including structured descriptions.
  • confidence is computed from the distribution with (n * peak - 1) / (n - 1), clamped to [0, 1], where peak is the largest probability and n is the number of options or levels. All probability on one outcome gives 1; an even split gives 0. Single-option questions give 1.

Example

Request:

curl -X POST http://127.0.0.1:8787/v1/systemone \
  -H "Content-Type: application/json" \
  -d '{
    "state": "Hi, I have been trying to connect my Stripe account for 3 days and the integration keeps failing. I am losing sales. Please help ASAP.",
    "model": "jev-latest",
    "questions": {
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this",
        "criteria": {
          "billing": "Payment or subscription issues",
          "technical": "Bugs or integration problems",
          "sales": "Pricing or account questions"
        }
      },
      "frustration": {
        "type": "score",
        "instructions": "How frustrated the customer appears",
        "criteria": ["Calm, just stating facts", "Frustrated but civil", "Very angry, strong language"]
      },
      "is_urgent": {
        "type": "noul",
        "instructions": "The message conveys urgency or time-sensitivity"
      }
    }
  }'

Response:

{
  "model": "inception/mercury-2.5",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "probabilities": { "billing": 0.05, "technical": 0.9, "sales": 0.05 },
      "confidence": 0.85
    },
    "frustration": {
      "type": "score",
      "score": 1.05,
      "legend": {
        "0": "Calm, just stating facts",
        "1": "Frustrated but civil",
        "2": "Very angry, strong language"
      },
      "probabilities": { "0": 0.05, "1": 0.85, "2": 0.1 },
      "confidence": 0.775
    },
    "is_urgent": { "type": "noul", "noul": 0.95 }
  },
  "usage": { "input_tokens": 1172, "output_tokens": 138 }
}

GET /v1/models

Lists the configured model and the TypeSafe aliases this server accepts. The model request field is advisory, so this list is informational.

{
  "models": [
    {
      "name": "inception/mercury-2.5",
      "description": "inception/mercury-2.5 served through OpenRouter with structured outputs.",
      "release_date": "2026-09-08"
    },
    { "name": "jev-latest", "description": "TypeSafe alias accepted for compatibility; resolves to inception/mercury-2.5.", "release_date": "2026-09-08" },
    { "name": "jev-preview", "description": "TypeSafe alias accepted for compatibility; resolves to inception/mercury-2.5.", "release_date": "2026-09-08" },
    { "name": "jev-1.13.0", "description": "TypeSafe version id accepted for compatibility; resolves to inception/mercury-2.5.", "release_date": "2026-09-08" }
  ]
}

With JEB_PROVIDER=inception the descriptions name Inception's API instead. When OPENROUTER_MODEL (or legacy MODEL_ID) points elsewhere, that slug is listed first and inception/mercury-2.5 is appended as the default slug; the same applies to INCEPTION_MODEL with mercury-2.5.

GET /health

{ "ok": true }

GET /

{
  "name": "jeb",
  "description": "A TypeSafe Jev-compatible structured evaluation API powered by Inception Mercury 2.5.",
  "provider": "openrouter",
  "model": "inception/mercury-2.5",
  "endpoints": {
    "systemone": "POST /v1/systemone",
    "models": "GET /v1/models",
    "health": "GET /health"
  }
}

Errors

Errors use the HTTP status codes TypeSafe documents, with a JSON body:

{
  "error": {
    "message": "Score question 'severity' must have at least 2 levels.",
    "type": "invalid_request_error",
    "param": "questions.severity.criteria"
  }
}
Status type When
401 authentication_error API_KEY is set and the bearer token is missing or wrong.
404 not_found_error Unknown path.
405 invalid_request_error Wrong method for a known path.
422 invalid_request_error Body failed validation. param names the offending field, for example state, questions.<id>.criteria, or reasoning_effort.
429 rate_limit_error The model provider rate-limited the request. retry-after is passed through when present. Back off and retry.
500 internal_error Unhandled server error, including an invalid REASONING_EFFORT or JEB_PROVIDER value, or a missing key for the selected provider.
502 upstream_error The model provider returned an error or malformed content that is not retryable.
529 overloaded_error The provider failed, timed out, or is temporarily overloaded. Retry after a short delay.

Validation enforces TypeSafe's documented limits: 2–10 Score levels, at most 255 Choice options, and a non-empty questions map. state accepts any JSON value, including null. State size limits are not enforced.

Extensions and deviations from TypeSafe

  1. reasoning_effort (and the reasoningEffort alias) is an extension. Accepted values are none, low, medium, and high, the efforts Mercury 2.5 advertises. The request value overrides the REASONING_EFFORT server default; when neither is set, reasoning is disabled for the default model. Other effort values (minimal, max, xhigh) return 422.
  2. model is advisory. Any model name is accepted and resolves to the configured MODEL_ID. The response reports the model that actually answered. GET /v1/models lists the aliases kept for SDK compatibility.
  3. GET /v1/models, GET /health, and GET / are not part of the TypeSafe API.
  4. Probabilities are instructed, not natively calibrated. Jev is trained for calibrated decisions; Mercury 2.5 is prompted to distribute probability mass honestly. The shapes and arithmetic match Jev, but values may need retuning if you have thresholds tuned against real Jev answers.
  5. confidence uses the formula from TypeSafe's confidence explorer and is not rounded to two decimals, so it can show more precision than TypeSafe's example responses.
  6. instructions is optional. TypeSafe's HTTP reference marks it required, but their SDKs treat it as optional; this server accepts missing or null instructions on any question.
  7. usage.output_tokens includes reasoning tokens when reasoning is enabled, matching the provider's completion token count.
  8. Error message text may differ from api.typesafe.ai for statuses TypeSafe does not document (404, 405, 500), and upstream provider failures are mapped onto 429, 529, and 502.