Back to Blog
Engineering12 October 20266 min read · 1,399 words

OpenAI Decisions API on Vercel AI Gateway: Setup and Limits

N7

No7 Engineering Team

Growth Architecture Unit

Engineering: OpenAI Decisions API on Vercel AI Gateway: Setup and Limits (illustration)

Since 7 October 2026, OpenAI's Decisions API has been available through Vercel AI Gateway at /v1/decisions, according to Vercel's changelog entry. The OpenAI Decisions API is OpenAI's interface for asking a model typed questions about a shared input. It returns structured answers (a probability, a selected option or a rubric score) instead of generated text. On AI Gateway it keeps OpenAI's request and response format, so an existing OpenAI SDK integration switches by changing the base URL, credentials and model ID.

What Vercel added to AI Gateway

OpenAI defines the API; Vercel adds the routing. The gateway endpoint answers with any decision model it hosts, not only OpenAI's. The models include openai/gpt-6-luna-decisions for OpenAI's GPT-6 Luna Decisions model, plus Jev, Liquid d1 and Laya from other providers. GPT-6 Luna Decisions is a separate model from openai/gpt-6-luna, which generates text.

Requests through the gateway also accept AI Gateway providerOptions, so you can require zero data retention, restrict which providers serve the call, or add a decision fallback. Per the AI Gateway decision documentation, decisions are not available through Chat Completions, Responses, or the Anthropic-compatible and Cohere-compatible endpoints.

Which question types does the OpenAI Decisions API take on the gateway?

The endpoint takes three question types: a yes/no predicate, a choice and a graded score. Each question needs a type and instructions, and the answers come back in question order. A choice question also suits tagging a shopper's query by intent before it reaches AI search, rather than asking a chat model to label it in prose.

TypeWhat you sendWhat you get back
predicateInstructionsThe probability that the answer is yes
choiceInstructions and 1 to 255 choices, each with a value and an optional descriptionThe choice, a confidence and the probabilities
scoreInstructions and 2 to 10 levels, each with a label and an optional descriptionThe score, a confidence and the probabilities

A question name is optional, and an unnamed question comes back with name: null, but names must be unique within a request. Choice values can be strings or booleans; a string and a boolean with the same text, such as true and "true", can't both appear in one question. If the model declines a question, that answer comes back as a refusal in its position and the others are answered normally. The request is still billed for its input.

A worked example: triaging a store's support tickets

Take a store whose helpdesk receives a ticket saying a package arrived with a broken screen and the customer wants a refund. The OpenAI-compatible Decisions API documentation uses this ticket to show three questions answered in one call: whether the customer reports a damaged item, which queue should take it, and how urgent it is.

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' }] },
  ],
});

The response lists the answers in question order, along with the model that answered, token usage and gateway routing and cost metadata. Trimmed to the model, answers and usage, the documented example looks like this:

{
  "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 }
}

The routing code reads three typed fields: probability on the predicate, choice on the queue question and score on the urgency question. A score is the probability-weighted average of the level indexes, starting at 0, so 1.3 sits between medium and high. The confidence and probabilities fields appear only when the answering model returns them, so treat them as optional.

One way to wire it: the helpdesk posts each new ticket to a small serverless function that makes this call and writes the answers back as ticket tags. Nothing in that path parses a text completion. The same shape works inside an agent: before an agent built on ACP, MCP or UCP completes a checkout, a predicate can ask whether the request needs a human first.

SDK versions you need

Decision calls need a recent client, or a plain HTTP request to the gateway.

ClientMinimum versionCall
OpenAI SDK for JavaScript7.30.0decisions.create
OpenAI SDK for Python3.26.0decisions.create
AI SDK (ai)7.0.128experimental_decide (or experimental_evaluate on earlier AI SDK 7 releases)
HTTPn/aPOST /v1/decisions or POST /v1/evaluate

The AI SDK names its yes/no type boolean and keys questions by name in an object, while the OpenAI-compatible endpoint uses predicate and an ordered array. Pick one shape per codebase rather than mixing both, so the routing code reads one format.

Limits and behaviour to plan for

Before you ship decision calls

  • Authentication: use an AI Gateway API key or a Vercel OIDC token. If an API key is set it takes precedence over the OIDC token, even when the key is invalid, so a stale key in CI breaks OIDC.
  • Images: input can include base64 data URLs, up to 128 images per request; external URLs and file IDs are not supported. Text-only models such as typesafe-ai/jev return a 400 error for requests with images.
  • Fallbacks: decision fallbacks are opt-in. They rerun a successful decision with another model when a choice or score has low confidence, a yes/no probability falls inside an uncertain band, or a compound condition matches. Only one fallback stage is supported. When it triggers, both stages are billed and run one after the other, so the request carries both calls' latency. The condition format and complete requests are in Vercel's decision fallbacks documentation.
  • Billing: decision requests are billed from the model's per-token rates, and some decision models price input tokens only. Requests work with Bring Your Own Key.
  • Safety identifier: safety_identifier maps to providerOptions.gateway.safetyIdentifier; it must be non-empty, and values over 64 characters are truncated.
  • Typed SDKs: providerOptions isn't part of the OpenAI SDKs' typed request; pass it through extra_body in Python, or with a // @ts-expect-error comment in TypeScript.

Validation errors use the OpenAI error format and set param to the field that failed. Errors from routing, billing or the model provider use AI Gateway's format, where param can carry details such as the model ID. Log both shapes, because a retry policy that only reads one can misclassify the other.

What should you do next?

Decision models were previously called evaluation models, and nothing breaks if you leave existing calls alone. experimental_evaluate and gateway.evaluationModel() still work as deprecated aliases with the same request and response shapes, and /v1/evaluate still answers. Move to /v1/decisions when you want the OpenAI SDK surface, and do it one decision at a time.

  1. Find one decision you make with a prompt and a parser. Ticket routing, review moderation and a refund rubric are typical candidates.
  2. Upgrade the client to the minimum version in the table above.
  3. Point it at the gateway: base URL https://ai-gateway.vercel.sh/v1, your AI Gateway key and a decision model ID.
  4. Check CI for stale API keys before relying on OIDC.
  5. Add a fallback only where an uncertain answer needs a second model, and budget for the second billed stage and its latency.

If the ticket tags should trigger in-store actions, our Shopify Flow AI automation patterns cover that step. If the decision feeds an agent, the Shopify MCP server guide covers the commerce tools that agent calls next. When you want the routing built and tested against your own tickets and catalogue, our ecommerce development team can scope it.

Frequently Asked Questions

The questions buyers and engineers ask us most about this topic.

What does the OpenAI Decisions API return?

Structured answers to typed questions about a shared input: the probability that a predicate is true, a selected choice with a confidence and probabilities, or a rubric score with a confidence and probabilities. It returns these instead of generated text, so routing code reads typed fields rather than parsing a completion.

Do I have to migrate existing AI Gateway evaluation calls?

No. The old evaluation names still work as deprecated aliases with the same request and response shapes, and /v1/evaluate still answers. Switch when you want to call decision models through the OpenAI SDK.

Can non-OpenAI models answer on the /v1/decisions endpoint?

Yes. On Vercel AI Gateway the endpoint works with any decision model the gateway hosts, including Jev, Liquid d1 and Laya, as well as openai/gpt-6-luna-decisions. Text-only models such as typesafe-ai/jev return a 400 error if the request includes images.