> For clean Markdown of this page, append .md to its URL. For the complete documentation index, see https://www.superagent.sh/llms.txt.


Endpoints, request and response schemas, limits, errors, and usage for the hosted Security-One API.

# SystemOne API

The SystemOne API serves Security-One over HTTPS. The base URL is:

```text
https://api.superagent.sh
```

The SystemOne API is separate from the [Superagent REST API](https://www.superagent.sh/docs/api) and uses its own host. Both APIs accept Superagent organization `sk_live_` API keys.

## Authentication

Create an organization API key in [Settings](https://www.superagent.sh/docs/reference/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.

```bash
curl https://api.superagent.sh/v1/systemone \
  -H "Authorization: Bearer $SUPERAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data @request.json
```

Keep 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

```http
POST /v1/systemone
```

### Request 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

- **`noul`** is the probability that the `true` criterion applies. The probability of `false` is `1 - noul`.
- **`choice`** is the option key with the highest probability. `probabilities` covers every option and sums to 1.
- **`score`** is the expected level: the probability-weighted average of the zero-based level indexes. A `score` of `3.99` on a five-level scale from None to Critical means "almost certainly Critical". `legend` maps each index back to your description.
- **`confidence`** is `1` minus the normalized entropy of the distribution. It is near `1` when one option dominates and near `0` when 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 `noul` and a `choice`.
- Describe each option by what makes it true, not just its name. For `choice`, a `null` description uses the key itself.
- Keep `choice` options mutually exclusive, and cover the benign case explicitly.
- Order `score` levels from lowest to highest so `score` reads 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](https://www.superagent.sh/docs/models/examples).

### TypeScript

```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.7
```

### Python

```python
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.7
```

## Model document

`GET /models` returns a public, unauthenticated description of the served model in the OpenRouter provider format:

```bash
curl https://api.superagent.sh/models
```

The 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`:

```json
{
  "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.

## Next steps

- [Browse Security-One examples](https://www.superagent.sh/docs/models/examples)
- [Self-host Security-One](https://www.superagent.sh/docs/models/self-hosting)

---
Source: https://www.superagent.sh/docs/models/api
Index: https://www.superagent.sh/llms.txt
