Docs

Decision API reference

Ask the six Decision 3.0 models typed questions about text, images and videos, through the vLLM Semantic Router System One API. Free.

On this page
  1. Overview
  2. Authentication
  3. Request format
  4. Responses
  5. Models
  6. Limits
  7. Rate limits
  8. Errors
  9. Logging
  10. Help

Overview #

A decision model answers typed questions about an input with calibrated probabilities, from one forward pass per question instead of generated text. Send an input (the state), optionally with images and videos, and the questions you need answered; every answer comes back in one response.

Base URL: https://decision.vllm-sr.ai

EndpointKeyPurpose
POST /v1/systemoneYesAsk questions about an input. POST /v1/decisions is the same endpoint.
GET /v1/systemone/modelsNoThe published models.
GET /api/infoNoModels, limits and rate limits, as JSON.
GET /api/statusNoThe state, latency and uptime of every model, and incidents. The status page shows it.
GET /api/statsNoUsage statistics: totals, requests and tokens over time, the share of each model.
GET /api/keys/selfYesToday's usage and the limits of your key.
DELETE /api/keys/selfYesRevoke your key. Its stored data is kept.
POST /api/keys/self/forgetYesRevoke your key and delete everything stored for it within 30 days.
First request
curl -s https://decision.vllm-sr.ai/v1/systemone \
  -H "Authorization: Bearer $DECISION_API_KEY" \
  -H 'content-type: application/json' -d '{
  "model": "vllm-sr/d3-flash",
  "state": "Write a Python function that merges two sorted lists.",
  "questions": {
    "kind": {
      "type": "choice",
      "instructions": "What kind of work is this?",
      "criteria": {
        "code": "Writing or fixing code",
        "math": "Mathematics",
        "chat": "Anything else"
      }
    },
    "reasoning": {
      "type": "noul",
      "instructions": "Does answering this need multi-step reasoning?"
    }
  }
}'
Response
{
  "answers": {
    "kind": {
      "type": "choice",
      "choice": "code",
      "probabilities": {"code": 0.9986, "math": 0.0004, "chat": 0.0010},
      "confidence": 0.9894
    },
    "reasoning": {"type": "noul", "noul": 0.4143}
  },
  "model": "vllm-sr/d3-flash",
  "usage": {"input_tokens": 213, "output_tokens": 0}
}

A real response of vllm-sr/d3-flash (2026-10-13). Numbers rounded to 4 decimal places.

Authentication #

Send your API key in either header, on every endpoint that needs one:

Authorization: Bearer sk-vsr-...
X-API-Key: sk-vsr-...

Keys are sk-vsr- followed by 43 letters and digits ([0-9A-Za-z]). Get a key on the home page: one click, no account. The key is shown once and the service stores only a hash of it, so a lost key cannot be recovered; create a new one instead. Code that runs in a browser should use X-API-Key, because the browser may use Authorization for its own login.

Keep keys secret. Do not put them in web pages, apps or public repositories: anyone with your key can use its quota. Keys used for abuse may be revoked.

Check and revoke a key

GET /api/keys/self returns today's usage and the limits of the key that calls it. Days are UTC: usage_today resets at 00:00 UTC and adds up every model, while the limits apply per model. In a browser, the Keys page shows the same and revokes or deletes a key.

Check a key
curl -s https://decision.vllm-sr.ai/api/keys/self \
  -H "Authorization: Bearer $DECISION_API_KEY"
{
  "key_id": "key_...", "prefix": "sk-vsr-AbCd", "label": "...", "status": "active", "created_at": "...",
  "usage_today": {"requests": n, "input_tokens": n},
  "limits": { same shape as rate_limits.per_key in /api/info }
}

DELETE /api/keys/self revokes the key that calls it and answers 204. Requests with a revoked key fail with 401 invalid_api_key. Revoking keeps the data stored for the key, and a revoked key can no longer ask for its deletion: to delete the data, use forget instead.

Revoke a key
curl -s -X DELETE https://decision.vllm-sr.ai/api/keys/self \
  -H "Authorization: Bearer $DECISION_API_KEY"

Delete your data

POST /api/keys/self/forget (no body) revokes the key that calls it at once and deletes everything stored for it, within 30 days and including backups: its logged requests and responses, the media only this key sent, and its metadata. From the moment you ask, its data is left out of exports for model improvement. It answers 202. The Keys page has the same action: Delete key and its data.

Delete everything stored for a key
curl -s -X POST https://decision.vllm-sr.ai/api/keys/self/forget \
  -H "Authorization: Bearer $DECISION_API_KEY"
{"key_id": "key_...", "status": "scheduled", "message": "..."}

Create a key from a terminal

The home page creates keys in your browser. Without a browser, make the same two calls. POST /api/keys/challenge takes no body and returns a challenge: an opaque string to use as text. Find a nonce (a decimal string) such that the SHA-256 of the UTF-8 text challenge + ":" + nonce starts with at least difficulty zero bits, and send it back to POST /api/keys. Sending "accept_terms": true accepts the terms of use. A challenge can be used once and expires after 10 minutes, and each network address may ask for 20 challenges per minute (then 429 rate_limit_exceeded with Retry-After: 60).

create_key.py (Python 3, standard library)
import hashlib, itertools, json, urllib.request

BASE = "https://decision.vllm-sr.ai"

def post(path, body=None):
    data = b"" if body is None else json.dumps(body).encode()
    headers = {} if body is None else {"content-type": "application/json"}
    req = urllib.request.Request(BASE + path, data=data, headers=headers)
    with urllib.request.urlopen(req) as res:
        return json.load(res)

challenge = post("/api/keys/challenge")
target = 1 << (256 - challenge["difficulty"])
prefix = challenge["challenge"] + ":"

def solves(n):
    digest = hashlib.sha256(f"{prefix}{n}".encode()).digest()
    return int.from_bytes(digest, "big") < target

nonce = next(n for n in itertools.count() if solves(n))
key = post("/api/keys", {
    "challenge": challenge["challenge"],
    "nonce": str(nonce),
    "accept_terms": True,
})
print(key["api_key"])  # shown once: store it now

The number of new keys per network address is limited per hour and per day; see Rate limits. When it is reached, key creation answers 429 key_creation_limited with a Retry-After header.

Request format #

Tip: ask everything about one input in one request.

Put all the questions you have about the same text, image or video into one request. The media is processed once and the questions are answered together, which is faster than one request per question and counts as one request against the request limits (the token limits count the input of every question).

POST /v1/systemone takes a JSON body (content-type: application/json, UTF-8) in the vLLM Semantic Router System One format:

FieldTypeMeaning
modelstringA model ID from Models, such as vllm-sr/d3-flash.
statestringThe input the questions are about: text, or JSON written as text. Its length counts Unicode code points.
questionsobjectYour question IDs, each with its question. Answers come back under the same IDs.
imagesarray of stringsOptional. Base64 data URLs of PNG, JPEG or WebP images.
videosarray of stringsOptional. Base64 data URLs of MP4, WebM, QuickTime or Matroska videos.
statesobjectOptional. More inputs in the same call: each entry has its own state and questions (and may have images and videos), and is answered under the same name in the response's states.
optionsobjectOptional. {"return_meta": true} adds meta to the response: the model that answered (model_id, revision, model_sha256), its dtype (such as bfloat16) and numerics (approximate when an answer can move slightly with the requests batched with it, exact when it cannot), where it ran (engine, accelerator, device) and how long it took (queue_ms, compute_ms). The only other option accepted is deadline_ms; any other option, max_tokens and profile included, answers 400 invalid_request.

The question limit counts the questions of every entry of states too; see Limits.

Question types

Every question has a type and instructions, the question in plain language. Decision 3.0 models answer three types:

TypeAskscriteriaAnswer
choiceWhich option fits?An object of options, each with a description or null; at least 2 options.choice, the chosen option, and probabilities of every option.
noulYes or no?Optional: {"false": "...", "true": "..."} to describe each answer.noul, the probability of yes.
scoreHow much, on an ordered scale?An array of level descriptions, lowest first; 2 to 10 levels.score, the expected level (the first level is 0), and probabilities of every level.

The options limit (max_options) covers choice and noul criteria and score levels alike. Write instructions the way you would ask a colleague, and describe each option in a few words. All three types in one request:

Request body
{
  "model": "vllm-sr/d3-flash",
  "state": "The order arrived damaged yesterday. The customer has a receipt and asks for a replacement today.",
  "questions": {
    "route": {
      "type": "choice",
      "instructions": "Which team should handle this request?",
      "criteria": {
        "returns": "Refunds, replacements and damaged deliveries",
        "billing": "Payments, invoices and charges",
        "technical": "Product setup and faults"
      }
    },
    "receipt": {"type": "noul", "instructions": "Does the customer have a receipt?"},
    "urgency": {"type": "score", "instructions": "How urgent is this request?",
                "criteria": ["Routine", "Soon", "Today"]}
  }
}

A question takes only the fields of its type. An invalid question is answered with an error of its own and the other questions are answered as usual; a request in which no question is valid fails with 400 invalid_request.

Images and videos

Send images in images as base64 data URLs (PNG, JPEG or WebP) and videos in videos (MP4, WebM, QuickTime or Matroska); links and file paths are never fetched. Every question sees all of them: the images first, then the videos, in front of the text. The models read each image at up to 1.6 megapixels, and each video at 2 frames per second, at most 32 frames spread over the whole video, each at up to 0.2 megapixels. How many images and videos a request may carry, and how large and long they may be, is listed under Limits; the size limits count decoded bytes, so the base64 text is about a third larger.

The service reads a video’s container from its bytes, not from the type in the data URL: MP4 or QuickTime, WebM or Matroska, with at least one video track. Its duration and frame count must stay within the limits (400 video_too_long); a container whose duration or frames cannot be read is 400 invalid_media.

Send media on standard input, as below: a command-line argument holds at most 128 KiB. On macOS, use base64 -i chart.png instead of base64 -w0 chart.png.

Image
curl -s https://decision.vllm-sr.ai/v1/systemone \
  -H "Authorization: Bearer $DECISION_API_KEY" \
  -H 'content-type: application/json' --data-binary @- <<EOF
{
  "model": "vllm-sr/d3-flash",
  "state": "What does the attached image show?",
  "images": ["data:image/png;base64,$(base64 -w0 chart.png)"],
  "questions": {
    "kind": {
      "type": "choice",
      "instructions": "What kind of image is this?",
      "criteria": {
        "chart": "A chart or plot",
        "photo": "A photograph",
        "document": "A document"
      }
    }
  }
}
EOF
Response, for a plain bar chart
{
  "answers": {
    "kind": {
      "type": "choice",
      "choice": "chart",
      "probabilities": {"chart": 0.9992, "photo": 0.0002, "document": 0.0006},
      "confidence": 0.9933
    }
  },
  "model": "vllm-sr/d3-flash",
  "usage": {"input_tokens": 261, "output_tokens": 0}
}

A real response of vllm-sr/d3-flash (2026-10-13). Numbers rounded to 4 decimal places. Image tokens count in usage.input_tokens.

Video
curl -s https://decision.vllm-sr.ai/v1/systemone \
  -H "Authorization: Bearer $DECISION_API_KEY" \
  -H 'content-type: application/json' --data-binary @- <<EOF
{
  "model": "vllm-sr/d3-flash",
  "state": "What happens in the clip?",
  "videos": ["data:video/mp4;base64,$(base64 -w0 clip.mp4)"],
  "questions": {
    "moving": {"type": "noul", "instructions": "Does something move across the scene?"}
  }
}
EOF

Responses #

A successful response has one answer per question ID and the request's usage. The answer of vllm-sr/d3-flash to the request body with all three types, above:

Response
{
  "answers": {
    "route": {
      "type": "choice",
      "choice": "returns",
      "probabilities": {"returns": 0.9978, "billing": 0.0002, "technical": 0.0020},
      "confidence": 0.9850
    },
    "receipt": {"type": "noul", "noul": 0.9993},
    "urgency": {
      "type": "score",
      "score": 1.6958,
      "probabilities": {"0": 0.0342, "1": 0.2359, "2": 0.7300},
      "confidence": 0.3758,
      "legend": {"0": "Routine", "1": "Soon", "2": "Today"}
    }
  },
  "model": "vllm-sr/d3-flash",
  "usage": {"input_tokens": 345, "output_tokens": 0}
}

A real response (2026-10-13). Numbers rounded to 4 decimal places.

  • choice answers name the chosen option and give the probability of every option.
  • noul answers give the probability that the answer is yes.
  • score answers give the expected level (here 1.6958, between Soon and Today), the probability of every level and a legend of the levels.
  • Choice and score answers also carry a confidence.
  • usage.input_tokens counts the input tokens of the request; these are the tokens of the token rate limits. Decision models generate no text, so output_tokens is 0.
  • An invalid question is answered {"type": "...", "error": "invalid_question", "message": "..."}.

Every response carries an X-Request-Id header, and the rate-limit headers.

Models #

The six Decision 3.0 models read text, images and videos. Larger models score higher on both boards; smaller ones are lighter. GET /v1/systemone/models lists the published models without a key.

Model IDSizeInputsText / visionStatusCard
vllm-sr/d327Btext, images, videos66.26 / 71.26Model card
vllm-sr/d3-flash9Btext, images, videos55.79 / 66.89Model card
vllm-sr/d3-mini4Btext, images, videos52.61 / 63.16Model card
vllm-sr/d3-nano2Btext, images, videos38.86 / 54.67Model card
vllm-sr/d3-lite0.8Btext, images, videos32.33 / 42.11Model card
vllm-sr/d3-edge0.6Btext, images, videos20.24 / 31.71Model card

Text / vision: official Full scores on the Jev Decision Index 0.3.2 text and vision boards (2026-10-12). A model that is starting or under maintenance answers 503 model_unavailable with a Retry-After header.

Limits #

Each request must stay within these limits. They come live from /api/info, so this table always shows the current values.

Loading the current limits

Per-model limits

A model may be stricter than the limits above; models[].limits in /api/info holds each model’s own values.

  • Image and video sizes count decoded bytes. The state length counts Unicode code points.
  • The options limit covers choice and noul criteria, score levels, and set and span labels.
  • The states limit counts the entries of states; the question limit counts the questions of every state.
  • The token limit bounds an upper estimate of the input tokens of all questions together, because every question reads its whole state and media: per question, the UTF-8 bytes of its state (after Unicode NFC normalization) plus the bytes of the question’s JSON, plus 1,650 per image, 3,200 per video and 40. No tokenizer of these models makes more tokens than bytes, so the estimate is safe for any language; for English it is about 4 to 6 times the real count.
  • Because the estimate is conservative, a full 32,000-character English state fits about 2 questions in one vllm-sr/d3 request and about 4 with the other models. If a request is refused as too large (request_too_long), send fewer questions per request or a shorter state.
  • The frame limit counts the frames of one video, as read from its container.
  • A request may take 30 seconds; the models end a longer call with 504 timeout. Send fewer questions or less input.

A request over a limit fails with 400 and a specific code (see Errors), and a body over the size limit with 413 request_too_large.

Rate limits #

Each key has per-minute budgets and a daily quota for every model, counted in requests and in input tokens (as in usage.input_tokens). Days are UTC: the daily quotas reset at 00:00 UTC. Each network address also has its own limits, shown below the table.

Loading the current rate limits

Rate-limit headers

Every answer of /v1/systemone, 429s included, carries your key's per-minute budgets for the model you asked:

HeaderMeaning
X-RateLimit-Limit-RequestsRequests per minute for this model.
X-RateLimit-Remaining-RequestsRequests left in the per-minute budget.
X-RateLimit-Limit-TokensInput tokens per minute for this model.
X-RateLimit-Remaining-TokensInput tokens left in the per-minute budget.
X-RateLimit-ResetSeconds until the per-minute budget is full again.

When a limit is reached

The API answers 429 with a Retry-After header, in seconds. Wait at least that long before you retry; for server_overloaded, retry with exponential backoff and some random jitter. A request that ends in 504 timeout, or that you abandon before it is answered, is charged its estimated tokens against the per-minute token budget.

CodeMeaning
rate_limit_exceededA per-minute budget was used up: your key's, or your network address's.
quota_exceededYour key's daily quota for this model was used up. It resets at 00:00 UTC.
concurrency_limitToo many requests at once.
server_overloadedThe model is at capacity right now.

Errors #

Errors have one shape:

{"error": {"type": "...", "code": "...", "message": "..."}}
HTTPCodeWhen
400invalid_requestMalformed JSON, no model, no questions, a question that is not an object, or a request the model rejects; the message names the reason, such as video longer than 300 s.
400too_many_questions, too_many_options, too_many_states, too_many_images, too_many_videosA count over its limit. Questions count across states too.
400state_too_long, request_too_longA state over the character limit, or more estimated input tokens than the token limit for the whole request (every question reads its state and media).
400image_too_large, video_too_large, video_too_longAn image or video over its size limit, or a video over the length limit.
400invalid_media, unsupported_media_typeMedia that is not a base64 data URL (links and paths are never fetched), or a type other than PNG, JPEG, WebP, MP4, WebM, QuickTime or Matroska.
401missing_api_key, invalid_api_keyNo key, or an unknown or revoked key.
403key_disabled, model_not_allowed, access_deniedThe key was disabled by an operator, the model is unknown, or the network is blocked.
413request_too_largeThe body is larger than the size limit.
429rate_limit_exceeded, quota_exceeded, concurrency_limit, server_overloadedA per-minute budget (of the key or of the network address), the daily quota (resets 00:00 UTC), too many requests at once, or the model at capacity; always with Retry-After. See When a limit is reached.
502upstream_errorThe model server did not answer; with Retry-After.
503model_unavailableThe model is starting or under maintenance; with Retry-After.
504timeoutThe request needed more than the models' 30 seconds per call: send fewer questions or less input.

Logging #

Every request and response, images and videos included, may be logged.

Logged data may be stored and used to operate, secure, debug, analyse and improve the service, and to train and evaluate models. No request header turns logging off. Do not send sensitive personal, confidential or regulated data. IP addresses are kept for up to 30 days, for abuse prevention.

To delete everything stored for a key, use forget (or on the Keys page: Delete key and its data).

More in the privacy notice and the terms of use.

Help #

Is something slow or failing? The status page shows the live state of every model.

Questions, problems and ideas go to the vLLM Semantic Router issues on GitHub. Include the X-Request-Id of a failing request, and never post your API key or the content of a request.

Credits for the examples on the home page: the Mars photo is Mount Sharp Comes In Sharply (PIA19912), image NASA/JPL-Caltech/MSSS; the launch clip is from NASA’s wide shot of the Artemis II launch across the water, video NASA.

To run the models yourself, see the model cards on Hugging Face and vLLM Semantic Router; the full System One format is described in its model runtime reference.