Skip to content
Get API key

API reference

Evaluate a state against a map of typed questions (choice, noul, score) in a single forward pass (~40–100 ms) and receive structured answers with calibrated probability distributions.

  • Base URL: https://api.mouse.dev (US-East)
  • Authentication: Authorization: Bearer sk_mouse_... (create an API key)
  • Data retention: Zero data retention by default on every request (Mouse-Data-Retention: none, Cache-Control: no-store)
  • OpenAPI 3.1 spec: /openapi.json · Interactive Playground: /console/playground
Method & endpointAuthDescription
POST /v1/decisionsBearer sk_mouse_...Evaluate 1 to 128 typed choice, noul (yes/no), and score questions in parallel against a state (POST /v1/systemone is served as an identical alias).
POST /v1/calibrationsBearer sk_mouse_...Fit a signed confidence cutoff profile (calp1.) on 200–5,000 labeled examples using 5-fold out-of-fold cross-fitting.
GET /v1/modelsBearer sk_mouse_...List immutable model versions (mouse-preview-2026-10-13) and current aliases (mouse-1, mouse).
GET /healthzNoneUnauthenticated liveness and region probe.

Evaluate up to 128 typed questions in parallel and in isolation against a shared state.

POST https://api.mouse.dev/v1/systemone
Authorization: Bearer <MOUSE_API_KEY>
Content-Type: application/json
PropertyTypeRequiredDefaultDescription
statestring | object | array | number | booleanRequired—The content to evaluate (up to 65,536 tokens). Pass a string for a single message or passage, or a JSON object/array for structured application state, tool proposals, and records. Never truncated; states over 65,536 tokens return 422 state_too_long.
questionsRecord<string, Question>Required—Non-empty map of 1 to 128 question IDs you choose to typed ChoiceQuestion, NoulQuestion, or ScoreQuestion objects. Question IDs are returned as keys in answers and are not sent to the model during inference.
modelstringOptional"mouse-1"Model alias ("mouse-1", "mouse") or immutable snapshot ID ("mouse-preview-2026-10-13").
calibrationstring | nullOptionalnullOptional signed calibration profile token ("calp1.<kid>.<blob>") returned by POST /v1/calibrations.
order_invariantbooleanOptionalfalseScore every multi-option choice question a second time with its options reversed and average the two distributions, removing option-order sensitivity. The reversed questions are billed again (their question_tokens and framing_tokens roughly double); the state prefix is shared and billed once. noul and score are never permuted.

Unknown top-level fields (for example "temperature") are ignored and listed in the response's warnings array. Unknown fields inside a question object are refused with 422 invalid_schema.

So that a migration is a base-URL change, the request parser also accepts, and normalizes before validation:

You sendRead as
inputstate
questions as an array of objects carrying id or namequestions map keyed by that id/name
type: "predicate" or type: "boolean"type: "noul"
textinstructions
options: ["a", "b"]criteria: { "a": null, "b": null }
choices: [{ value, description }]criteria: { [value]: description }
levels: [{ label, description }]criteria: ["<label>: <description>", ...]

Aliases change only how the request is read; the response is always the Mouse shape documented below.

Example request (cURL, TypeScript, Python)

Section titled “Example request (cURL, TypeScript, Python)”
curl https://api.mouse.dev/v1/systemone \
-H "Authorization: Bearer $MOUSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mouse-1",
"state": {
"task": "Rename exported function `parseToken` to `verifyToken` in src/auth.ts.",
"proposed_command": "pnpm --filter @mouse/relay test"
},
"questions": {
"next_tool": {
"type": "choice",
"instructions": "Which tool should handle `task` next?",
"criteria": {
"edit_file": "Modify source code in a file",
"grep_search": "Search for symbols across the repository",
"run_tests": "Execute the test suite",
"ask_user": "Ask the user for clarification"
}
},
"safe_to_run": {
"type": "noul",
"instructions": "Is `proposed_command` safe to run without human approval?"
},
"blast_radius": {
"type": "score",
"instructions": "What is the impact of `proposed_command`?",
"criteria": [
"Read-only inspection or local test execution",
"Reversible local workspace edit",
"Irreversible deletion, remote git push, or external mutation"
]
}
}
}'
const res = await fetch("https://api.mouse.dev/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.MOUSE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "mouse-1",
state: {
task: "Rename exported function `parseToken` to `verifyToken` in src/auth.ts.",
proposed_command: "pnpm --filter @mouse/relay test",
},
questions: {
next_tool: {
type: "choice",
instructions: "Which tool should handle `task` next?",
criteria: {
edit_file: "Modify source code in a file",
grep_search: "Search for symbols across the repository",
run_tests: "Execute the test suite",
ask_user: "Ask the user for clarification",
},
},
safe_to_run: {
type: "noul",
instructions: "Is `proposed_command` safe to run without human approval?",
},
blast_radius: {
type: "score",
instructions: "What is the impact of `proposed_command`?",
criteria: [
"Read-only inspection or local test execution",
"Reversible local workspace edit",
"Irreversible deletion, remote git push, or external mutation",
],
},
},
}),
});
const data = await res.json();
console.log(data.answers.next_tool.choice, data.answers.safe_to_run.noul);
import os
import requests
res = requests.post(
"https://api.mouse.dev/v1/systemone",
headers={"Authorization": f"Bearer {os.environ['MOUSE_API_KEY']}"},
json={
"model": "mouse-1",
"state": {
"task": "Rename exported function `parseToken` to `verifyToken` in src/auth.ts.",
"proposed_command": "pnpm --filter @mouse/relay test",
},
"questions": {
"next_tool": {
"type": "choice",
"instructions": "Which tool should handle `task` next?",
"criteria": {
"edit_file": "Modify source code in a file",
"grep_search": "Search for symbols across the repository",
"run_tests": "Execute the test suite",
"ask_user": "Ask the user for clarification",
},
},
"safe_to_run": {
"type": "noul",
"instructions": "Is `proposed_command` safe to run without human approval?",
},
"blast_radius": {
"type": "score",
"instructions": "What is the impact of `proposed_command`?",
"criteria": [
"Read-only inspection or local test execution",
"Reversible local workspace edit",
"Irreversible deletion, remote git push, or external mutation",
],
},
},
},
timeout=5.0,
)
print(res.json()["answers"])

A Question is one of three primitives determined by its type field ("choice", "noul", or "score").

All three primitives accept instructions as a string, a structured object, an array, or null. Point a question at a specific nested field inside state (or inside a structured instructions object) using backticks, such as "Does `ticket.messages[0].text` request a refund?".

Picks one option from a set of 1 to 1,024 named options and returns the full probability distribution.

PropertyTypeRequiredDescription
type"choice"RequiredAlways "choice".
criteriaRecord<string, string | object | array | null>RequiredMap of option keys to rubric descriptions. Use null when an option key needs no extra detail. Passing an array instead of an object returns 422 invalid_schema.
instructionsstring | object | array | nullOptionalWhat the model should decide.
{
"department": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Payments, invoices, duplicate charges, or refunds",
"technical": "Bugs, outages, or API integration problems",
"sales": "Pricing, upgrades, or new accounts"
}
}
}

Evaluates a yes/no predicate and returns the probability (0.0 to 1.0) that the statement is true (yes).

PropertyTypeRequiredDescription
type"noul"RequiredAlways "noul".
instructionsstring | object | array | nullOptionalThe yes/no question or statement to evaluate against state. Phrase positively so a value near 1.0 means yes.
criteria{ "true"?: JSONContent, "false"?: JSONContent } | nullOptionalOptional descriptions clarifying what "true" (yes, near 1.0) and "false" (no, near 0.0) mean. Only "true" and "false" keys are allowed.
{
"is_urgent": {
"type": "noul",
"instructions": "Does this message convey time-sensitive urgency?",
"criteria": {
"true": "Production outage, blocked customers, or explicit deadline",
"false": "Routine question, feature request, or non-blocking issue"
}
}
}

Rates the state along an ordered rubric of 2 to 10 concrete levels (0 to N - 1).

PropertyTypeRequiredDescription
type"score"RequiredAlways "score".
criteriaArray<string | object | array>RequiredOrdered array of 2 to 10 level descriptions from lowest (0) to highest (N - 1). Describe concrete situations rather than bare numbers.
instructionsstring | object | array | nullOptionalWhat dimension the model should rate.
{
"frustration": {
"type": "score",
"instructions": "How frustrated does the customer appear?",
"criteria": [
"Calm, stating facts",
"Frustrated but civil",
"Very angry or threatening to leave"
]
}
}

PropertyTypeDescription
idstringUnique decision identifier ("dec_...").
object"systemone.response"Fixed object type string.
modelstringExact immutable model snapshot ID that evaluated the request ("mouse-preview-2026-10-13"). Also echoed in the Mouse-Model-Version HTTP header.
answersRecord<string, Answer>One typed ChoiceAnswer, NoulAnswer, or ScoreAnswer per question, keyed by the same IDs you passed in questions.
usageUsageItemized token counts and exact USD cost for the request.
latency_msnumberEnd-to-end server evaluation latency in milliseconds.
warningsstring[] (optional)Present only when unknown top-level request fields were ignored.

Every answer carries a type matching its question, the full probabilities distribution, the raw logits behind it (softmax(logits) == probabilities), and a mouse metadata object ({ calibrated, p_top, abstain, confidence_winning, confidence_normalized }). choice and score answers also carry a top-level confidence (0.0 to 1.0).

Log logits and model together if you fit thresholds or calibrators offline; probabilities alone lose the scale.

PropertyTypeDescription
type"choice"Always "choice".
choicestringThe highest-probability option key from criteria.
probabilitiesRecord<string, number>Every option key mapped to its probability (0.0 to 1.0, summing to 1.0).
logitsRecord<string, number>Every option key mapped to its raw log-score.
confidencenumberNormalized concentration score in 0.0 to 1.0: ((p_{\max} - 1/k) / (1 - 1/k)) for (k) options (0.0 for an even split, 1.0 when all probability is on one option).
mouseMouseAnswerMeta{ calibrated, p_top, abstain, confidence_winning, confidence_normalized }.
{
"type": "choice",
"choice": "edit_file",
"confidence": 0.88,
"probabilities": {
"edit_file": 0.91,
"grep_search": 0.06,
"run_tests": 0.02,
"ask_user": 0.01
},
"logits": {
"edit_file": 2.41,
"grep_search": -0.31,
"run_tests": -1.41,
"ask_user": -2.1
},
"mouse": {
"calibrated": false,
"p_top": 0.91,
"abstain": null,
"confidence_winning": 0.91,
"confidence_normalized": 0.88
}
}
PropertyTypeDescription
type"noul"Always "noul".
noulnumberThe yes/no probability on a scale from 0.0 (false / no) to 1.0 (true / yes). A value near 0.5 indicates equal probability for yes and no (certainty is (|2 \cdot \text{noul} - 1|), also returned as mouse.confidence_normalized).
probabilities{ "false": number, "true": number }The same distribution in the shape choice uses: { "false": 1 - noul, "true": noul }.
logits{ "false": number, "true": number }Raw log-scores for each side.
mouseMouseAnswerMeta{ calibrated, p_top, abstain, confidence_winning, confidence_normalized }, where p_top = max(noul, 1 - noul).
{
"type": "noul",
"noul": 0.98,
"probabilities": { "false": 0.02, "true": 0.98 },
"logits": { "false": -1.95, "true": 1.94 },
"mouse": {
"calibrated": false,
"p_top": 0.98,
"abstain": null,
"confidence_winning": 0.98,
"confidence_normalized": 0.96
}
}
PropertyTypeDescription
type"score"Always "score".
scorenumberProbability-weighted mean level index: (\sum_{i=0}^{k-1} i \cdot p_i), landing continuously between 0.0 and k - 1.
legendRecord<string, JSONContent>Each string level index ("0", "1", …) mapped back to its description from criteria.
probabilitiesRecord<string, number>Each string level index ("0", "1", …) mapped to its probability (summing to 1.0).
logitsRecord<string, number>Each string level index mapped to its raw log-score.
confidencenumberOrdinal concentration score in 0.0 to 1.0: (\max(0, 1 - \sum_i p_i |i - m| / \text{MAD}_{\text{unif}})), where (m) is the most likely level.
mouseMouseAnswerMeta{ calibrated, p_top, abstain, confidence_winning, confidence_normalized }.
{
"type": "score",
"score": 0.04,
"confidence": 0.94,
"legend": {
"0": "Read-only inspection or local test execution",
"1": "Reversible local workspace edit",
"2": "Irreversible deletion, remote git push, or external mutation"
},
"probabilities": {
"0": 0.96,
"1": 0.04,
"2": 0.0
},
"logits": {
"0": 3.18,
"1": 0.0,
"2": -6.9
},
"mouse": {
"calibrated": false,
"p_top": 0.96,
"abstain": null,
"confidence_winning": 0.96,
"confidence_normalized": 0.94
}
}
PropertyTypeDescription
calibratedbooleantrue when a calibration profile was passed on the request and matched this question's signature. When true, probabilities (and every field derived from them) are temperature-scaled on your labels.
p_topnumberTop outcome probability ((\max_i p_i)).
abstainboolean | nullnull when uncalibrated; true when p_top is below the profile's fitted cutoff (signaling your code to escalate or fall back); false when p_top >= cutoff.
confidence_winningnumberThe winning-probability convention ((\max_i p_i)), as Cloudflare Clef reports confidence. Equal to p_top; named so a Clef threshold ports without arithmetic.
confidence_normalizednumberThe normalized convention ((p_{\max} - 1/k) / (1 - 1/k)), as TypeSafe reports confidence, on every answer type including noul.

Both conventions are present because the same distribution reads differently under each: (0.80, 0.15, 0.05) is 0.80 winning and 0.70 normalized, so a 0.75 threshold flips depending on which one a vendor calls confidence. Threshold on the convention your labels were fitted against, or on the full probabilities.

PropertyTypeDescription
input_tokensintegerTotal billed input tokens: state_tokens + question_tokens + framing_tokens + image_tokens.
state_tokensintegerTokens in state (counted once per request regardless of how many questions are evaluated).
question_tokensintegerTokens across all question instructions and criteria.
framing_tokensintegerStructural delimiter tokens: 4 per request + 3 per question + 2 per option (2 options per noul).
image_tokensinteger0 for text/JSON requests.
cached_tokensintegerstate_tokens served from the 5-minute in-memory prefix cache ($0.02 / 1M instead of $0.10 / 1M).
output_tokensintegerAlways 0 (output is free).
cost_usdnumberExact USD cost for the request (((input_tokens - cached_tokens) * 0.10 + cached_tokens * 0.02) / 1_000_000).

Score 200 to 5,000 labeled examples and fit a signed confidence cutoff profile (calp1.<kid>.<blob>) using 5-fold out-of-fold cross-fitting. Examples are evaluated in memory and discarded immediately (Mouse-Data-Retention: none).

POST https://api.mouse.dev/v1/calibrations
Authorization: Bearer <MOUSE_API_KEY>
Content-Type: application/json
PropertyTypeRequiredDefaultDescription
examplesCalExample[]Required—Array of 200 to 5,000 labeled examples. Each distinct question signature requires 50+ labeled examples to be fitted.
modelstringOptional"mouse-1"Model alias or snapshot ID. The returned profile is cryptographically bound to the resolved snapshot ID.
target_errornumberOptional0.05Maximum acceptable error rate (0.0 < target_error < 0.5) among non-abstained predictions.

Each item in examples contains state and questions, where each question includes its ground-truth label:

  • Labeled choice (CalChoice): label is a string matching one of the option keys in criteria.
  • Labeled noul (CalNoul): label is a boolean (true or false).
  • Labeled score (CalScore): label is an integer level index in 0 .. len(criteria) - 1.
curl https://api.mouse.dev/v1/calibrations \
-H "Authorization: Bearer $MOUSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mouse-1",
"target_error": 0.05,
"examples": [
{
"state": "Customer was charged twice for order #1042.",
"questions": {
"intent": {
"type": "choice",
"instructions": "What does the customer need?",
"criteria": {
"billing": "A charge, invoice, or refund issue",
"shipping": "Delivery status or tracking",
"account": "Login or profile settings"
},
"label": "billing"
}
}
}
]
}'
PropertyTypeDescription
object"calibration"Fixed object type string.
modelstringImmutable snapshot ID this profile was fitted against ("mouse-preview-2026-10-13").
target_errornumberTarget error rate used to select the abstention cutoff.
profilestringHMAC-SHA256 signed profile token ("calp1.<kid>.<blob>"). Pass as calibration on POST /v1/systemone.
n_examplesintegerTotal labeled examples processed.
questionsCalibrationQuestionReport[]Per-question-signature report (signature, type, question_ids, status, before, and after metrics including the fitted temperature, cutoff, and 5-fold out-of-fold coverage_at_target_error and error_when_covered).
notestringOut-of-fold methodology and zero-data-retention note.
usageUsageItemized token counts and USD cost for scoring the calibration examples.
{
"object": "calibration",
"model": "mouse-preview-2026-10-13",
"target_error": 0.05,
"profile": "calp1.k1.eyJmaXRzIjpb...",
"n_examples": 250,
"questions": [
{
"signature": "4f8c12a90e3b7104",
"type": "choice",
"question_ids": ["intent"],
"status": "fitted",
"before": {
"accuracy": 0.912,
"ece": 0.031,
"brier": 0.142,
"nll": 0.264,
"mean_confidence": 0.938,
"coverage": 1.0,
"cutoff": null,
"n": 250
},
"after": {
"accuracy": 0.912,
"ece": 0.031,
"brier": 0.142,
"nll": 0.264,
"mean_confidence": 0.938,
"coverage": 0.864,
"cutoff": 0.78,
"n": 250,
"coverage_at_target_error": 0.856,
"error_when_covered": 0.0421
}
}
],
"note": "'coverage_at_target_error' and 'error_when_covered' are 5-fold out-of-fold. Examples were not stored.",
"usage": {
"input_tokens": 14500,
"state_tokens": 3500,
"question_tokens": 7750,
"framing_tokens": 3250,
"image_tokens": 0,
"cached_tokens": 0,
"output_tokens": 0,
"cost_usd": 0.00145
}
}

List active immutable model versions and alias mappings (mouse-1, mouse).

curl https://api.mouse.dev/v1/models \
-H "Authorization: Bearer $MOUSE_API_KEY"
{
"object": "list",
"alias": {
"mouse-1": "mouse-preview-2026-10-13",
"mouse": "mouse-preview-2026-10-13"
},
"data": [
{
"id": "mouse-preview-2026-10-13",
"released": "2026-10-13",
"status": "preview",
"base": "mouse-1",
"max_state_tokens": 65536,
"modalities": ["text"],
"retire_after": null
}
],
"today": "2026-10-14"
}

Unauthenticated liveness probe returning serving region and engine status.

curl https://api.mouse.dev/healthz
{
"ok": true,
"region": "us",
"engine": "modal"
}

Every response from https://api.mouse.dev includes these HTTP headers:

HeaderExampleDescription
Mouse-Request-Idreq_8f92a1c04e1b902d73c104abUnique request identifier (req_ + 24 hex chars).
Mouse-Model-Versionmouse-preview-2026-10-13Exact immutable snapshot ID that scored the request (POST /v1/systemone and POST /v1/calibrations).
Mouse-RegionusServing region (us).
Mouse-Data-RetentionnoneEnforced zero data retention on every response.
Cache-Controlno-storePrevents intermediary caching.
x-ratelimit-limit-requests200Sustained requests-per-second limit (200 standard, 5 playground).
x-ratelimit-remaining-requests399Remaining requests in the burst bucket (400 capacity).
x-ratelimit-reset-requests1Seconds until the request bucket resets (1).
x-ratelimit-limit-tokens20000000Input-tokens-per-minute limit (20,000,000).
x-ratelimit-remaining-tokens19999926Remaining input tokens in the 60-second bucket.
x-ratelimit-reset-tokens60Seconds until the token bucket resets (60).
Retry-After2Present on 429 and 503 responses; integer seconds to wait before retrying.

All non-2xx responses use a single JSON error envelope. Messages name the limit or offending field and never echo request content:

{
"error": {
"type": "invalid_request_error",
"code": "state_too_long",
"message": "state is 71,204 tokens; mouse-preview-2026-10-13 reads at most 65,536. Nothing was truncated or billed.",
"param": "state"
}
}
Statuserror.typeerror.codeMeaning
400invalid_request_errorinvalid_calibration_profileMalformed, altered, or retired calibration profile token.
401authentication_errormissing_api_key, invalid_api_keyMissing Authorization: Bearer sk_mouse_... header, bad CRC32 checksum, or revoked/expired key.
402billing_errorinsufficient_creditsPrepaid credit balance is $0.00. Add credits at /console/billing.
404invalid_request_errormodel_not_foundUnknown model name. See GET /v1/models.
409invalid_request_errorprofile_model_mismatchcalibration profile was fitted on a different model snapshot than the one serving the request.
413invalid_request_errorrequest_too_largeRequest body exceeds 4,000,000 bytes (4 MB).
422invalid_request_errorinvalid_schema, state_too_long, too_many_questions, too_many_options, too_few_examples, too_many_examples, too_few_examples_per_questionRequest failed schema validation or exceeded a published payload limit. Never billed.
429rate_limit_errorrate_limited, token_rate_limitedExceeded 200 RPS (burst 400) or 20M TPM. Honor the Retry-After header.
503api_errorengine_timeoutInference worker timed out (10s). Honor the Retry-After header.