THX-02 API
HAL-X / Decision API

THX-02.
Decisions that think.

THX-02 reads a state (a message, a document, a JSON record or up to four images) and answers typed questions with a calibrated probability for every option. Most decisions take one forward pass. When a question needs a calculation or a chain of logic, THX-02 writes a short thought first and decides with it.

50–150 msdirect decision
~80 msmedian with thinking on auto
Text + imagesup to 4 images per state
Calibratedprobabilities you can threshold
Get started How thinking works https://api.hal-x.ai
01 / Overview

One request. Every decision about a state.

Products are built from small decisions: which team owns a ticket, whether a payment looks like fraud, whether a loan application passes policy, which button an agent should press, what a receipt says. THX-02 makes these decisions and tells you how sure it is, so you can act automatically on confident answers and send the rest to a person.

The options are part of the request. A new category, a new check or a new routing target needs no training: you change the question. THX-02 only scores the options you give it, so it cannot answer with something that is not on your list.

PropertyValue
Model idthx-02 (send it as "model"; without it the request goes to THX-01)
Base URLhttps://api.hal-x.ai
EndpointPOST /v1/decide, alias POST /v1/systemone; batches at /v1/decide/batch
AccessFree without a key (60 decisions per minute per IP), or with an API key for production traffic
Question typeschoice, noul (yes / no), score (ordered scale)
InputsText or JSON state, plus up to 4 images (photos, screenshots, charts, documents)
Thinking"think": false | "auto" | true; thoughts can be returned with "return_thoughts": true
Latency50–150 ms direct; median about 80 ms with "auto"; 0.5–2 s with true (plus network)
LanguagesMultilingual: Azerbaijani, Russian, English, Turkish, Kazakh, Uzbek, Chinese, Spanish and more
02 / Quickstart

Your first decision.

  1. Get an API key from your HAL-X contact or the HAL-X console. The key needs the THX decisions endpoint with model thx-02.
  2. Send a state and the questions you want answered, with "model": "thx-02".
  3. Read answers: every question gets its choice and the full probability distribution.
curl https://api.hal-x.ai/v1/decide \
  -H "Authorization: Bearer $HALX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "thx-02",
    "state": "Salam, kartımdan iki dəfə pul çıxılıb, geri qaytarın zəhmət olmasa.",
    "questions": {
      "team": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "billing": "Charges and refunds",
          "support": "Technical problems",
          "sales":   "Buying a product"
        }
      },
      "refund": { "type": "noul", "instructions": "Does the customer ask for a refund?" },
      "anger":  { "type": "score", "instructions": "How frustrated is the customer?",
                  "criteria": ["calm", "annoyed", "angry"] }
    }
  }'
import os, requests

r = requests.post(
    "https://api.hal-x.ai/v1/decide",
    headers={"Authorization": f"Bearer {os.environ['HALX_API_KEY']}"},
    json={
        "model": "thx-02",
        "state": "Salam, kartımdan iki dəfə pul çıxılıb, geri qaytarın zəhmət olmasa.",
        "questions": {
            "team": {"type": "choice", "instructions": "Which team should handle this?",
                     "criteria": {"billing": "Charges and refunds",
                                  "support": "Technical problems",
                                  "sales": "Buying a product"}},
            "refund": {"type": "noul", "instructions": "Does the customer ask for a refund?"},
            "anger": {"type": "score", "instructions": "How frustrated is the customer?",
                      "criteria": ["calm", "annoyed", "angry"]},
        },
    },
    timeout=30,
)
r.raise_for_status()
a = r.json()["answers"]
print(a["team"]["choice"], a["refund"]["noul"])
const res = await fetch("https://api.hal-x.ai/v1/decide", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.HALX_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "thx-02",
    state: "Salam, kartımdan iki dəfə pul çıxılıb, geri qaytarın zəhmət olmasa.",
    questions: {
      team: { type: "choice", instructions: "Which team should handle this?",
              criteria: { billing: "Charges and refunds", support: "Technical problems", sales: "Buying a product" } },
      refund: { type: "noul", instructions: "Does the customer ask for a refund?" },
      anger: { type: "score", instructions: "How frustrated is the customer?", criteria: ["calm", "annoyed", "angry"] },
    },
  }),
  signal: AbortSignal.timeout(30000),
});
const { answers } = await res.json();
console.log(answers.team.choice, answers.refund.noul);

Response:

200 OK · application/json
{
  "model": "THX-02",
  "answers": {
    "team":   { "type": "choice", "choice": "billing", "confidence": 0.968,
                "probabilities": { "billing": 0.979, "support": 0.017, "sales": 0.004 } },
    "refund": { "type": "noul", "noul": 0.957 },
    "anger":  { "type": "score", "score": 0.50, "confidence": 0.255,
                "probabilities": { "0": 0.574, "1": 0.355, "2": 0.071 },
                "legend": { "0": "calm", "1": "annoyed", "2": "angry" } }
  },
  "usage": { "input_tokens": 402, "output_tokens": 0, "thoughts": 0 },
  "latency_ms": 118.4
}
03 / Authentication

Free to try. A key for production.

Send your key in the Authorization header as a bearer token, or in X-API-Key. Keys start with sk-. Keep them on your server; never ship them in a browser or mobile app.

headers
Authorization: Bearer sk-...
Content-Type: application/json

A key grants endpoints and models separately. For THX-02 it needs the THX decisions endpoint (/v1/systemone, which covers all four decision paths) with the model thx-02 enabled. Keys can also carry a concurrency limit; requests beyond it are rejected with 429.

Free tierRequests without a key are served by THX-01 and THX-02, limited per client IP (THX-02: 60 decisions per minute; every question counts as one decision). Over the limit you get 429 with Retry-After; every response carries X-RateLimit-Remaining-Decisions. Use a key for production traffic and higher volume.
04 / Endpoints

Four paths, one grant.

MethodPathUse
POST/v1/decideOne state and its questions
POST/v1/decide/batchUp to 1,024 states in one call: {"model": "thx-02", "items": [...]}
POST/v1/systemone, /v1/systemone/batchThe same endpoints under their TypeSafe-compatible names
GET/v1/modelsModels your key can use

The model is chosen by the body's "model", not by the path. Every path works with both THX-01 and THX-02.

05 / Request body

A state, and questions about it.

FieldTypeDescription
modelrequiredstring"thx-02". Without it the request goes to THX-01
staterequiredstring or objectWhat the decision is about: text, or a JSON record (ticket, transaction, form, agent trace). Use "" when the images are the whole state
questionsrequiredobject1 to 64 named questions. The names are yours and come back as keys of answers
imagesoptionalstring[]Up to 4 images as data URIs (data:image/png;base64,...) or raw base64. See Images
thinkoptionalfalse, "auto", trueDefault false. See Thinking
return_thoughtsoptionalbooleanInclude the thought text in each answer that used one
ttaoptional1 or 4Read choice options in several orders and average. Default 1. See Option-order averaging

Each question is an object:

FieldDescription
typerequired"choice", "noul" or "score"
instructionsrequiredThe question, in plain language. "question" is accepted as an alias
criteriachoice, scoreFor choice: {"key": "what this option means", ...}, 2 to 255 options. For score: an ordered list, lowest first
06 / Question types

Three shapes cover almost every decision.

Pick one

choice

One option out of a list you define: routing, classification, next action, the right value on a chart.

Yes or no

noul

The probability that a statement is true: policy checks, fraud flags, "does this contain personal data".

Ordered scale

score

A level on a scale: urgency, sentiment, risk, quality. Returns the expected level and the distribution.

choice

Give every option a key and a short description. The description is what the model reads, so write it as a definition, not a label.

question → answer
// question
"next_step": { "type": "choice", "instructions": "What should the agent do next?",
  "criteria": { "refund": "Issue a refund for the duplicate charge",
                "ask":    "Ask the customer for the order number",
                "escalate": "Hand over to a human agent" } }

// answer
"next_step": { "type": "choice", "choice": "ask", "confidence": 0.625,
  "probabilities": { "refund": 0.22, "ask": 0.75, "escalate": 0.03 } }

confidence says how far the winning option is above a uniform guess: 0 when every option is equally likely, 1 when one option takes everything. For thresholds, use the winning option's probability.

noul

A yes / no question. No criteria. The answer is the probability of yes.

question → answer
"has_pii": { "type": "noul", "instructions": "Does the message contain a card number?" }

"has_pii": { "type": "noul", "noul": 0.993 }

score

An ordered scale, lowest level first. The answer has the probability of every level (keys are the level indexes), the legend, score (the expected level, a number between 0 and the last index) and confidence (how tightly the probability sits around the most likely level).

question → answer
"risk": { "type": "score", "instructions": "How risky is this transaction?",
          "criteria": ["low", "medium", "high", "block"] }

"risk": { "type": "score", "score": 2.31, "confidence": 0.38,
          "probabilities": { "0": 0.04, "1": 0.11, "2": 0.35, "3": 0.50 },
          "legend": { "0": "low", "1": "medium", "2": "high", "3": "block" } }
07 / Thinking

Think only when it changes the answer.

Most decisions are recognition: the answer is visible in the state. Some need a step of work first, such as adding up debts against a policy limit, reading two values off a chart and subtracting them, or tracing what a piece of code returns. For those, THX-02 can think: it writes a short note with the facts or the calculation that decide the question, then answers again with the note in front of it.

1 · ReadEvery question is answered directly in one pass.
2 · GateTHX-02 checks how sure it is and whether the question needs step-by-step work.
3 · ThinkOnly for unsure questions that need it: a short thought (up to 96 tokens on auto).
4 · DecideTHX-02 re-reads with the thought and mixes both reads. A wrong thought cannot overturn a confident answer.
thinkBehaviourTypical latencyUse it for
falseDirect answer, no thoughts (default)50–150 msRouting, tagging, high-volume classification
"auto"Thinks only on questions that are unsure and need reasoningmedian ~80 ms; 0.3–1 s when it thinksProduction default when some questions involve numbers or logic
trueThinks on almost every question, with longer thoughts (up to 320 tokens)0.5–2 sAudits, reviews, cases where a person reads the reasoning

Add "return_thoughts": true to see the thought on every answer that used one. usage.thoughts counts thoughts and usage.output_tokens their tokens.

curl https://api.hal-x.ai/v1/decide \
  -H "Authorization: Bearer $HALX_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "thx-02",
    "state": "Applicant income $4,200/month; rent $1,500; car loan $450; cards $250; new loan payment $600. Policy: total debt payments (including rent) must not exceed 50% of income.",
    "questions": {
      "policy": { "type": "choice", "instructions": "Does the application pass the bank policy?",
                  "criteria": { "pass": "Within the limit", "fail": "Over the limit" } }
    },
    "think": true,
    "return_thoughts": true
  }'
r = requests.post(URL, headers=HEADERS, timeout=60, json={
    "model": "thx-02",
    "state": application_text,
    "questions": {"policy": {"type": "choice", "instructions": "Does the application pass the bank policy?",
                           "criteria": {"pass": "Within the limit", "fail": "Over the limit"}}},
    "think": True,
    "return_thoughts": True,
})
policy = r.json()["answers"]["policy"]
print(policy["choice"], policy.get("thought"))
200 OK
{
  "model": "THX-02",
  "answers": {
    "policy": {
      "type": "choice", "choice": "fail", "confidence": 0.509,
      "probabilities": { "pass": 0.245, "fail": 0.755 },
      "thought": "Total monthly debt payments = $1,500 (rent) + $450 (car) + $250 (cards) + $600 (new loan) = $2,800.
                  50% of income = $4,200 × 0.50 = $2,100. Since $2,800 > $2,100, the application exceeds the 50% limit."
    }
  },
  "usage": { "input_tokens": 318, "output_tokens": 74, "thoughts": 1 },
  "latency_ms": 667.2
}
Thoughts are working notesThey are written for the model, not for end users. They help a reviewer see why a decision was made; check them before showing them to customers.

Questions that share one type and one set of criteria (for example ten tags on the same text) are read together in a single pass and are not thought about. Images are supported with thinking.

08 / Images

Photos, screenshots, charts and documents.

Send up to 4 images in images, as data URIs or raw base64 (PNG, JPEG or WebP). They are read together with state; use an empty state when the images say everything. Keep the whole body under 8 MB: resize large photos to about 1,500 px on the long side.

import base64, os, requests

def data_uri(path):
    mime = "image/png" if path.endswith(".png") else "image/jpeg"
    return f"data:{mime};base64," + base64.b64encode(open(path, "rb").read()).decode()

r = requests.post("https://api.hal-x.ai/v1/decide", timeout=60,
    headers={"Authorization": f"Bearer {os.environ['HALX_API_KEY']}"},
    json={
        "model": "thx-02",
        "state": "",
        "images": [data_uri("receipt.jpg")],
        "questions": {
            "total": {"type": "choice", "instructions": "What is the total amount on the receipt?",
                      "criteria": {"a": "8.85", "b": "8.56", "c": "6.56"}},
            "legible": {"type": "noul", "instructions": "Is the receipt fully legible?"},
        },
    })
print(r.json()["answers"])
import { readFileSync } from "node:fs";

const img = "data:image/jpeg;base64," + readFileSync("receipt.jpg").toString("base64");
const res = await fetch("https://api.hal-x.ai/v1/decide", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.HALX_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    model: "thx-02", state: "", images: [img],
    questions: { legible: { type: "noul", instructions: "Is the receipt fully legible?" } },
  }),
});

Good uses: quality control from a camera, reading receipts and forms, checking screenshots in an agent loop, answering questions about charts and diagrams.

09 / Option-order averaging

tta: remove position bias.

With "tta": 4, every choice question is read with its options in four different orders and the results are averaged (geometric mean). Accuracy goes up on hard multiple-choice questions, and latency roughly doubles. noul and score questions are read once. Use it for evaluations and high-stakes decisions; keep the default 1 for high-volume traffic.

10 / Response body

Answers, usage and timing.

FieldDescription
model"THX-02"
answersOne entry per question name. Every answer has type; choice adds choice, probabilities, confidence; noul adds noul; score adds score, probabilities, legend, confidence. With return_thoughts, answers that used a thought add thought
usage.input_tokensTokens THX-02 read, including option texts and image positions
usage.output_tokensThought tokens written (0 without thinking)
usage.thoughtsHow many questions used a thought
latency_msTime spent in the model, without network

Every response carries an X-Request-ID header. Quote it when you contact support.

11 / Batch

Up to 1,024 states in one call.

Send {"model": "thx-02", "items": [...]} to /v1/decide/batch. Each item is a request body without model (state, questions and the optional fields). Results come back in the same order. A failed item is returned as {"error": "..."} in its place; the other items are unaffected.

POST /v1/decide/batch
{
  "model": "thx-02",
  "items": [
    { "state": "My package never arrived",
      "questions": { "team": { "type": "choice", "instructions": "Which team?",
                               "criteria": { "billing": "Charges", "shipping": "Deliveries" } } } },
    { "state": "I was charged twice",
      "questions": { "team": { "type": "choice", "instructions": "Which team?",
                               "criteria": { "billing": "Charges", "shipping": "Deliveries" } } } }
  ]
}

// → { "results": [ { "model": "THX-02", "answers": {...}, "usage": {...} }, { ... } ] }

Items are packed together on the GPU, so a batch is much faster than the same requests one by one. For very large jobs, send batches of 100 to 500 items from a few parallel workers.

12 / Errors

Clear errors, safe retries.

Errors use one envelope: {"error": {"type": ..., "message": ...}}. The message says what to fix.

StatusWhenWhat to do
400Malformed body: no state, empty questions, unknown type, a choice without two options, more than 64 questions, a body over 8 MBFix the request; do not retry
401Invalid or revoked keyCheck the key
403The key does not grant THX decisions or the model thx-02Ask for the grant
404Unknown modelUse thx-02
429The key's concurrency limit, or the free tier's per-IP limit, is reachedRetry after Retry-After
502 / 503 / 504The model server is unavailable or timed outRetry with exponential back-off; see status
13 / Limits

Limits.

LimitValue
Questions per state64
Options per choice2 to 255
Images per state4
Request body8 MB
Items per batch1,024
Request timeout120 s
Concurrent requestsSet per key
Thought length96 tokens on "auto", 320 on true
14 / Best practices

Getting the most out of THX-02.

  • Describe options, don't just name them. "billing": "Charges, refunds and invoices" beats "billing": "billing". Keep descriptions short and mutually exclusive, ideally in English; the state can be in any language.
  • Ask everything about a state in one request. Questions about the same state share one read, which is faster and cheaper than separate calls.
  • Put options in criteria, not in the state. The model chooses only among the criteria.
  • Use the probabilities. They are calibrated: act automatically above a threshold (for example 0.9) and send the rest to a person. Choose the threshold on a few hundred of your own labelled examples.
  • Add an escape option such as "other": "None of the above" when real inputs may fit nothing on the list.
  • Use "think": "auto" when questions involve arithmetic, dates, thresholds or multi-step rules; keep false for pure routing and tagging.
  • Send structured data as JSON. A JSON object in state keeps field names and values unambiguous.
  • Batch offline work. Back-fills, audits and evaluations should use /v1/decide/batch.
15 / Code examples

A small client with retries.

import os, time, requests

URL = "https://api.hal-x.ai/v1/decide"
HEADERS = {"Authorization": f"Bearer {os.environ['HALX_API_KEY']}"}

def decide(state, questions, think=False, images=None, retries=4):
    body = {"model": "thx-02", "state": state, "questions": questions, "think": think}
    if images:
        body["images"] = images
    for attempt in range(retries):
        r = requests.post(URL, headers=HEADERS, json=body, timeout=60)
        if r.status_code in (429, 502, 503, 504):
            time.sleep(0.5 * 2 ** attempt)
            continue
        r.raise_for_status()
        return r.json()["answers"]
    r.raise_for_status()

a = decide("Is it possible to pay my bill in installments?",
           {"intent": {"type": "choice", "instructions": "What does the customer want?",
                       "criteria": {"plan": "A payment plan or installments",
                                    "refund": "Money back", "other": "Anything else"}}},
           think="auto")
if a["intent"]["probabilities"][a["intent"]["choice"]] >= 0.9:
    route(a["intent"]["choice"])
else:
    send_to_human()
const URL = "https://api.hal-x.ai/v1/decide";

export async function decide(state, questions, { think = false, images } = {}) {
  const body = { model: "thx-02", state, questions, think, ...(images ? { images } : {}) };
  for (let attempt = 0; attempt < 4; attempt++) {
    const res = await fetch(URL, {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.HALX_API_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(60000),
    });
    if ([429, 502, 503, 504].includes(res.status)) {
      await new Promise((r) => setTimeout(r, 500 * 2 ** attempt));
      continue;
    }
    if (!res.ok) throw new Error(`THX-02 ${res.status}: ${await res.text()}`);
    return (await res.json()).answers;
  }
  throw new Error("THX-02 unavailable");
}
16 / From THX-01

Moving from THX-01.

THX-02 uses the same endpoints and the same request and answer shapes. To switch, set "model": "thx-02" and make sure your key grants it. Differences:

THX-01THX-02
AccessKey, or the free tierKey, or the free tier
Question typeschoice, noul, score, number, excerpt, citechoice, noul, score
ImagesNoUp to 4 per state
ThinkingNothink, return_thoughts
Question text fieldquestioninstructions (question also accepted)
Latency~10 ms50–150 ms direct
Best atVery high-volume text routingHarder decisions: knowledge, reasoning, documents, vision, agents

Keep number, excerpt and cite questions on THX-01; THX-02 rejects them with 400.

17 / Status and support

Status and support.

Live uptime, response time and incidents for THX-01 and THX-02 are on api.hal-x.ai/status/thx. For keys, higher limits or help, contact your HAL-X representative and include the X-Request-ID of the request in question.