Decisions
OpenAI Decisions API
Ask GPT-6 Luna Decisions classification and scoring questions about text, images or JSON, and get typed answers your code can act on: how likely a statement is true, which of your values fits, or where it sits on your scale, each with its probabilities. Many questions in one request, usually in under a second, through OpenAI’s own Decisions API and SDKs.
/v1/decisionsGPT-6 Luna Decisions is OpenAI’s decision model. It reads your input and answers the questions you set about it, and it writes no text, so it isn’t a chat model: send it to /v1/decisions, not /v1/chat/completions. On this endpoint gpt-6-luna works as the model ID too, as in OpenAI’s own SDK docs, so OpenAI’s sample code runs unchanged; the reply’s model says gpt-6-luna-decisions. For Jev, TypeSafe’s decision model, see Decisions (Jev).
Ask questions
# pip install --upgrade openaiimport osfrom openai import OpenAIclient = OpenAI( base_url="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"],)decision = client.decisions.create( model="gpt-6-luna-decisions", input="My headphones stopped charging two days after they arrived. I want a refund.", questions=[ { "type": "predicate", "name": "refund", "instructions": "Is the customer asking for a refund?", }, { "type": "choice", "name": "topic", "instructions": "What is the ticket about?", "choices": [ {"value": "billing", "description": "Charges and invoices"}, {"value": "hardware", "description": "A device that is broken or faulty"}, {"value": "shipping", "description": "Delivery and tracking"}, ], }, { "type": "score", "name": "sentiment", "instructions": "How does the customer feel?", "levels": [ {"label": "very negative"}, {"label": "negative"}, {"label": "neutral"}, ], }, ],)# One answer per question, in the order askedfor answer in decision.answers: if answer.type == "refusal": print(answer.name, "refused") elif answer.type == "predicate": print(answer.name, answer.probability) elif answer.type == "choice": print(answer.name, answer.choice, answer.confidence) else: print(answer.name, answer.score, answer.confidence)print(decision.usage.input_tokens, "input tokens")Point OpenAI’s SDK at https://api.zurelay.com/v1, with /v1 as for every OpenAI model on Zurelay, use your Zurelay key, and call client.decisions.create. In Node.js that needs openai 7.30.0 or later; in Python, an openai package recent enough to have client.decisions (3.41 has it). Any HTTP client works the same way: POST the JSON with your key as a Bearer token.
The answer
{ "id": "req_3Fh8sLq0vW2nTc7yRb5kZp1M", "model": "gpt-6-luna-decisions", "answers": [ { "type": "predicate", "name": "refund", "probability": 1 }, { "type": "choice", "name": "topic", "choice": "hardware", "confidence": 1, "probabilities": [ { "value": "billing", "probability": 0 }, { "value": "hardware", "probability": 1 }, { "value": "shipping", "probability": 0 } ] }, { "type": "score", "name": "sentiment", "score": 0.18, "confidence": 0.85, "probabilities": [ { "value": 0, "label": "very negative", "probability": 0.82 }, { "value": 1, "label": "negative", "probability": 0.18 }, { "value": 2, "label": "neutral", "probability": 0 } ] } ], "usage": { "input_tokens": 325, "input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 }, "output_tokens": 0, "output_tokens_details": { "reasoning_tokens": 0 }, "total_tokens": 325, "cost": 0.000013 }}answers has one answer per question, in the order you asked them, each with its type and, when you gave one, its name. usage is what the request was billed on: input_tokens, with output_tokens always 0, and cost what it cost in US dollars. The reply’s x-request-id header finds it in your request log, and x-zurelay-input-tokens repeats what it was billed on.
Parameters
modelstringrequiredgpt-6-luna-decisions, orgpt-6-lunaon this endpoint.inputstring or arrayrequired- What to decide about: a non-empty text, or an array of user messages,
{ "role": "user", "content": ... }("type": "message"is optional). A message’scontentis a text or an array of parts, below. questionsarrayrequired- 1 to 64 questions, answered in the order you list them.
safety_identifierstring- Optional: an opaque id for the end user the request is for, such as a hash of your own user id.
prompt_cache_keystring- Accepted and ignored, as is
prompt_cache_options: caching is automatic, and every input token costs the same.
typestringrequiredpredicate,choiceorscore.instructionsstringrequired- The question, as text. Images go in the input, not here.
namestring- Optional, and unique within the request: comes back on the question’s answer, so you can read answers by name.
choicesarray- For a
choice(required): 2 to 255 values, each{ "value": ..., "description": ... }. A value is text ortrue/false, and each must be unique; the description is optional. levelsarray- For a
score(required): 2 to 10 levels, lowest first, each{ "label": ..., "description": ... }, the description optional.
input_text{ text }- Text.
input_image{ image_url, detail }- A PNG, JPEG, WebP or GIF image: a base64 data URL, or a public
httpslink that Zurelay downloads.detailis optional:low,high,autoororiginal. input_json{ data }- An object or an array, such as a record or your app’s state. The model reads it as compact JSON text, and it’s counted as that text.
Question types
One request can mix all three. The input is read once for all of them, so asking several questions in one request costs less than asking each in a request of its own.
Predicate
A statement or yes/no question about the input. The answer’s probability is how likely it is true: near 1 for yes, near 0 for no, and in between when the input doesn’t say clearly.
{ "type": "predicate", "name": "refund", "instructions": "Is the customer asking for a refund?" }{ "type": "predicate", "name": "refund", "probability": 1 }Choice
Picks one of the values you list, and gives the probability of each. A description says what a value means, which helps when the value alone is a code. Values can be true and false too, and the text "true" and the boolean true are different values.
{ "type": "choice", "name": "language", "instructions": "Which language is the message written in?", "choices": [ { "value": "en", "description": "English" }, { "value": "es", "description": "Spanish" }, { "value": "other", "description": "Any other language" } ]}Score
Places the input on your levels, lowest first. score is the expected level: each level’s index, counting from 0, weighted by its probability. So it can land between two levels: in the example response above, 0.18 is mostly the first level with a little of the second. Each entry in probabilities has the level’s index as value and its label.
{ "type": "score", "name": "relevance", "instructions": "How well does the passage answer the search query?", "levels": [ { "label": "irrelevant" }, { "label": "related", "description": "On the topic, but doesn't answer it" }, { "label": "answers it" } ]}Images
Send images in the input, as parts of a user message beside any text. Give a base64 data URL, or a public https link, which Zurelay downloads and sends on: PNG, JPEG, WebP or GIF. Send big images as links.
import base64, osfrom openai import OpenAIclient = OpenAI(base_url="https://api.zurelay.com/v1", api_key=os.environ["ZURELAY_API_KEY"])with open("return-photo.jpg", "rb") as file: photo = "data:image/jpeg;base64," + base64.b64encode(file.read()).decode()decision = client.decisions.create( model="gpt-6-luna-decisions", input=[ { "role": "user", "content": [ {"type": "input_text", "text": "Photo sent with a return request for a blender."}, {"type": "input_image", "image_url": photo, "detail": "low"}, ], } ], questions=[ { "type": "predicate", "name": "damaged", "instructions": "Does the photo show visible damage to the product?", }, { "type": "choice", "name": "product", "instructions": "What product is in the photo?", "choices": [ {"value": "blender"}, {"value": "kettle"}, {"value": "other", "description": "Anything else, or no product"}, ], }, ],)print(decision.answers)An image adds tokens by its size and detail: a 1200 × 630 picture is about 750 tokens at high or auto, and about 150 at low. Use low when the question is about the whole picture rather than fine print. Images go in the input only: a question’s instructions take text.
JSON input
Structured data can go in as it is, in an input_json part, beside text or images. The model reads it as compact JSON text, and it’s billed as that text.
"input": [ { "role": "user", "content": [ { "type": "input_text", "text": "A sign-up from our app:" }, { "type": "input_json", "data": { "name": "Free Gift Card Giveaway", "email": "win-now@example.net", "bio": "Click my link" } } ] }]Refusals
Any question can come back as a refusal instead of an answer, while the others in the request are still answered. Check each answer’s type before you read its fields. In our tests, the input “Refund requested 38 hours ago.” with the predicate “Page on-call?” came back refused.
{ "type": "refusal", "name": "page_on_call" }A refusal comes in a normal reply, whose usage says what the request was billed on. Ask again with a clearer question, or send the case to a person.
Limits
- Up to 1.05M tokens a request: the input and every question together. An input of 918K tokens worked in our tests.
- 1 to 64 questions. A
choicetakes 2 to 255 unique values and ascore2 to 10 levels. - Only user messages: system, developer and assistant messages, and function or tool call items, are refused. Put context in a user message, or in a question’s instructions.
- No streaming:
streamis refused, and the answers come back whole. - A malformed request, or one with a field the API doesn’t take, is refused with a
400that names the field (such asquestions[2].choices) before it reaches the model, and costs nothing. - A key limited to some models needs
gpt-6-luna-decisionson its list, also for requests that saygpt-6-luna. See Key limits and rules.
Speed
Answers usually come back in 0.3 to 1 second, and in about a second with an image, in our tests. A 900K-token input took about 10 seconds.
Paying for decisions
GPT-6 Luna Decisions costs $0.04 per 1M input tokens, and its answers are free. The tokens are your input, counted once, plus about 110 to 160 of the model’s own for each question: one short predicate on a short text is about 160 tokens in all, two about 320, and a 2,600-token input with four questions about 3,200. Images add their tokens as above. So a one-question check on a short text costs about $0.0000064, and a million of them about $6.40.
Each reply’s usage and x-zurelay-input-tokens header say what it was billed on, every request is in your request log, and requests that fail cost nothing.
Errors
Keys, credit, budgets and rate limits give the same errors as every other endpoint: see Errors. OpenAI’s SDKs retry the ones worth retrying.
Try it first
Questions, or something missing? Ask our support team and we’ll answer by email, usually within a minute.