Skip to content
Docs

OpenAI-Compatible Decisions API with AI Gateway

Ask typed questions about shared input and get back probabilities, choices, and scores using the OpenAI-compatible /decisions endpoint. It implements the same request and response shapes as the OpenAI Decisions API, so the OpenAI SDKs work with only a base URL change.

Any decision model on AI Gateway can answer, not just OpenAI's. For an overview of decision models and the AI SDK decide function, see Decision. This page covers the OpenAI-compatible REST endpoint.

The Decisions API is available at the following base URL:

https://ai-gateway.vercel.sh/v1

The Decisions API supports the same authentication methods as the main AI Gateway:

  • API key: Use your AI Gateway API key with the Authorization: Bearer <token> header
  • OIDC token: Use your Edgenivo OIDC token with the Authorization: Bearer <token> header

You only need one of these. If an API key is specified it takes precedence over any OIDC token, even if the API key is invalid.

POST /decisions
decisions.ts
import OpenAI from 'openai';
 
const openai = new OpenAI({
  apiKey: process.env.AI_GATEWAY_API_KEY,
  baseURL: 'https://ai-gateway.vercel.sh/v1',
});
 
const decision = await openai.decisions.create({
  model: 'openai/gpt-6-luna-decisions',
  input: 'The package arrived with a broken screen. I want my money back.',
  questions: [
    {
      type: 'predicate',
      name: 'damaged',
      instructions: 'Does the customer report a damaged item?',
    },
    {
      type: 'choice',
      name: 'queue',
      instructions: 'Which team should handle this ticket?',
      choices: [
        { value: 'billing', description: 'Refunds and payments' },
        { value: 'shipping', description: 'Delivery and damaged items' },
      ],
    },
    {
      type: 'score',
      name: 'urgency',
      instructions: 'How urgent is this ticket?',
      levels: [{ label: 'low' }, { label: 'medium' }, { label: 'high' }],
    },
  ],
});
 
console.log(decision.answers);
decisions.py
import os
from openai import OpenAI
 
client = OpenAI(
    api_key=os.getenv("AI_GATEWAY_API_KEY"),
    base_url="https://ai-gateway.vercel.sh/v1",
)
 
decision = client.decisions.create(
    model="openai/gpt-6-luna-decisions",
    input="The package arrived with a broken screen. I want my money back.",
    questions=[
        {
            "type": "predicate",
            "name": "damaged",
            "instructions": "Does the customer report a damaged item?",
        },
        {
            "type": "choice",
            "name": "queue",
            "instructions": "Which team should handle this ticket?",
            "choices": [
                {"value": "billing", "description": "Refunds and payments"},
                {"value": "shipping", "description": "Delivery and damaged items"},
            ],
        },
        {
            "type": "score",
            "name": "urgency",
            "instructions": "How urgent is this ticket?",
            "levels": [{"label": "low"}, {"label": "medium"}, {"label": "high"}],
        },
    ],
)
 
print(decision.answers)
decisions.sh
curl -X POST "https://ai-gateway.vercel.sh/v1/decisions" \
  -H "Authorization: Bearer $AI_GATEWAY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-6-luna-decisions",
    "input": "The package arrived with a broken screen. I want my money back.",
    "questions": [
      {
        "type": "predicate",
        "name": "damaged",
        "instructions": "Does the customer report a damaged item?"
      },
      {
        "type": "choice",
        "name": "queue",
        "instructions": "Which team should handle this ticket?",
        "choices": [
          { "value": "billing", "description": "Refunds and payments" },
          { "value": "shipping", "description": "Delivery and damaged items" }
        ]
      },
      {
        "type": "score",
        "name": "urgency",
        "instructions": "How urgent is this ticket?",
        "levels": [{ "label": "low" }, { "label": "medium" }, { "label": "high" }]
      }
    ]
  }'

Answers come back in question order. The response also reports the model that answered, token usage, and AI Gateway routing and cost metadata:

{
  "model": "openai/gpt-6-luna-decisions",
  "answers": [
    { "type": "predicate", "name": "damaged", "probability": 0.95 },
    {
      "type": "choice",
      "name": "queue",
      "choice": "shipping",
      "confidence": 0.81,
      "probabilities": [
        { "value": "billing", "probability": 0.19 },
        { "value": "shipping", "probability": 0.81 }
      ]
    },
    {
      "type": "score",
      "name": "urgency",
      "score": 1.3,
      "confidence": 0.62,
      "probabilities": [
        { "value": 0, "label": "low", "probability": 0.08 },
        { "value": 1, "label": "medium", "probability": 0.54 },
        { "value": 2, "label": "high", "probability": 0.38 }
      ]
    }
  ],
  "usage": { "input_tokens": 96, "output_tokens": 0, "total_tokens": 96 },
  "provider_metadata": {
    "gateway": {
      "routing": {
        "originalModelId": "openai/gpt-6-luna-decisions",
        "resolvedProvider": "openai",
        "canonicalSlug": "openai/gpt-6-luna-decisions",
        "finalProvider": "openai"
      },
      "cost": "0.0000096",
      "generationId": "gen_..."
    }
  }
}

A score is the probability-weighted average of the level indexes, starting at 0. confidence and probabilities appear when the answering model returns them.

Set model to any decision model slug, such as openai/gpt-6-luna-decisions or typesafe-ai/jev. Use the Decision filter on the AI Gateway Models page to see them all.

input accepts a string, or an array of user messages. Each message's content is a string or an ordered list of input_text and input_image parts, and every question is asked against the whole input.

Images must be base64-encoded data URLs. External URLs and file IDs aren't supported, and a request can include at most 128 images. Set detail to low, high, auto, or original to choose an image's detail level:

{
  "model": "openai/gpt-6-luna-decisions",
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_text", "text": "Inspect the product in this photo." },
        {
          "type": "input_image",
          "image_url": "data:image/png;base64,iVBORw0KGgo...",
          "detail": "high"
        }
      ]
    }
  ],
  "questions": [
    {
      "type": "predicate",
      "name": "visible_damage",
      "instructions": "Does the product have visible damage?"
    }
  ]
}

Image input needs a decision model that accepts images, such as openai/gpt-6-luna-decisions. Models that only accept text, such as typesafe-ai/jev, return a 400 error for requests with images. If a request with images falls back to a language model, that model must support vision. See decision fallbacks.

Each question needs a type and instructions. name is optional, and an unnamed question comes back with name: null.

TypeRequest fieldsAnswer fields
predicateinstructionsprobability that the answer is yes
choiceinstructions, between 1 and 255 choices with a value and optional descriptionchoice, confidence, probabilities
scoreinstructions, between 2 and 10 levels with a label and optional descriptionscore, confidence, probabilities

If the model declines to answer a question, its answer is { "type": "refusal", "name": ... } in that question's position, and the other questions are answered normally. A request with refused questions is billed for its input like any other.

Question names must be unique within a request. Choice values can be strings or booleans, but a string and a boolean with the same text (such as true and "true") can't both appear in one question.

safety_identifier maps to providerOptions.gateway.safetyIdentifier, the same as on Chat Completions. It must be non-empty, and values longer than 64 characters are truncated. See Safety identifiers for which providers receive it and how precedence works.

The request body also accepts AI Gateway providerOptions, so you can require zero data retention, restrict providers, or add decision fallbacks:

{
  "model": "openai/gpt-6-luna-decisions",
  "input": "...",
  "questions": [{ "type": "predicate", "instructions": "..." }],
  "providerOptions": {
    "gateway": { "zeroDataRetention": true, "models": ["typesafe-ai/jev"] }
  }
}

With the OpenAI SDKs, providerOptions isn't part of the typed request. In Python, pass it through extra_body. In TypeScript, add it to the request object with a // @ts-expect-error comment on that line, and the SDK sends it in the body.

Errors raised while validating the request use the OpenAI error format, and set param to the field that failed:

{
  "error": {
    "message": "Images must be base64-encoded image data URLs. External URLs and file IDs are not supported.",
    "type": "invalid_request_error",
    "param": "input[0].content[1].image_url",
    "code": "invalid_request_error"
  }
}

Errors from routing, billing, or the model provider use AI Gateway's error format. They have the same message and type fields, and param can be an object with details such as the model ID.

Last updated October 8, 2026

Was this helpful?

supported.