SovrGPT Docs
API

Decision Engine (POST /v1/decisions)

Decisions instead of prose — state and questions with fixed options in, probabilities out. Private preview.

Private preview. The contract is stable, the operation is new: limits, prices and model versions may still change during the preview. Changes are announced in the changelog.

The Decision Engine answers questions with a fixed set of options — which team is responsible, how urgent is this, does a human need to review? It generates no text: for every question the model runs exactly one forward pass and we read the probability of each allowed option directly. That is why a decision comes back in milliseconds when warm, where a chat answer takes seconds.

Measured on 2026-09-20 from a machine in Jena, warm, a ticket text with four options: 65 ms per question on sovr-decision-v1, 198 ms on sovr-decision-v2; four questions in parallel 123 ms and 397 ms respectively. A chat answer to the same text on the same models takes 5–30 s. These are our measurements, not a guarantee — they depend on the provider, the load and the length of the state.

Base URL: https://sovrgpt.com/api/v1 · Scope: decisions · Auth: Authorization: Bearer sov_…

Models

ModelOperated inImagesCharacter
sovr-decision-v1 (default)Germany, approved infrastructure (BSI C5)yesvery fast, very decisive — probabilities almost always near 1.0 or 0
sovr-decision-v2EU (Ireland/Finland), zero retention by contractyesfiner-grained probabilities — for threshold logic

Both versions run on open base models under our control; which version applies without a model field and where it computes is listed per organisation under GET /v1/decisions/models. An owner or admin can change it under Decision Engine → Settings of this organisation.

Request a decision

curl https://sovrgpt.com/api/v1/decisions \
  -H "Authorization: Bearer $SOVR_KEY" -H "content-type: application/json" \
  -d '{
    "preset": "ticket-routing-v1",
    "state": "Since this morning I cannot log in. My invoice was also charged twice — please check this urgently."
  }'

Response (abridged, a real measurement from 2026-09-20 on sovr-decision-v2):

{
  "object": "decision",
  "id": "dec_3f1c…",
  "status": "completed",
  "model": { "id": "sovr-decision-v2", "runtime": "…", "operator": "TensorX Ltd", "country": "IE", "via": "platform" },
  "preset": { "id": "ticket-routing-v1", "version": "1.0.0" },
  "answers": {
    "team": {
      "type": "choice",
      "value": "support",
      "probabilities": { "support": 0.6512, "billing": 0.0367, "sales": 0.0044, "unclear": 0.3076 },
      "max_probability": 0.6512,
      "normalized_entropy": 0.58,
      "calibration": { "status": "uncalibrated" },
      "abstain": true,
      "abstain_reasons": ["review_only_policy", "calibration_unavailable"],
      "diagnostics": { "label_mass": 0.9967, "low_label_mass": false, "missing_labels": [] }
    },
    "urgency": { "type": "score", "value": 3, "expected_index": 3.29, "normalized_score": 0.82, "probabilities": { "0": 0.0002, "1": 0.0003, "2": 0.0017, "3": 0.7042, "4": 0.2936 }, "…": "…" },
    "needs_human": { "type": "binary", "value": true, "probability_true": 0.9933, "…": "…" },
    "language": { "type": "choice", "value": "de", "probabilities": { "de": 0.9993, "en": 0.0006, "other": 0 }, "…": "…" }
  },
  "policy": { "mode": "review_only", "threshold": 0.85 },
  "usage": { "input_tokens": 860, "output_tokens": 4, "evaluated_questions": 4, "forward_passes": 4 },
  "timing_ms": { "total": 412, "inference_max": 397, "inference_sum": 688 }
}

Request

FieldRequiredMeaning
state or messagesexactly oneThe state to judge: text, a JSON object/array — or a conversation ([{role, content}], text only). Treated as data, never as instructions.
questions or presetexactly oneYour own questions (below) or a server-side pack (GET /v1/decisions/presets).
modelnosovr-decision-v1 or sovr-decision-v2. Omitted: the organisation's default.
imagesnoUp to 2 images as [{ "data_url": "data:image/png;base64,…" }], ≤ 4 MB each. data: URLs only — the service never fetches remote URLs.
policyno{ "mode": "review_only" } (default) or { "mode": "threshold", "threshold": 0.85 }, see “Release”.
request_idnoFree-form, echoed back (correlation).

Question types

{
  "team":  { "type": "choice", "instructions": "Which team first?", "options": [ { "id": "support", "description": "technical" }, { "id": "billing" }, { "id": "unclear", "description": "cannot be assigned" } ] },
  "urgency": { "type": "score", "instructions": "How urgent?", "levels": ["not", "slightly", "normal", "urgent", "immediately"] },
  "needs_human": { "type": "binary", "instructions": "Does a human need to review?", "true_description": "yes", "false_description": "no" }
}
TypeResult
choice — 2–26 optionsvalue = option id with the highest probability, probabilities over all options
score — 2–10 ordered levelsvalue = most likely level (0-based), expected_index = Σ p·i, normalized_score = expected/(n−1). A score is not a physical scale.
binaryvalue = true/false, probability_true

Order matters: options and questions are evaluated in source order. A model may react to the order — if you need to rule that out, test with the options swapped.

Limits (preview)

1–16 questions · state ≤ 16,000 characters · question ≤ 1,000 characters · description ≤ 512 characters · body ≤ 64 KiB. Too large → 413. Unknown fields are rejected (400), not ignored — a tenant_id or tools in the body must not look as if it had an effect.

What the numbers mean — and what they do not

  • probabilities is the distribution over your options. max_probability: 0.92 means the model gives that option 92 % of the mass. It does not mean it is 92 % likely to be correct. calibration.status is always uncalibrated in the preview.
  • diagnostics.label_mass tells you how much probability sat on the allowed options at all. Below 0.5 the model wanted to write something else — the distribution is then an artefact, and low_label_mass is true.
  • sovr-decision-v1 mostly answers 1.0 / 0 / 0 — very decisive, few shades. For “unsure → human review” the domain option (unclear, other) is more reliable there than a percentage threshold.

Release (abstain)

abstain: true means: do not automate unreviewed. The evaluation is complete nonetheless — it is a recommendation to your workflow, not an action.

  • review_only (default): every answer carries abstain: true.
  • threshold: abstain drops to false when max_probability ≥ threshold, the label mass is sound and the chosen option is not unclear/other/unknown. The reasons are always listed in abstain_reasons.

Binding approvals, access rights and actions stay with your application. A typed wrong answer is still a wrong answer.

Errors

StatuscodeMeaning
400invalid_request_error, invalid_json, images_unsupportedinvalid body
401key missing, invalid or revoked
402org_spend_limit, user_spend_limitspending limit reached
403missing_scopekey lacks the decisions scope
404unknown_preset, model_not_found
413request_too_large
502 / 504upstream_error, upstream_timeoutthe runtime did not answer usably/in time — no partial answer
503runtime_unavailableno measured runtime is reachable in this deployment — a different model is never used silently

Privacy

State, questions and answers are not stored. Billing counts input tokens (plus one token per question); only these numbers appear in your usage. Images are never fetched: only data: URLs are accepted. The processing location is part of every response (model.operator, model.country).

Further endpoints

  • GET /v1/decisions/presets — server-side question packs with example states.
  • GET /v1/decisions/models — this organisation's versions, default, operator, country, measurements, limits.

A playground with real inference is available when logged in under Decision Engine in the sidebar.

Decision Engine (POST /v1/decisions)