> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/docs/_mcp/server.

# Run span-02

POST https://api.respan.ai/api/v1/decisions
Content-Type: application/json

Answer typed questions about a state with span-02, Respan's decision model. Each question is `choice`, `noul` (yes/no), `score` or `number`, and each answer carries its probability distribution and confidence. One request can mix all four types.

Set a thinking deadline with `reasoning.budget_ms`: span-02 thinks until then and answers with its best decision so far. The body is the System One format, so a System One client works by changing the URL. span-02 runs on Respan credits only and is priced per input and output token; thought tokens aren't billed. Streaming is unsupported.

Reference: https://respan.ai/docs/apis/gateway/run-span-02

## Authentication

- `Authorization` header (bearer token, required) — Use your Respan API key for Respan API authentication. Enter only the Respan API key value; clients send Authorization: Bearer \<RESPAN\_API\_KEY>. For /api/responses, provider credentials such as Perplexity, OpenAI, or Azure OpenAI go in Settings -> Providers or respan\_params.credential\_override in the request body, not in this authentication field.

## Request

### Body (application/json)

This endpoint expects a Span2DecisionRequest.

- `state` (Span2DecisionRequestState, required) — What the questions are about: text or any JSON value.
- `questions` (map from string to Span2DecisionRequestQuestions, required) — Your question ids mapped to questions. Answers come back under the same ids.
- `model` (string, optional, default: span-02) — Use `span-02` or `respan/span-02`. Omit for `span-02`.
- `images` (list of string, optional) — Up to 4 images, as data URIs or base64. span-02 sees them before the text.
- `reasoning` (Span2Reasoning, optional) — How span-02 thinks before it answers. The thought itself is never returned, and its tokens aren't billed.
- `respan_params` (map from string to any, optional) — Respan parameters for the request log, such as `customer_identifier`.

## Response

### 200

Answers keyed by question id. The example is the captured response to the request shown; probabilities can vary slightly between calls.

- `model` (enum, required) — The model that answered.
  - Allowed values: `span-02`
- `answers` (map from string to Span2DecisionResponseAnswers, required) — Answers keyed by the ids in `questions`.
- `usage` (Span2DecisionResponseUsage, optional) — Billed token counts. Thought tokens aren't billed.

## Errors

### 400 Bad Request Error

Invalid request: an unknown model, `stream: true`, or questions the model rejects. Correct the request before retrying.

- `map from string to any`

### 402 Payment Required Error

The organization has no Respan credits. Add credits to call span-02.

- `map from string to any`

### 403 Forbidden Error

Missing, invalid or expired Respan API key.

- `map from string to any`

### 429 Too Many Requests Error

The organization's span-02 rate limit was reached. Retry with exponential backoff.

- `map from string to any`

### 504 Gateway Timeout Error

span-02 didn't answer in time. Retry; if it persists, send fewer questions or a shorter state.

- `map from string to any`

## Types

### Span2DecisionRequestState

What the questions are about: text or any JSON value.

### Span2DecisionRequestQuestions

### Span2Reasoning

How span-02 thinks before it answers. The thought itself is never returned, and its tokens aren't billed.

- `mode` (enum, optional, default: auto) — `auto` thinks only when span-02 is unsure, `on` thinks for every question, `off` never thinks.
  - Allowed values: `auto`, `on`, `off`
- `budget_ms` (integer, optional, default: 0) — Thinking time for the request in milliseconds. span-02 thinks until then, then answers with its best decision so far. `0` means no deadline; a per-question cap on thought length still applies.

### Span2DecisionResponseAnswers

### Span2DecisionResponseUsage

Billed token counts. Thought tokens aren't billed.

- `input_tokens` (integer, optional)
- `output_tokens` (integer, optional)

### TypeSafeChoiceQuestion

Select an option from the supplied criteria.

- `type` (enum, required)
  - Allowed values: `choice`
- `criteria` (map from string to TypeSafeEntry, required) — Option names mapped to descriptions. Use null for an option without a description.
- `instructions` (TypeSafeEntry, optional) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.

### TypeSafeNoulQuestion

A yes/no judgment expressed as a probability.

- `type` (enum, required)
  - Allowed values: `noul`
- `instructions` (TypeSafeEntry, optional) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.
- `criteria` (TypeSafeNoulQuestionCriteria, optional, nullable) — Optional guidance for yes and no.

### TypeSafeScoreQuestion

Evaluate the state on an ordered rubric.

- `type` (enum, required)
  - Allowed values: `score`
- `criteria` (list of TypeSafeContent, required) — Rubric levels in order, starting at index 0.
- `instructions` (TypeSafeEntry, optional) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.

### Span2NumberQuestion

Ask for a number. span-02 answers with the number itself, so no options are listed.

- `type` (enum, required)
  - Allowed values: `number`
- `instructions` (TypeSafeEntry, required) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.

### TypeSafeChoiceAnswer

- `type` (enum, required)
  - Allowed values: `choice`
- `choice` (string, required) — Selected option name.
- `probabilities` (map from string to double, required)
- `confidence` (double, required)

### TypeSafeNoulAnswer

- `type` (enum, required)
  - Allowed values: `noul`
- `noul` (double, required) — Probability of yes: 0 means no, 1 means yes.

### TypeSafeScoreAnswer

- `type` (enum, required)
  - Allowed values: `score`
- `score` (double, required) — Probability-weighted level index; may be fractional.
- `legend` (map from string to TypeSafeContent, required) — Level indices mapped to rubric descriptions.
- `probabilities` (map from string to double, required)
- `confidence` (double, required)

### Span2NumberAnswer

- `type` (enum, required)
  - Allowed values: `number`
- `number` (double, required, nullable) — The answer as a number, or null if the model's text is not a number.
- `text` (string, required) — The answer as the model wrote it.
- `confidence` (double, required) — How sure span-02 is of the answer.

### TypeSafeEntry

Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.

### TypeSafeNoulQuestionCriteria

Optional guidance for yes and no.

- `true` (TypeSafeEntry, optional) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.
- `false` (TypeSafeEntry, optional) — Question instructions or rubric descriptions. Accepts text, an object, an array, or null. See https://docs.typesafe.ai/primitives/advanced.

### TypeSafeContent

Text or structured context supplied to Jev.

## Examples

**Request**

```json
{
  "state": {
    "customer_message": "My package arrived damaged and nobody answers my emails. I want a refund now.",
    "order": {
      "id": 123,
      "days_late": 4
    }
  },
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "criteria": {
        "returns": "Exchanges, wrong or damaged items",
        "shipping": "Delivery status, delays, lost packages",
        "billing": "Charges, invoices, payment problems"
      }
    },
    "wants_refund": {
      "type": "noul",
      "instructions": "Does the customer ask for a refund?"
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": [
        "Calm or positive",
        "Mildly annoyed",
        "Angry or threatening to leave"
      ]
    },
    "days_late": {
      "type": "number",
      "instructions": "How many days late is the order?"
    }
  },
  "model": "span-02",
  "reasoning": {
    "mode": "auto"
  }
}
```

**Response**

```json
{
  "model": "span-02",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "returns",
      "confidence": 0.969893,
      "probabilities": {
        "returns": 0.9799287,
        "shipping": 0.012929817,
        "billing": 0.00714149
      }
    },
    "wants_refund": {
      "type": "noul",
      "noul": 0.99997675
    },
    "frustration": {
      "type": "score",
      "score": 1.9921819,
      "legend": {
        "0": "Calm or positive",
        "1": "Mildly annoyed",
        "2": "Angry or threatening to leave"
      },
      "probabilities": {
        "0": 0.00009271287,
        "1": 0.0076327343,
        "2": 0.9922746
      },
      "confidence": 0.9882728
    },
    "days_late": {
      "type": "number",
      "number": 4,
      "text": "4",
      "confidence": 0.9999089
    }
  },
  "usage": {
    "input_tokens": 580,
    "output_tokens": 1
  }
}
```

**SDK Code**

```python Respan Models_createDecisions_example
import requests

url = "https://api.respan.ai/api/v1/decisions"

payload = {
    "state": {
        "customer_message": "My package arrived damaged and nobody answers my emails. I want a refund now.",
        "order": {
            "id": 123,
            "days_late": 4
        }
    },
    "questions": {
        "department": {
            "type": "choice",
            "instructions": "Which team should handle this ticket?",
            "criteria": {
                "returns": "Exchanges, wrong or damaged items",
                "shipping": "Delivery status, delays, lost packages",
                "billing": "Charges, invoices, payment problems"
            }
        },
        "wants_refund": {
            "type": "noul",
            "instructions": "Does the customer ask for a refund?"
        },
        "frustration": {
            "type": "score",
            "instructions": "How frustrated is the customer?",
            "criteria": ["Calm or positive", "Mildly annoyed", "Angry or threatening to leave"]
        },
        "days_late": {
            "type": "number",
            "instructions": "How many days late is the order?"
        }
    },
    "model": "span-02",
    "reasoning": { "mode": "auto" }
}
headers = {
    "Authorization": "Bearer <respanApiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript Respan Models_createDecisions_example
const url = 'https://api.respan.ai/api/v1/decisions';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <respanApiKey>', 'Content-Type': 'application/json'},
  body: '{"state":{"customer_message":"My package arrived damaged and nobody answers my emails. I want a refund now.","order":{"id":123,"days_late":4}},"questions":{"department":{"type":"choice","instructions":"Which team should handle this ticket?","criteria":{"returns":"Exchanges, wrong or damaged items","shipping":"Delivery status, delays, lost packages","billing":"Charges, invoices, payment problems"}},"wants_refund":{"type":"noul","instructions":"Does the customer ask for a refund?"},"frustration":{"type":"score","instructions":"How frustrated is the customer?","criteria":["Calm or positive","Mildly annoyed","Angry or threatening to leave"]},"days_late":{"type":"number","instructions":"How many days late is the order?"}},"model":"span-02","reasoning":{"mode":"auto"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go Respan Models_createDecisions_example
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.respan.ai/api/v1/decisions"

	payload := strings.NewReader("{\n  \"state\": {\n    \"customer_message\": \"My package arrived damaged and nobody answers my emails. I want a refund now.\",\n    \"order\": {\n      \"id\": 123,\n      \"days_late\": 4\n    }\n  },\n  \"questions\": {\n    \"department\": {\n      \"type\": \"choice\",\n      \"instructions\": \"Which team should handle this ticket?\",\n      \"criteria\": {\n        \"returns\": \"Exchanges, wrong or damaged items\",\n        \"shipping\": \"Delivery status, delays, lost packages\",\n        \"billing\": \"Charges, invoices, payment problems\"\n      }\n    },\n    \"wants_refund\": {\n      \"type\": \"noul\",\n      \"instructions\": \"Does the customer ask for a refund?\"\n    },\n    \"frustration\": {\n      \"type\": \"score\",\n      \"instructions\": \"How frustrated is the customer?\",\n      \"criteria\": [\n        \"Calm or positive\",\n        \"Mildly annoyed\",\n        \"Angry or threatening to leave\"\n      ]\n    },\n    \"days_late\": {\n      \"type\": \"number\",\n      \"instructions\": \"How many days late is the order?\"\n    }\n  },\n  \"model\": \"span-02\",\n  \"reasoning\": {\n    \"mode\": \"auto\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <respanApiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby Respan Models_createDecisions_example
require 'uri'
require 'net/http'

url = URI("https://api.respan.ai/api/v1/decisions")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <respanApiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"state\": {\n    \"customer_message\": \"My package arrived damaged and nobody answers my emails. I want a refund now.\",\n    \"order\": {\n      \"id\": 123,\n      \"days_late\": 4\n    }\n  },\n  \"questions\": {\n    \"department\": {\n      \"type\": \"choice\",\n      \"instructions\": \"Which team should handle this ticket?\",\n      \"criteria\": {\n        \"returns\": \"Exchanges, wrong or damaged items\",\n        \"shipping\": \"Delivery status, delays, lost packages\",\n        \"billing\": \"Charges, invoices, payment problems\"\n      }\n    },\n    \"wants_refund\": {\n      \"type\": \"noul\",\n      \"instructions\": \"Does the customer ask for a refund?\"\n    },\n    \"frustration\": {\n      \"type\": \"score\",\n      \"instructions\": \"How frustrated is the customer?\",\n      \"criteria\": [\n        \"Calm or positive\",\n        \"Mildly annoyed\",\n        \"Angry or threatening to leave\"\n      ]\n    },\n    \"days_late\": {\n      \"type\": \"number\",\n      \"instructions\": \"How many days late is the order?\"\n    }\n  },\n  \"model\": \"span-02\",\n  \"reasoning\": {\n    \"mode\": \"auto\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java Respan Models_createDecisions_example
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.respan.ai/api/v1/decisions")
  .header("Authorization", "Bearer <respanApiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"state\": {\n    \"customer_message\": \"My package arrived damaged and nobody answers my emails. I want a refund now.\",\n    \"order\": {\n      \"id\": 123,\n      \"days_late\": 4\n    }\n  },\n  \"questions\": {\n    \"department\": {\n      \"type\": \"choice\",\n      \"instructions\": \"Which team should handle this ticket?\",\n      \"criteria\": {\n        \"returns\": \"Exchanges, wrong or damaged items\",\n        \"shipping\": \"Delivery status, delays, lost packages\",\n        \"billing\": \"Charges, invoices, payment problems\"\n      }\n    },\n    \"wants_refund\": {\n      \"type\": \"noul\",\n      \"instructions\": \"Does the customer ask for a refund?\"\n    },\n    \"frustration\": {\n      \"type\": \"score\",\n      \"instructions\": \"How frustrated is the customer?\",\n      \"criteria\": [\n        \"Calm or positive\",\n        \"Mildly annoyed\",\n        \"Angry or threatening to leave\"\n      ]\n    },\n    \"days_late\": {\n      \"type\": \"number\",\n      \"instructions\": \"How many days late is the order?\"\n    }\n  },\n  \"model\": \"span-02\",\n  \"reasoning\": {\n    \"mode\": \"auto\"\n  }\n}")
  .asString();
```

```php Respan Models_createDecisions_example
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.respan.ai/api/v1/decisions', [
  'body' => '{
  "state": {
    "customer_message": "My package arrived damaged and nobody answers my emails. I want a refund now.",
    "order": {
      "id": 123,
      "days_late": 4
    }
  },
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "criteria": {
        "returns": "Exchanges, wrong or damaged items",
        "shipping": "Delivery status, delays, lost packages",
        "billing": "Charges, invoices, payment problems"
      }
    },
    "wants_refund": {
      "type": "noul",
      "instructions": "Does the customer ask for a refund?"
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": [
        "Calm or positive",
        "Mildly annoyed",
        "Angry or threatening to leave"
      ]
    },
    "days_late": {
      "type": "number",
      "instructions": "How many days late is the order?"
    }
  },
  "model": "span-02",
  "reasoning": {
    "mode": "auto"
  }
}',
  'headers' => [
    'Authorization' => 'Bearer <respanApiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp Respan Models_createDecisions_example
using RestSharp;

var client = new RestClient("https://api.respan.ai/api/v1/decisions");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <respanApiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"state\": {\n    \"customer_message\": \"My package arrived damaged and nobody answers my emails. I want a refund now.\",\n    \"order\": {\n      \"id\": 123,\n      \"days_late\": 4\n    }\n  },\n  \"questions\": {\n    \"department\": {\n      \"type\": \"choice\",\n      \"instructions\": \"Which team should handle this ticket?\",\n      \"criteria\": {\n        \"returns\": \"Exchanges, wrong or damaged items\",\n        \"shipping\": \"Delivery status, delays, lost packages\",\n        \"billing\": \"Charges, invoices, payment problems\"\n      }\n    },\n    \"wants_refund\": {\n      \"type\": \"noul\",\n      \"instructions\": \"Does the customer ask for a refund?\"\n    },\n    \"frustration\": {\n      \"type\": \"score\",\n      \"instructions\": \"How frustrated is the customer?\",\n      \"criteria\": [\n        \"Calm or positive\",\n        \"Mildly annoyed\",\n        \"Angry or threatening to leave\"\n      ]\n    },\n    \"days_late\": {\n      \"type\": \"number\",\n      \"instructions\": \"How many days late is the order?\"\n    }\n  },\n  \"model\": \"span-02\",\n  \"reasoning\": {\n    \"mode\": \"auto\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift Respan Models_createDecisions_example
import Foundation

let headers = [
  "Authorization": "Bearer <respanApiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "state": [
    "customer_message": "My package arrived damaged and nobody answers my emails. I want a refund now.",
    "order": [
      "id": 123,
      "days_late": 4
    ]
  ],
  "questions": [
    "department": [
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "criteria": [
        "returns": "Exchanges, wrong or damaged items",
        "shipping": "Delivery status, delays, lost packages",
        "billing": "Charges, invoices, payment problems"
      ]
    ],
    "wants_refund": [
      "type": "noul",
      "instructions": "Does the customer ask for a refund?"
    ],
    "frustration": [
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": ["Calm or positive", "Mildly annoyed", "Angry or threatening to leave"]
    ],
    "days_late": [
      "type": "number",
      "instructions": "How many days late is the order?"
    ]
  ],
  "model": "span-02",
  "reasoning": ["mode": "auto"]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.respan.ai/api/v1/decisions")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```