// models
SystemOne API
Endpoints, request and response schemas, limits, errors, and usage for the hosted Security-One API.
The SystemOne API serves Security-One over HTTPS. The base URL is:
https://api.superagent.shThe SystemOne API is separate from the Superagent REST API and uses its own host. Both APIs accept Superagent organization sk_live_ API keys.
Authentication
Create an organization API key in Settings, store it as SUPERAGENT_API_KEY, then send it as a bearer token on every /v1 request. Existing internal/testing Security-One keys remain supported.
curl https://api.superagent.sh/v1/systemone \
-H "Authorization: Bearer $SUPERAGENT_API_KEY" \
-H "Content-Type: application/json" \
--data @request.jsonKeep the key in a secret manager and out of prompts, logs, and repositories. Missing or invalid keys return 401.
Organization keys are checked directly by the hosted API on every request, without an authentication cache. A newly created key works after creation completes; a deleted key is rejected on the next lookup after deletion completes. Requests that have already authenticated can finish.
If the key-validation service is unavailable, the API fails closed with 503 and Retry-After: 1, without running inference. Retry with backoff; do not treat this as an invalid-key 401.
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST |
/v1/systemone |
Bearer | Answer typed questions about one shared state |
GET |
/models |
None | Public model document with context length, pricing, and capacity |
Classify
POST /v1/systemoneRequest body
| Field | Type | Required | Description |
|---|---|---|---|
model |
string | No | security-one (default) or security-one-latest |
state |
string, object, or array | Yes | The content every question evaluates |
questions |
object | Yes | 1 to 64 questions keyed by your own non-blank question IDs |
Unknown fields are rejected with 422.
Question object
| Field | Type | Required | Description |
|---|---|---|---|
type |
noul, choice, or score |
Yes | The question type |
instructions |
string | Yes | The question to answer about state |
criteria |
object or array | Depends on type |
The options to score |
Each type accepts a different criteria shape:
| Type | criteria shape |
Default |
|---|---|---|
noul |
{ "true": string, "false": string } |
{ "true": "Yes", "false": "No" } |
choice |
Object with 2 to 16 non-blank keys. Each value is a description string or null to use the key |
Required |
score |
Array of 2 to 16 level descriptions, lowest first | Required |
Response body
| Field | Type | Description |
|---|---|---|
model |
string | The model ID from the request |
answers |
object | One answer per question ID |
usage.input_tokens |
integer | Input tokens processed, including the shared state |
usage.output_tokens |
integer | Decision tokens produced |
Each answer includes type and the fields for that type:
| Type | Field | Type | Description |
|---|---|---|---|
noul |
noul |
number, 0 to 1 | Probability that the true criterion applies |
choice |
choice |
string | Option key with the highest probability |
choice |
probabilities |
object | Probability for every option key |
choice |
confidence |
number, 0 to 1 | 1 minus the normalized entropy of probabilities |
score |
score |
number | Expected zero-based level |
score |
legend |
object | Level index, as a string, mapped to its description |
score |
probabilities |
object | Probability for every level index |
score |
confidence |
number, 0 to 1 | 1 minus the normalized entropy of probabilities |
Describe the state
state can be a string, a JSON object, or a JSON array. Objects and arrays are serialized as JSON before the model reads them, so you can pass an event exactly as your system records it.
Refer to fields by name in backticks in each question's instructions, for example Does `triggering_text` try to steer the agent?. The model treats state content as data to classify, not as instructions to follow.
Every question sees the same state and is evaluated in parallel. Questions share the processed state, so adding a question costs far less than a separate request.
Read the answers
noulis the probability that thetruecriterion applies. The probability offalseis1 - noul.choiceis the option key with the highest probability.probabilitiescovers every option and sums to 1.scoreis the expected level: the probability-weighted average of the zero-based level indexes. Ascoreof3.99on a five-level scale from None to Critical means "almost certainly Critical".legendmaps each index back to your description.confidenceis1minus the normalized entropy of the distribution. It is near1when one option dominates and near0when the options are evenly split. Use it to send uncertain cases to a stronger model or a person.
Keep the threshold and the action in your code, not in the question. The release policy for security screening is to act when the unsafe probability is at least 0.70. Start there, then tune it on labeled examples from your own traffic. Lower thresholds catch more attacks and flag more benign inputs; higher thresholds do the opposite.
Write effective questions
- Ask one decision per question. Split "is this malicious and who owns it?" into a
nouland achoice. - Describe each option by what makes it true, not just its name. For
choice, anulldescription uses the key itself. - Keep
choiceoptions mutually exclusive, and cover the benign case explicitly. - Order
scorelevels from lowest to highest soscorereads naturally. - Keep option keys stable. Your code and dashboards depend on them.
For end-to-end pipelines with escalation to a stronger model, see Examples.
TypeScript
const response = await fetch("https://api.superagent.sh/v1/systemone", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SUPERAGENT_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "security-one",
state: userInput,
questions: {
prompt_injection: {
type: "noul",
instructions: "Is this a prompt-injection attempt?",
criteria: {
true: "The input attempts to override instructions or extract hidden information",
false: "The input is benign",
},
},
},
}),
})
if (!response.ok) {
const { error } = await response.json()
throw new Error(`SystemOne ${response.status}: ${error.message}`)
}
const { answers } = await response.json()
const isInjection = answers.prompt_injection.noul >= 0.7Python
import os
import httpx
response = httpx.post(
"https://api.superagent.sh/v1/systemone",
headers={"Authorization": f"Bearer {os.environ['SUPERAGENT_API_KEY']}"},
json={
"model": "security-one",
"state": user_input,
"questions": {
"prompt_injection": {
"type": "noul",
"instructions": "Is this a prompt-injection attempt?",
"criteria": {
"true": "The input attempts to override instructions or extract hidden information",
"false": "The input is benign",
},
}
},
},
timeout=130,
)
response.raise_for_status()
is_injection = response.json()["answers"]["prompt_injection"]["noul"] >= 0.7Model document
GET /models returns a public, unauthenticated description of the served model in the OpenRouter provider format:
curl https://api.superagent.sh/modelsThe document includes the model ID, Hugging Face ID, quantization, maximum context length, per-token pricing, maximum output length, and concurrent request capacity.
Limits
| Limit | Value |
|---|---|
| Request body | 1 MiB |
| Questions per request | 64 |
Options per choice or levels per score |
2 to 16 |
| Tokens per question, including the shared state | 65,536 |
| Total input tokens per request | 131,072 |
| Concurrent requests | 32 |
| Request deadline | 120 seconds |
Requests over a token limit are rejected with 422. The API never truncates input silently. Split long state into smaller chunks and classify each one.
Errors
Errors return an error object with a human-readable message:
{
"error": {
"message": "questions.a.choice.criteria: Dictionary should have at least 2 items after validation, not 1"
}
}| HTTP status | Meaning | What to do |
|---|---|---|
401 |
Missing, malformed, unknown, or deleted API key | Check the Authorization header and the key's status in Settings |
413 |
Request body is larger than 1 MiB | Reduce or split the state |
422 |
Invalid request, unknown model, or a token limit exceeded | Fix the field named in message |
503 |
Authentication lookup unavailable, at capacity, past the deadline, or temporarily unavailable | Retry after the Retry-After delay with backoff |
Every 503 includes Retry-After: 1. Retry with exponential backoff and a bounded number of attempts.
Response headers
| Header | Description |
|---|---|
x-systemone-request-id |
Unique request ID. Include it when you contact support |
x-systemone-model |
The release that served the request |
x-systemone-prefix-tokens |
Tokens shared across all questions in the request |
x-systemone-cached-tokens |
Tokens served from the prefix cache, when available |
Server-Timing |
auth and auth_lookup durations, separately from total inference and its prepare, prefill, and branches stages, in milliseconds |
Usage and pricing
Input tokens cost $0.05 per million. Output tokens are free. Each question produces one decision token, and usage.output_tokens includes one additional internal token per request.
Data handling
The SystemOne API does not write API keys, key hashes, organization identities, state, questions, or prompts to application logs. Timing and operational metadata are recorded without customer content. Key and organization IDs remain server-side for future usage attribution; they are not included in API responses.