Make a decision
curl --request POST \
--url https://kymaapi.com/v1/decisions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "<string>",
"state": "<string>",
"questions": {}
}
'import requests
url = "https://kymaapi.com/v1/decisions"
payload = {
"model": "<string>",
"state": "<string>",
"questions": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: '<string>', state: '<string>', questions: {}})
};
fetch('https://kymaapi.com/v1/decisions', 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://kymaapi.com/v1/decisions",
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([
'model' => '<string>',
'state' => '<string>',
'questions' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://kymaapi.com/v1/decisions"
payload := strings.NewReader("{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://kymaapi.com/v1/decisions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kymaapi.com/v1/decisions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}"
response = http.request(request)
puts response.read_bodyEndpoints
Make a decision
Ask a model to pick one option per question, with a confidence. No prose, no parsing.
POST
/
v1
/
decisions
Make a decision
curl --request POST \
--url https://kymaapi.com/v1/decisions \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"model": "<string>",
"state": "<string>",
"questions": {}
}
'import requests
url = "https://kymaapi.com/v1/decisions"
payload = {
"model": "<string>",
"state": "<string>",
"questions": {}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({model: '<string>', state: '<string>', questions: {}})
};
fetch('https://kymaapi.com/v1/decisions', 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://kymaapi.com/v1/decisions",
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([
'model' => '<string>',
'state' => '<string>',
'questions' => [
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://kymaapi.com/v1/decisions"
payload := strings.NewReader("{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://kymaapi.com/v1/decisions")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://kymaapi.com/v1/decisions")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"model\": \"<string>\",\n \"state\": \"<string>\",\n \"questions\": {}\n}"
response = http.request(request)
puts response.read_bodySome calls are not writing tasks. Should this command run? Which model should serve this request? Is this ticket a bug or a feature? Those are choices, and a model that answers them in prose makes you parse the prose back into a choice.
POST /v1/decisions answers them as choices. You send the state and the questions, each with its options; every question comes back with one option and a confidence.
jev is a preview model (release_stage: "preview" on /v1/models). A decision that takes longer than 10 seconds returns 504.curl https://api.kymaapi.com/v1/decisions \
-H "Authorization: Bearer $KYMA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev",
"state": "Proposed shell command: `rm -rf ./build` in a CI job that has already published its artifacts.",
"questions": {
"gate": {
"type": "choice",
"instructions": "Should this command be allowed to run?",
"criteria": {
"allow": "safe, run it",
"deny": "refuse it",
"escalate": "ask a human"
}
}
}
}'
{
"id": "5b1f7c9e-2d4a-4c1e-9f0b-7a3d2e6c8b41",
"model": "jev",
"answers": {
"gate": {
"choice": "allow",
"confidence": 0.82,
"probabilities": { "allow": 0.82, "deny": 0.11, "escalate": 0.07 }
}
},
"usage": { "input_tokens": 383, "output_tokens": 41, "cost": 0.0001 }
}
Request
string
default:"jev"
The decisions model.
jev is the only value today; any other returns 400.string
required
What the model is deciding about, in your own words. Plain text.
object
required
One or more questions, keyed by an id you choose. Each must be
{"type": "choice", "instructions": "...", "criteria": { "<option>": "<what it means>", ... }} with at least one option. Anything else returns 400 naming the question.Response
answers is keyed by your question ids. Each carries choice (always one of the options you supplied), confidence between 0 and 1, and probabilities across the options when the model reports them.
Use the confidence: a common pattern is to act on the choice above a threshold you pick and hand the rest to a full language model, which costs more and answers slower.
What it will not do
It writes no prose, reads no images and calls no tools. It is a classifier you put in front of a model, a tool call or a risky command — not a replacement for one. Sending it to/v1/chat/completions returns 400.
Billing
Per input token, at the model’s published rate —$0.0567 per million for jev, with output free — rounded up to the nearest $0.0001, which is the smallest amount the ledger holds. A decision is small enough that the honest price of one call lands below that, so the rounding is what makes it chargeable at all: up to about 1,700 input tokens a call costs $0.0001, and 10,000 short decisions are $1. The response quotes usage.cost in dollars, and that is the amount taken from your balance — not a number rounded somewhere else afterwards.
Failures are not charged: an upstream error or an unreadable answer returns 502 and writes a $0 row you can see in usage.
Errors
| Status | error.type | When |
|---|---|---|
400 | invalid_request | state or questions missing or malformed, a question that is not a choice question, or a model other than jev |
401 | auth_error | Missing or invalid API key |
402 | insufficient_credits | Balance does not cover the call. The body names need_usd and have_usd. |
4xx | invalid_request | The model rejected the request itself; the status is the one it returned. Check state and questions. Not charged. |
502 | provider_unreachable | The model could not be reached. Not charged; retry. |
502 | server_error | The model returned an error or an answer Kyma could not read. Not charged; retry. |
503 | server_error | Decisions are unavailable on our side (for example, Kyma could not read your account to price the call). Not charged; retry. |
504 | timeout | The decision did not come back within 10 seconds. Not charged. |