Decisions
curl --request POST \
--url https://api.scaledown.xyz/v1/scaledown \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"state": {
"text": "<string>",
"document": "<string>",
"document_mime_type": "<string>"
},
"questions": {},
"model": "classify-1"
}
'import requests
url = "https://api.scaledown.xyz/v1/scaledown"
payload = {
"state": {
"text": "<string>",
"document": "<string>",
"document_mime_type": "<string>"
},
"questions": {},
"model": "classify-1"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
state: {text: '<string>', document: '<string>', document_mime_type: '<string>'},
questions: {},
model: 'classify-1'
})
};
fetch('https://api.scaledown.xyz/v1/scaledown', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scaledown.xyz/v1/scaledown",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'state' => [
'text' => '<string>',
'document' => '<string>',
'document_mime_type' => '<string>'
],
'questions' => [
],
'model' => 'classify-1'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scaledown.xyz/v1/scaledown"
payload := strings.NewReader("{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scaledown.xyz/v1/scaledown")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scaledown.xyz/v1/scaledown")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}"
response = http.request(request)
puts response.read_body{
"model": "<string>",
"answers": {},
"usage": {
"input_tokens": 123,
"output_tokens": 123,
"cost": 123
}
}Decisions
Decisions
Jev (TypeSafe)-compatible endpoint: answer typed choice/noul/score questions against a shared state object.
POST
/
v1
/
scaledown
Decisions
curl --request POST \
--url https://api.scaledown.xyz/v1/scaledown \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"state": {
"text": "<string>",
"document": "<string>",
"document_mime_type": "<string>"
},
"questions": {},
"model": "classify-1"
}
'import requests
url = "https://api.scaledown.xyz/v1/scaledown"
payload = {
"state": {
"text": "<string>",
"document": "<string>",
"document_mime_type": "<string>"
},
"questions": {},
"model": "classify-1"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
state: {text: '<string>', document: '<string>', document_mime_type: '<string>'},
questions: {},
model: 'classify-1'
})
};
fetch('https://api.scaledown.xyz/v1/scaledown', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scaledown.xyz/v1/scaledown",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'state' => [
'text' => '<string>',
'document' => '<string>',
'document_mime_type' => '<string>'
],
'questions' => [
],
'model' => 'classify-1'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.scaledown.xyz/v1/scaledown"
payload := strings.NewReader("{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.scaledown.xyz/v1/scaledown")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.scaledown.xyz/v1/scaledown")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"state\": {\n \"text\": \"<string>\",\n \"document\": \"<string>\",\n \"document_mime_type\": \"<string>\"\n },\n \"questions\": {},\n \"model\": \"classify-1\"\n}"
response = http.request(request)
puts response.read_body{
"model": "<string>",
"answers": {},
"usage": {
"input_tokens": 123,
"output_tokens": 123,
"cost": 123
}
}Overview
The/v1/scaledown endpoint mirrors TypeSafe’s Jev Decisions API request/response shape. You send a state object plus a questions map, and get back one typed answer per question. choice and noul questions are answered using the same calibrated scoring /classify uses; score questions are not supported - see score behavior below.
Request
string
default:"classify-1"
Optional. The only supported value is
"classify-1", which is also the default when omitted. Any other value is rejected with 422.object
required
The item being decided on.
Show State fields
Show State fields
string
The text to evaluate every question against. Provide
text, document, or both.string
A base64-encoded file to evaluate. Supported formats: JPEG, PNG, TIFF, single-page PDF, multi-page PDF. Non-standard extension - not part of the Jev API.
string
MIME type of
document (e.g. "image/jpeg", "application/pdf"). Required when document is provided.object
required
A map of question name → question definition. Must contain at least one entry. Every question is answered against the same
state, concurrently.Show choice question
Show choice question
Show noul question
Show noul question
Show score question (unsupported)
Show score question (unsupported)
string
required
"score" - accepted for request-shape compatibility only, never actually evaluated. See score behavior.Response
string
Always
"classify-1".object
Map of question name → typed answer, matching the
questions keys from the request.Show choice answer
Show choice answer
Show unsupported answer
Show unsupported answer
string
"unsupported"string
Why this question type isn’t answered - always
"'score' questions are not supported by this API" today. Only appears in a mixed request that also has at least one choice/noul question - see score behavior.object
score behavior
We have no ordered-scale scoring primitive, so a score question is never actually evaluated. What happens depends on whether the request has anything else in it:
- Mixed request (at least one
choice/noulquestion alongside thescorequestion(s)): returns200. Thechoice/noulquestions are answered normally; eachscorequestion’s answer comes back as{"type": "unsupported", "reason": "..."}and contributes nothing tousage. - All-
scorerequest (every question inquestionsis typescore): returns422. Nothing in the request could be answered, so it fails outright instead of returning a200full of"unsupported"placeholders.
Error responses
| Status | Meaning |
|---|---|
422 Unprocessable Entity | Malformed request body, empty questions map, empty criteria on a choice question, neither state.text nor state.document provided, every question in the request is type score, an unsupported model value, or OCR failed. |
402 Payment Required | Insufficient credits. |
502 Bad Gateway | Model service unavailable or returned an error. |
504 Gateway Timeout | Request timed out. |
Authentication
Include your API key in every request using thex-api-key header.
-H "x-api-key: <your-api-key>"
Examples
Single choice question
curl -X POST https://api.scaledown.xyz/v1/scaledown \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"model": "classify-1",
"state": { "text": "I was charged twice for my subscription this month." },
"questions": {
"category": {
"type": "choice",
"instructions": "Which single category best describes the post?",
"criteria": {
"billing": "About a charge, invoice, refund, or payment problem.",
"technical": "About a bug or something not working.",
"account": "About login, access, or account settings."
}
}
}
}'
import requests
response = requests.post(
"https://api.scaledown.xyz/v1/scaledown",
headers={"x-api-key": "<your-api-key>"},
json={
"model": "classify-1",
"state": {"text": "I was charged twice for my subscription this month."},
"questions": {
"category": {
"type": "choice",
"instructions": "Which single category best describes the post?",
"criteria": {
"billing": "About a charge, invoice, refund, or payment problem.",
"technical": "About a bug or something not working.",
"account": "About login, access, or account settings.",
},
}
},
},
)
print(response.json())
const response = await fetch("https://api.scaledown.xyz/v1/scaledown", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<your-api-key>",
},
body: JSON.stringify({
model: "classify-1",
state: { text: "I was charged twice for my subscription this month." },
questions: {
category: {
type: "choice",
instructions: "Which single category best describes the post?",
criteria: {
billing: "About a charge, invoice, refund, or payment problem.",
technical: "About a bug or something not working.",
account: "About login, access, or account settings.",
},
},
},
}),
});
const data = await response.json();
{
"model": "classify-1",
"answers": {
"category": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 0.93, "technical": 0.04, "account": 0.03 },
"confidence": 0.93
}
},
"usage": { "input_tokens": 62, "output_tokens": 1, "cost": 0.0000026 }
}
Mixed choice + noul questions
choice and noul questions can be combined in one request. Both are answered concurrently against the same state.
curl -X POST https://api.scaledown.xyz/v1/scaledown \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"state": { "text": "The invoice is overdue and the customer is threatening to cancel." },
"questions": {
"category": {
"type": "choice",
"criteria": {
"billing": "About a charge, invoice, or payment matter.",
"legal": "About a legal or contractual dispute."
}
},
"is_churn_risk": {
"type": "noul",
"instructions": "Does this text signal the customer may cancel or leave?"
}
}
}'
import requests
response = requests.post(
"https://api.scaledown.xyz/v1/scaledown",
headers={"x-api-key": "<your-api-key>"},
json={
"state": {"text": "The invoice is overdue and the customer is threatening to cancel."},
"questions": {
"category": {
"type": "choice",
"criteria": {
"billing": "About a charge, invoice, or payment matter.",
"legal": "About a legal or contractual dispute.",
},
},
"is_churn_risk": {
"type": "noul",
"instructions": "Does this text signal the customer may cancel or leave?",
},
},
},
)
print(response.json())
const response = await fetch("https://api.scaledown.xyz/v1/scaledown", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": "<your-api-key>",
},
body: JSON.stringify({
state: { text: "The invoice is overdue and the customer is threatening to cancel." },
questions: {
category: {
type: "choice",
criteria: {
billing: "About a charge, invoice, or payment matter.",
legal: "About a legal or contractual dispute.",
},
},
is_churn_risk: {
type: "noul",
instructions: "Does this text signal the customer may cancel or leave?",
},
},
}),
});
const data = await response.json();
{
"model": "classify-1",
"answers": {
"category": {
"type": "choice",
"choice": "billing",
"probabilities": { "billing": 0.88, "legal": 0.12 },
"confidence": 0.88
},
"is_churn_risk": { "type": "noul", "noul": 0.79 }
},
"usage": { "input_tokens": 94, "output_tokens": 2, "cost": 0.0000039 }
}
A score question in the mix
score questions are accepted but always answered as "unsupported" - the rest of the request still succeeds.
curl -X POST https://api.scaledown.xyz/v1/scaledown \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"state": { "text": "Great product, but the onboarding was confusing." },
"questions": {
"sentiment": {
"type": "choice",
"criteria": { "positive": "Overall positive tone.", "negative": "Overall negative tone." }
},
"quality_score": {
"type": "score",
"instructions": "Rate overall quality from 1 to 5."
}
}
}'
sentiment is answered normally; quality_score comes back as unsupported and costs nothing.
{
"model": "classify-1",
"answers": {
"sentiment": {
"type": "choice",
"choice": "positive",
"probabilities": { "positive": 0.81, "negative": 0.19 },
"confidence": 0.81
},
"quality_score": {
"type": "unsupported",
"reason": "'score' questions are not supported by this API"
}
},
"usage": { "input_tokens": 41, "output_tokens": 1, "cost": 0.0000017 }
}
An all-score request
When every question in the request is score, nothing can be answered, so the request fails with 422 instead of returning 200 with everything marked unsupported.
curl -X POST https://api.scaledown.xyz/v1/scaledown \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"state": { "text": "Great product, but the onboarding was confusing." },
"questions": {
"quality_score": {
"type": "score",
"instructions": "Rate overall quality from 1 to 5."
}
}
}'
422 Unprocessable Entity
{
"detail": "'score' questions are not supported by this API, and no other question type was provided to answer."
}
Notes
"classify-1"is the only supportedmodel. Omit it (defaults to"classify-1") or pass it explicitly - any other value is rejected with422.- Every
choice/noulquestion in a request is one separate calibrated model call, run concurrently.usageis the sum across all of them - a request with achoiceand twonoulquestions makes 3 calls and bills for all 3. - A request made entirely of
scorequestions returns422, not200- seescorebehavior. - No
reasoningfield exists on this endpoint - it never generates a free-form explanation, unlike/classifywithreasoning: true. - No batching - each request evaluates one
state. To classify many items, use/classify’schunksarray instead. - See Differences from Jev for the full compatibility matrix.
Authorizations
Body
application/json
The item being decided on. Provide text, document, or both.
Show child attributes
Show child attributes
Map of question name to question definition. Must contain at least one entry.
Show child attributes
Show child attributes
Optional. "classify-1" is the only supported value and also the default when omitted. Any other value returns 422.
Available options:
classify-1