1. Core Concepts
  2. System One Models

TypeSafe Jev is a System One model: you give it context and typed questions, and it returns decisions, scores, and probabilities instead of generated prose. Your code can use those answers directly to route a support ticket, flag a request for review, or evaluate an item against a rubric.

Use Backboard’s POST /threads/messages endpoint, Python send_message, or TypeScript sendMessage. You do not need to create an assistant or thread first.

This guide covers cURL and both SDKs. For an SDK-focused walkthrough, see System One SDK examples.

Before you start

  • Set BACKBOARD_API_KEY in your server environment. You use your Backboard key, not a separate TypeSafe key.
  • For SDK examples, use a version of backboard-sdk that includes system_one (Python) or systemOne (TypeScript).
  • Set the provider to typesafe, the model to jev-latest, and stream to false.
  • Put one or more named questions in system_one.questions. Your question names become the answer keys.

Run SDK examples on your server; never expose your API key in browser code.

Quick start: evaluate a support request

This example asks three questions about the same message in one call:

QuestionTypeWhat your code receives
refund_requestednoulProbability that the customer wants a refund
departmentchoiceSelected team and probabilities for each team
urgencyscoreA score against ordered urgency levels
curl --fail-with-body https://app.backboard.io/api/threads/messages \
  -H "X-API-Key: $BACKBOARD_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "content": "I was charged twice. Please refund me urgently.",
    "llm_provider": "typesafe",
    "model_name": "jev-latest",
    "stream": false,
    "system_one": {
      "questions": {
        "refund_requested": {
          "type": "noul",
          "instructions": "Does the customer request a refund?"
        },
        "department": {
          "type": "choice",
          "instructions": "Which team should handle this request?",
          "criteria": {
            "billing": "Payments, charges, and refunds",
            "technical": "Software errors and troubleshooting",
            "general": "Other enquiries"
          }
        },
        "urgency": {
          "type": "score",
          "instructions": "How urgent is this request?",
          "criteria": ["Low: routine enquiry", "Medium: needs prompt attention", "High: immediate action needed"]
        }
      }
    }
  }'

Choose a question type

Noul: estimate whether something is true

Use type: "noul" for a yes/no proposition. The answer’s noul field is a probability from 0 to 1, not a boolean: 0.97 means an estimated 97% probability that the proposition is true.

instructions is required. Optionally define what true and false mean with criteria:

{
  "type": "noul",
  "instructions": "Does the customer request a refund?",
  "criteria": {
    "true": "The customer explicitly asks for money back",
    "false": "The customer does not ask for money back"
  }
}

Choose thresholds for your application and evaluate them on representative data. An uncertain result can go to human review; a high probability alone should not authorize a payment or another irreversible action.

Choice: select one named option

Use type: "choice" for categories such as departments, intent labels, or workflow routes. criteria maps your option names to descriptions; at least one option is required. A description can be null, but clear descriptions help distinguish similar options.

The answer includes:

  • choice: the selected option name, such as billing.
  • probabilities: the distribution across the options.
  • confidence: a summary of how concentrated that distribution is. This is not a guarantee of correctness.

Include an explicit fallback option such as general when none of the specific categories may apply.

Score: evaluate against an ordered rubric

Use type: "score" for urgency, quality, relevance, or other ordered assessments. criteria must contain at least two strings, ordered from the low end to the high end of your rubric.

The answer includes score, legend, probabilities, and confidence. Use the returned legend to interpret the numeric scale and the probability keys; do not assume the score is a percentage or an index into your criteria array.

All questions require instructions, which may be a string, object, or array. Text instructions are usually the simplest starting point. Multiple question types can share one request, and questions must be sent again on every inference turn.

Read and act on answers

For cURL, read system_one.answers from the JSON response. Python exposes result.system_one.answers; TypeScript exposes result.systemOne.answers. The answer fields inside those objects keep their API names.

Full response example

The quick-start request returns a non-streaming JSON response shaped like this, with all three answers under system_one.answers. IDs, timestamps, model resolution, token counts, and predictions below are illustrative, not a recorded live response. Optional fields unrelated to this evaluation are shown as null.

{
  "message": "Message added successfully",
  "thread_id": "11111111-1111-4111-8111-111111111111",
  "assistant_id": "22222222-2222-4222-8222-222222222222",
  "message_id": "33333333-3333-4333-8333-333333333333",
  "run_id": "44444444-4444-4444-8444-444444444444",
  "role": "assistant",
  "status": "COMPLETED",
  "content": "{\"refund_requested\": {\"type\": \"noul\", \"noul\": 0.97}, \"department\": {\"type\": \"choice\", \"choice\": \"billing\", \"probabilities\": {\"billing\": 0.96, \"technical\": 0.01, \"general\": 0.03}, \"confidence\": 0.82}, \"urgency\": {\"type\": \"score\", \"score\": 1.7, \"legend\": {\"0\": \"Low: routine enquiry\", \"1\": \"Medium: needs prompt attention\", \"2\": \"High: immediate action needed\"}, \"probabilities\": {\"0\": 0.05, \"1\": 0.2, \"2\": 0.75}, \"confidence\": 0.5}}",
  "system_one": {
    "model": "jev-1.13.0",
    "answers": {
      "refund_requested": {"type": "noul", "noul": 0.97},
      "department": {
        "type": "choice",
        "choice": "billing",
        "probabilities": {"billing": 0.96, "technical": 0.01, "general": 0.03},
        "confidence": 0.82
      },
      "urgency": {
        "type": "score",
        "score": 1.7,
        "legend": {
          "0": "Low: routine enquiry",
          "1": "Medium: needs prompt attention",
          "2": "High: immediate action needed"
        },
        "probabilities": {"0": 0.05, "1": 0.2, "2": 0.75},
        "confidence": 0.5
      }
    },
    "usage": {"input_tokens": 312, "output_tokens": 0}
  },
  "model_provider": "typesafe",
  "model_name": "jev-1.13.0",
  "input_tokens": 312,
  "output_tokens": 0,
  "total_tokens": 312,
  "created_at": "2026-09-17T12:00:00Z",
  "timestamp": "2026-09-17T12:00:01Z",
  "tool_calls": null,
  "memory_operation_id": null,
  "retrieved_memories": null,
  "retrieved_files": null,
  "retrieved_files_count": 0,
  "reasoning": null,
  "attachments": null,
  "generated_media": null,
  "voice_records": null,
  "context_usage": null
}

Interpret the result

  • Refund: noul: 0.97 estimates a 97% probability that a refund is requested. It is not a refund authorization.
  • Department: choice: "billing" selects the billing team. Its option probability is 0.96; confidence is a separate distribution summary, not the selected option’s probability or a guarantee of correctness.
  • Urgency: the returned legend maps 0 to Low, 1 to Medium, and 2 to High. Here score: 1.7 lies between Medium and High, while probabilities["2"]: 0.75 assigns 75% probability to the High level. The score can be fractional: do not use it as an array index or interpret it as 1.7%.
  • Content: content is a JSON string containing the same answers as system_one.answers, not generated prose. Prefer the structured answers object; the backslashes above are JSON string escaping.
  • Usage: top-level token counts mirror system_one.usage, and total_tokens is their sum. Zero output tokens in this example does not mean the response has no answers; use the reported usage rather than counting the response JSON.
  • Identifiers: reuse thread_id to continue the conversation; message_id identifies this assistant reply. model_name and system_one.model identify the resolved model rather than necessarily echoing the requested alias.

Use the answers in your application

These snippets continue the quick-start examples. Put the Python snippet inside main(), after checking result.system_one.

answers = result.system_one.answers
refund = answers["refund_requested"]
department = answers["department"]
urgency = answers["urgency"]

if refund["noul"] >= 0.9 and department["choice"] == "billing":
    print("Queue for billing review")
else:
    print("Queue for general review")

print("Urgency:", urgency["score"], "Scale:", urgency["legend"])
print("Team probabilities:", department["probabilities"])

The response also contains the usual thread and message identifiers. system_one.model identifies the resolved model, and system_one.usage contains input_tokens and output_tokens. The assistant message’s content is a JSON-encoded version of the answers; use the structured result rather than parsing prose.

Evaluate structured application data

Use system_one.state to pass an object, array, or string alongside your message. This is useful when the decision depends on data already in your application, such as transaction records or account status.

The following examples evaluate the same transaction data. The SDK snippets reuse the client from the quick start; put the Python call inside its async with block.

curl --fail-with-body https://app.backboard.io/api/threads/messages \
  -H "X-API-Key: $BACKBOARD_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "content": "Check these charges for a possible duplicate.",
    "llm_provider": "typesafe",
    "model_name": "jev-latest",
    "stream": false,
    "system_one": {
      "state": {"charges": [{"amount": 49, "currency": "USD", "order": "A123"}, {"amount": 49, "currency": "USD", "order": "A123"}]},
      "questions": {
        "possible_duplicate": {"type": "noul", "instructions": "Do the charges in state appear to be a duplicate payment for the same order?"}
      }
    }
  }'

Backboard includes conversation messages, the effective system_prompt, available document_summaries, and your optional state in the context sent to Jev. Questions can refer to those fields. Supplying state does not replace conversation history. If you omit content, Backboard saves a serialized representation of the state as the user message.

For independent evaluations, omit thread_id to start a new conversation. For an evolving case, reuse the thread and pass updated state on the next turn.

Continue a conversation

Reuse the returned thread ID to evaluate new information alongside earlier messages. Send the provider, model, and questions again; they are per-turn settings.

In cURL, set THREAD_ID to the thread_id from the first response. The SDK snippets reuse result and client from the quick start; place the Python snippet inside its async with block.

curl --fail-with-body https://app.backboard.io/api/threads/messages \
  -H "X-API-Key: $BACKBOARD_API_KEY" \
  -H "Content-Type: application/json" \
  --data "{
    \"thread_id\": \"$THREAD_ID\",
    \"content\": \"The second charge was reversed. I no longer need a refund.\",
    \"llm_provider\": \"typesafe\",
    \"model_name\": \"jev-latest\",
    \"stream\": false,
    \"system_one\": {
      \"questions\": {
        \"refund_still_needed\": {\"type\": \"noul\", \"instructions\": \"Considering the latest update and earlier messages, does the customer still need a refund?\"}
      }
    }
  }"

You can also use add_message(thread_id, ...) in Python or addMessage(threadId, {...}) in TypeScript with the same System One configuration. To start a separate conversation that shares an assistant’s configuration, pass assistant_id instead of thread_id (assistantId in TypeScript).

Models and usage

Use jev-latest for the latest alias, jev-1.13.0 for a pinned version, or jev-preview for the preview alias. Aliases can change; read the resolved system_one.model when recording evaluations.

List available models and their catalog pricing:

cURL
curl --fail-with-body \
  'https://app.backboard.io/api/models?provider=typesafe&model_type=system_one' \
  -H "X-API-Key: $BACKBOARD_API_KEY"

Normal Backboard credit checks and usage billing apply. Questions and conversation context contribute to input usage. To save a message without inference, use send_to_llm: false in JSON, send_to_llm="false" in Python, or sendToLlm: 'false' in TypeScript; that turn does not call or charge Jev.

Request checklist

  • Use non-streaming requests: stream=false.
  • Provide at least one question: every question needs a supported type and instructions; Choice and Score also need criteria.
  • Pass text or structured data: attachment input, voice, and media generation are not supported on this path.
  • Keep generation controls out: do not send temperature, max_tokens, response_format, thinking settings, tools, or web-search options. Typed answers do not require json_output.
  • Check inherited settings: if reusing an assistant with tools, pass tools=[]; disable enabled web-search or media settings for the turn.

Malformed questions or unsupported controls return 422. An insufficient-credit response is 402; check your Backboard balance before retrying.