<!-- generated: do not edit. source: content/docs/decisions/api.md -->

# Decisions API

`POST /v1/decisions` takes evidence and a list of questions and returns one
answer per question, in question order. Evidence is text, a list of messages
with text and image parts, or an audio clip.

## Request

```bash
curl https://api.sprag.ai/v1/decisions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $SPRAG_API_KEY" \
  -d '{
    "model": "spev",
    "input": "The agent was polite but could not answer my question about the invoice.",
    "questions": [
      {
        "name": "resolution",
        "type": "score",
        "instructions": "How well was the issue resolved?",
        "levels": [
          { "label": "unresolved", "description": "The question was not answered." },
          { "label": "partial" },
          { "label": "resolved" }
        ]
      }
    ]
  }'
```

| Field | Required | Type | Notes |
| --- | --- | --- | --- |
| `model` | Yes | string | A model id from the [Decisions models](/docs/decisions/models). |
| `input` | Yes | string or array | Text evidence, or an array of user messages. See [Send an image](#send-an-image). |
| `questions` | Yes | array | 1 to 200 questions. Answers come back in this order. |
| `input_audio` | No | object | An audio clip as `{"data": "<base64>", "format": "wav"}`. See [Send audio](#send-audio). |
| `safety_identifier` | No | string | An opaque id for your end user, up to 128 characters. |

## Ask a question

Every question has a `type` and `instructions`. `name` is optional. If you set
`name`, the answer carries the same `name`; otherwise its `name` is `null`.
Names are unique within a request.

### Predicate

A predicate asks whether a condition holds.

```json
{ "name": "refund", "type": "predicate", "instructions": "Is the customer asking for a refund?" }
```

The answer's `probability` is the probability that the condition holds, from 0
to 1.

```json
{ "type": "predicate", "name": "refund", "probability": 0.9997 }
```

### Choice

A choice picks one value from a list you supply.

| Field | Required | Type | Notes |
| --- | --- | --- | --- |
| `choices` | Yes | array | 2 to 255 options. Each `value` is a string or a boolean, and values are distinct. |
| `choices[].description` | No | string | When this option applies. |

The answer's `choice` is the selected value, with the same type you sent.
`probabilities` holds one entry per option in the order you supplied them, and
`confidence` runs from 0 to 1.

```json
{
  "type": "choice",
  "name": "department",
  "choice": "billing",
  "probabilities": [
    { "value": "billing", "probability": 0.981 },
    { "value": "technical", "probability": 0.019 }
  ],
  "confidence": 0.962
}
```

### Score

A score rates the evidence on a scale of levels you define, lowest first.

| Field | Required | Type | Notes |
| --- | --- | --- | --- |
| `levels` | Yes | array | 2 to 10 levels in ascending order. The first level has the value 0. |
| `levels[].label` | Yes | string | The level's name. |
| `levels[].description` | No | string | The criteria for this level. |

`probabilities` holds one entry per level, with its `value` and `label`.
`score` is the expected level value: each level's value weighted by its
probability. A `score` of 1.5 on a three-level scale sits halfway between the
second and third levels. `confidence` runs from 0 to 1.

```json
{
  "type": "score",
  "name": "resolution",
  "score": 0.007,
  "probabilities": [
    { "value": 0, "label": "unresolved", "probability": 0.993 },
    { "value": 1, "label": "partial", "probability": 0.007 },
    { "value": 2, "label": "resolved", "probability": 0.0003 }
  ],
  "confidence": 0.989
}
```

### Refusal

An answer of type `refusal` can replace the answer to any question. It carries
the question's `name` and no probabilities.

```json
{ "type": "refusal", "name": "resolution" }
```

## Send an image

To attach an image, send `input` as an array of user messages. Each message's
`content` is a string or an array of `input_text` and `input_image` parts.

```json
{
  "model": "spev",
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_text", "text": "Photo from the delivery driver." },
        { "type": "input_image", "image_url": "data:image/png;base64,iVBORw0KGgo..." }
      ]
    }
  ],
  "questions": [
    {
      "type": "predicate",
      "instructions": "Does the photo show a package at a door?"
    }
  ]
}
```

| Field | Required | Type | Notes |
| --- | --- | --- | --- |
| `role` | Yes | string | `user`. |
| `content` | Yes | string or array | Text, or an array of parts. |
| `input_image.image_url` | Yes | string | A `data:` URL or a public HTTPS URL. |
| `input_image.detail` | No | string | `low`, `high`, `auto`, or `original`. |

The number of images a request can carry depends on the model. See
[Models](/docs/decisions/models).

## Send audio

To attach audio, send the clip as `input_audio` next to `input`.

```json
{
  "model": "spev",
  "input": "Call recording attached.",
  "input_audio": { "data": "UklGRiQAAABXQVZF...", "format": "wav" },
  "questions": [
    { "type": "predicate", "instructions": "Does the caller ask to cancel?" }
  ]
}
```

`input_audio.data` is the base64-encoded file, not a `data:` URL.
`input_audio.format` names the container, such as `wav` or `mp3`.
`input_audio` is a Sprag extension to the request shape.

## Response

| Field | Type | Notes |
| --- | --- | --- |
| `model` | string | The model id from the request. |
| `answers` | array | One answer per question, in question order. |
| `usage.input_tokens` | integer | Input tokens across text, audio, and image evidence. |
| `usage.input_tokens_details.multimodal_tokens` | object or null | Input tokens by modality, such as `{"audio": 67}`. `null` for text-only evidence. |
| `usage.output_tokens` | integer | Output tokens. |
| `usage.total_tokens` | integer | Input and output tokens together. |

Decisions has no streaming mode. The response arrives once every question has
an answer.

## Errors

| Status | Meaning |
| --- | --- |
| `400` | Validation error, such as a duplicate question name, duplicate choice values, or fewer than two levels. |
| `401` | Missing, invalid, or expired credentials. |
| `402` | No payment method on the account. |
| `403` | Not a member of the requested organization. |
| `404` | Unknown model id. |
| `413` | Request too large. |
| `429` | Rate limit exceeded. |
| `502` | The model returned an error. Also sent when the model's answers do not match the questions. Retry the request. |
| `503` | Temporarily unavailable. Retry after `Retry-After` when it is set. |

`POST /v1/systemone` is retired. A request to it returns HTTP 410 with the
error code `endpoint_removed`.

See the [errors reference](/docs/api/errors) for the full error shape and retry
guidance.
