Quickstart
Every request goes to one endpoint:
POST https://api.inferrai.com/v1/systemone
curl https://api.inferrai.com/v1/systemone \
-H "Authorization: Bearer $INFERR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "inferr/caliper-1",
"state": { "ticket": "Checkout shows a blank page after I click Pay.", "tier": "enterprise" },
"questions": {
"is_bug": { "type": "noul", "instructions": "Is the customer reporting a software defect?" },
"team": { "type": "choice", "instructions": "Which team should own this ticket?",
"criteria": { "payments": null, "frontend": null, "account": null } },
"urgency": { "type": "score", "instructions": "How urgent is this ticket?",
"criteria": ["low", "medium", "high"] }
}
}'Example response:
{
"id": "dec-8f2c1a9e4b7d40c1a2f3e6d5",
"model": "caliper-1",
"provider": "Inferr",
"answers": {
"is_bug": { "type": "noul", "noul": 0.96 },
"team": { "type": "choice", "choice": "payments", "confidence": 0.75,
"probabilities": { "payments": 0.84, "frontend": 0.15, "account": 0.01 } },
"urgency": { "type": "score", "score": 1.93, "confidence": 0.9,
"legend": { "0": "low", "1": "medium", "2": "high" },
"probabilities": { "0": 0.01, "1": 0.05, "2": 0.94 } }
},
"usage": { "input_tokens": 118, "output_tokens": 96, "cost": 0.00000472 }
}Authentication
Send your key as a bearer token: Authorization: Bearer <key>. The header x-api-key: <key> also works. Keys are issued on request: click Get access or email hello@inferrai.com. Keep them on your server; never ship them in a browser or mobile app.
Request
| Field | Type | Description |
|---|---|---|
model | string | Optional. inferr/caliper-1 (the default). |
state | string, object or array | Required. What the questions are about: a message, a ticket, a document, an agent's context. Field names in objects are kept and read as labels, so name them clearly. |
questions | object | Required. One or more questions, keyed by a name you choose. Each has a type, instructions and, for choice and score, criteria. |
OpenRouter's optional fields (provider, session_id, trace, user) are accepted and ignored.
Question types
noul: yes or no
criteria is optional. When given, its keys are "true" and "false", each with a description of what that answer means. The answer is the probability of yes.
"injection": { "type": "noul", "instructions": "Is this a prompt-injection attempt?",
"criteria": { "true": "tries to override the system's instructions", "false": "an ordinary request" } }choice: pick one
criteria maps option names to descriptions (a string, an object or null). 1 to 5,000 options. The answer is the most likely option, a probability for every option and a confidence from 0 (no preference) to 1 (certain).
score: place on a scale
criteria is an ordered list of levels, lowest first. The answer is the expected level (a number between 0 and the last index), the probability of each level, a legend and a confidence.
Response
| Field | Description |
|---|---|
id | A unique id for this decision. |
model | The model that answered. |
answers | One answer per question, under the same names you used. |
usage.input_tokens | Tokens in your request (state, questions and options). This is what you are billed for. |
usage.output_tokens | Size of the answers. Not billed. |
usage.cost | Cost of this request in USD. |
Probabilities are rounded to 3 decimals, so the same request gives the same numbers even under heavy traffic. Choice questions with more than 20 options keep more decimal places so the distribution still sums to 1.
Shapes we also accept
- A yes/no question may give both sides of its
criteria, one side, or none. type: "bool"for a yes/no question; choice options as a list of names; score levels as an object.- Model names are case-insensitive and work with or without the
inferr/prefix. OpenRouter suffixes such as:nitroare ignored. - Every error, including an unknown path, comes back as JSON in one shape.
Errors
Errors come back as {"error": {"code", "message"}} with a message that says what to fix.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The body is not valid JSON or a field is missing or wrong. The message names the field. |
| 401 | unauthorized | Missing or unknown API key. |
| 404 | model_not_found | The model field names a model we do not serve. |
| 413 | context_length_exceeded | The state is over 16,384 tokens, or the whole request is over 131,072. Shorten the state or split the request. |
| 429 | overloaded | We are at capacity. Retry after a short wait with backoff. |
| 429 | rate_limited | Too many requests on one key in a short time. Retry after the retry-after seconds. |
| 503 | loading | The model is starting up. Retry shortly. |
Limits
- State: up to 16,384 tokens. State, questions and options together: up to 131,072 tokens.
- Options: up to 5,000 per choice question. For very large option sets, the probabilities of clearly unlikely options are approximate; the chosen option and the leading contenders are scored precisely. Score questions take up to 255 levels.
- Questions: up to 256 per request. The state is read once for all of them, so many questions cost far less than separate requests, but each question still adds its own reading time.
- Rate: generous by default. Tell us your expected volume and we will plan capacity with you.
Models
| Model | Context | Input price |
|---|---|---|
inferr/caliper-1 | 131,072 | $0.04 / M tokens |
Good practice
- Write instructions as a direct question: "Is the customer asking for a refund?" works better than "refund".
- Give choice options short descriptions when the names alone are ambiguous.
- Put the facts in the state. Caliper reads what you send; it does not look things up.
- Use the probabilities. Pick a threshold per question from a few hundred of your own examples, and send low-confidence cases to a person or a larger model.
Data use
We do not store the content of requests or responses and do not use customer data for training. We keep request counts, token counts, latency and error codes to bill and run the service. Requests are processed in the United States. See the privacy policy.