LatentKit

Decisions

POST /v1/decisions — typed decisions and calibrated probabilities instead of generated text.

POST /v1/decisions returns typed answers rather than prose. You send a state and a map of typed questions; the model evaluates them and returns one answer per question, each with calibrated probabilities.

This is the endpoint for the decisions your program makes, not the text a person reads: intent routing, moderation and triage, relevance scoring, and agent context retention. The assigned route must include a model with the decisions capability.

Request

curl https://ai.latentkit.com/v1/decisions \
  -H "Authorization: Bearer $LATENTKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "My card was declined twice and nobody has replied to my email.",
    "questions": {
      "intent": {
        "type": "choice",
        "instructions": "Which category applies?",
        "criteria": {
          "billing": "Payment-related",
          "technical": "Technical issue",
          "other": "Anything else"
        }
      },
      "urgency": {
        "type": "score",
        "instructions": "How urgent is this?",
        "criteria": ["not urgent", "somewhat urgent", "very urgent"]
      },
      "complaint": {
        "type": "noul",
        "instructions": "Is the customer complaining?"
      }
    }
  }'

Fields

FieldDescription
stateWhat to evaluate: a plain string, or structured data (required)
questionsMap of your own question id to a question object; at least one (required)
purposeRouting label configured for your app
response_profilefast, balanced, or thinking when the route allows overrides

There is no stream, temperature, max_tokens, tools, or response_format. A decision model has no dial for any of them, so sending one returns a 400 naming the field rather than being silently dropped.

state and instructions each accept a string, an object, or an array. Structured values reach the model unchanged, so you can put a record in one field and the question in another and refer to the data by name.

Question types

TypecriteriaAnswer
noulOptional {"true": …, "false": …}noul — probability between 0 and 1 (no confidence)
choiceRequired object, up to 255 optionschoice (your option key), probabilities, confidence
scoreRequired array of 2–10 level descriptions, lowest firstscore (probability-weighted), legend, probabilities, confidence

Question ids are yours. The answers come back keyed by the same ids.

SDK

const decision = await client.decisions.create({
  state: 'My card was declined twice and nobody has replied to my email.',
  questions: {
    intent: {
      type: 'choice',
      instructions: 'Which category applies?',
      criteria: { billing: 'Payment-related', technical: 'Technical issue', other: 'Anything else' },
    },
  },
});

if (decision.answers.intent.choice === 'billing') {
  // route to the billing queue
}
decision = client.decisions.create(
    state="My card was declined twice and nobody has replied to my email.",
    questions={
        "intent": {
            "type": "choice",
            "instructions": "Which category applies?",
            "criteria": {
                "billing": "Payment-related",
                "technical": "Technical issue",
                "other": "Anything else",
            },
        }
    },
)

if decision["answers"]["intent"]["choice"] == "billing":
    ...
latentkit decide "My card was declined twice" --ask intent:choice:billing,technical,other

Response

{
  "provider": "typesafe",
  "model": "jev-1.13.0",
  "content": "",
  "answers": {
    "intent": {
      "type": "choice",
      "choice": "billing",
      "probabilities": { "billing": 0.94, "technical": 0.04, "other": 0.02 },
      "confidence": 0.94
    },
    "urgency": {
      "type": "score",
      "score": 2.05,
      "legend": { "0": "not urgent", "1": "somewhat urgent", "2": "very urgent" },
      "confidence": 0.71
    },
    "complaint": { "type": "noul", "noul": 0.88 }
  },
  "usage": { "input_tokens": 318, "output_tokens": 34, "total_tokens": 352 }
}

Decision responses use the unified runtime envelope (id, provider, model, policy_version_id, usage) with two differences worth knowing:

  • content is always empty. A decision is machine-readable output and is never serialized as assistant text. Read answers.
  • output_tokens is reported but free. TypeSafe charges per input token and prices output tokens at zero, so billing is input-only. The count is still recorded in your usage and analytics.

Routing

Decision routes and chat routes never overlap. A decision model can never be selected for /v1/chat, and a chat model can never be selected for /v1/decisions — there is no silent fallback from a decision to a generated answer in either direction.

Troubleshooting

  • NO_HEALTHY_PROVIDER means no route model has the decisions capability. Check the route's model list in the console.
  • A 400 naming a field such as temperature or stream means the request carried a generative option this endpoint does not accept. Remove it.

On this page