Evalgent
Back to Blog
Voice AI Evaluation

OpenAI Decisions API vs Jev for Voice Agents: Pricing, Probabilities, Voice

Deepesh Jayal
Updated
22 min read
OpenAI Decisions API vs Jev for Voice Agents: Pricing, Probabilities, Voice
On this page

Update (October 7, 2026): OpenAI opened the Decisions API to all developers as a public beta on October 6 and published the guide, API reference, pricing and a voice guide. This version replaces the October 4 "not published" items with the documented schema, real pricing, data controls and a working adapter. Still unpublished: p50/p95 latency, rate limits, maximum choices or levels, and a GA date.

Last verified October 7, 2026, against the Decisions guide, the API reference and Connect voice to Decisions.

Every voice agent makes small, bounded decisions on almost every turn. Which flow does this caller need? Is it safe to run the refund tool? Should a human take over now? What disposition code goes on this call? A general LLM can answer all of these, but it answers slowly and in prose you then have to parse.

Two products now target exactly that slice. TypeSafe launched Jev, a purpose-built decision model, on September 15, 2026. OpenAI announced the Decisions API, powered by GPT-6 Luna, at DevDay on September 29, 2026, and opened it as a public beta on October 6.

If you run a voice agent in-house on LiveKit, Pipecat or Dograh, you now have two documented, priced options with nearly the same answer types. This guide is written from the position of an independent evaluator. Evalgent does not resell either product. The goal is to separate what is confirmed from what is marketing, run the numbers inside a real voice-turn budget, and give you a way to choose that holds up after GA.

What is confirmed, what is a vendor claim, and what is still unknown

"Confirmed" means a primary source states it as fact. "Vendor claim" means the vendor said it but conditions are not public or no third party has reproduced it. "Not published" means nobody can check it from public material yet.

ClaimStatusSourceDate checked
Public beta for all developers; GA expected "in the coming weeks"ConfirmedDecisions guideOct 7, 2026
Endpoint `POST /v1/decisions`; `gpt-6-luna` is the only modelConfirmedDecisions guideOct 7, 2026
Python `client.decisions.create(...)`; SDK minimums Python 3.26.0, JS 7.30.0, Go 3.73.0ConfirmedDecisions guideOct 7, 2026
Question types `predicate`, `choice`, `score`; any answer may be a `refusal`ConfirmedAPI referenceOct 7, 2026
Choice and score answers include per-option `probabilities` and a separate `confidence`ConfirmedDecisions guideOct 7, 2026
Several independent questions per request; dependent questions need separate requestsConfirmedDecisions guideOct 7, 2026
Input: text, or user messages with `input_text` and inline base64 `input_image`; max 128 images; no audioConfirmedAPI referenceOct 7, 2026
Price $0.10 / 1M input tokens; no output, cache-read or cache-write chargesConfirmedDecisions guideOct 7, 2026
Regional processing premiums and long-context multipliers applyConfirmedDecisions guideOct 7, 2026
ZDR and HIPAA for eligible customers; US and Europe (EEA + Switzerland) residencyConfirmedDecisions guideOct 7, 2026
Official voice pattern with GPT-Live client delegationConfirmedConnect voice to DecisionsOct 7, 2026
"About 10x faster than the Responses API"Vendor claimDecisions guideOct 7, 2026
150 ms vs 1.6 s for the GPT-6 Luna APIVendor claimDevDay slide via The DecoderOct 7, 2026
p50 / p95 latency, rate limits, max choices or levels, GA dateNot publishedNo figure on the guide or referenceOct 7, 2026
Jev endpoint POST api.typesafe.ai/v1/systemone, model `jev-latest`ConfirmedTypeSafe quickstartOct 4, 2026
Jev returns choice, confidence and per-option probabilitiesConfirmedTypeSafe quickstartOct 4, 2026
Jev $0.042 / 1M input tokens, output free; text onlyConfirmedOpenRouter Jev 1.13; TypeSafe docsOct 4, 2026
Jev context 32K on OpenRouter, 64K per TypeSafe docsConfirmed (two figures)OpenRouter; TypeSafe docsOct 4, 2026
Jev latency 70 to 500 msVendor claimTypeSafeOct 4, 2026
Jev p50 0.21 s, p95 0.34 s, p99 0.75 sThird-party telemetryOpenRouter, as of Oct 1Oct 4, 2026
Jev 100% agreement with human labels on 500 decisionsThird-party study, single taskLangChainOct 4, 2026

Read the table this way. Both products now have a public contract you can build, parse and price against. The one gap that matters most for voice is still on OpenAI's side: no published tail latency and no published rate limits.

There is also a naming trap. OpenRouter runs its own "Decisions" endpoint (POST openrouter.ai/api/alpha/decisions) that serves Jev. If a teammate says "we're on the Decisions API," ask which one.

Architecture: constrained generative model vs purpose-built decision model

The answer formats have converged. The models underneath have not, and that still drives cost structure, failure modes and how you should test.

Side-by-side diagram comparing OpenAI's Decisions API on a constrained GPT-6 Luna model and Jev's purpose-built decision model, both returning a choice, confidence and per-option probabilities

What OpenAI documents, and what it does not

OpenAI describes the Decisions API as focusing GPT-6 Luna on questions with finite predefined answers. The docs now specify the output in full. A `predicate` returns the probability a condition is true. A `choice` returns the selected `choice`, a `probabilities` array of `{value, probability}` for every option and a separate `confidence`. A `score` returns `score`, the probability-weighted average of level indices, plus `confidence` and per-level probabilities. Any question can come back as `{"type": "refusal", "name": ...}`.

What OpenAI has not published is the mechanism: how a generative model is constrained to produce option-level distributions, and how `confidence` is computed from or alongside them. That leaves two things to test rather than assume:

1. Format constraints can change answers. Tam et al. (2024), "Let Me Speak Freely?" found that strict format restrictions on LLMs caused a significant decline in reasoning performance, with stricter constraints causing larger drops. A dedicated endpoint may avoid this. Your labeled set will tell you.

2. Reported probabilities are not automatically calibrated. The docs tell you to set thresholds from labeled examples based on false-positive and false-negative cost. That is the right advice, and it applies to Jev too.

Because output is free, the October 4 concern about billed hidden reasoning tokens no longer applies. You pay only for input.

How Jev decides

Jev is a System One model. You send `state` (a string, JSON object or array) and a set of `questions`. Each is a Choice, a Score or a Noul. TypeSafe says Jev is trained with RLCD (Reinforcement Learning for Calibrated Decisions). It does not write prose, and output is free.

The types map nearly one to one

ConceptOpenAI Decisions APIJev
Is a condition true?`predicate` -> `probability`Noul -> `noul` probability
Pick one option`choice` with `choices: [{value, description}]` -> `choice`, `confidence`, `probabilities` arrayChoice with `criteria` -> `choice`, `confidence`, `probabilities` map
Ordered scale`score` with `levels: [{label, description}]`, low to high -> probability-weighted indexScore with ordered criteria array, 0-indexed -> probability-weighted position
Option value typesString or booleanNamed option keys
Several questions per callYes, independent questions onlyYes, mixable types
Declines to answer`refusal` answer typeNot among the documented answer types
InputString, or user messages with text and base64 imagesString, JSON object or array; text only
Max optionsNot publishedUp to 255
Price$0.10 / 1M input, output free$0.042 / 1M input, output free

Two practical differences fall out. Answers come back as an array in question order, each carrying its `name`, so look them up by name rather than index. And `choices[].value` can be a boolean, which makes a two-way choice usable as a typed yes/no with both probabilities.

The known failure mode on the Jev side still applies everywhere. TypeSafe's jaggedness page warns accuracy falls as irrelevant state grows. Send the last few turns plus the structured fields the decision needs, not the whole call.

Latency inside a voice turn

On a phone call, the number that matters is how much a decision adds to the gap between the caller finishing a sentence and hearing the agent start.

Stivers et al. (2009, PNAS) measured turn transitions across ten languages and found gaps cluster around 200 ms. Voice agents cannot hit that, but the further you drift past about 800 ms, the more callers talk over the agent or ask "hello?"

A worked turn budget

Take an illustrative cascaded pipeline. These are assumptions, not measurements:

HopAssumed ms
Endpointing (silence or end-of-turn model)200
STT final transcript80
LLM time to first token300
TTS time to first byte120
Transport and telephony (SIP, RTP, jitter buffer)100
Total, no decision call800

Now add a blocking decision before the LLM runs.

  • Jev at OpenRouter p50 (210 ms): 800 + 210 = 1,010 ms.
  • Jev at OpenRouter p95 (340 ms): 800 + 340 = 1,140 ms.
  • Jev at OpenRouter p99 (750 ms): 800 + 750 = 1,550 ms.
  • Decisions API at the DevDay 150 ms figure: 800 + 150 = 950 ms.

The docs now phrase speed as "about 10x faster than the Responses API." That is a relative claim against OpenAI's own endpoint, not a millisecond figure, and neither it nor the 150 ms slide is labeled p50 or p95. Jev's numbers are a router's round-trip telemetry, which includes OpenRouter's own hop. Neither is your network path. Measure from your own region.

Waterfall chart of an 800 millisecond voice turn showing a decision call added in series at Jev p50, Jev p95 and the OpenAI 150 millisecond claim, compared with running the decision in parallel with STT

Why p95 matters more than p50 on calls

A call is many turns, and the caller remembers the worst pause. If a blocking decision runs on every turn:

P(at least one slow turn) = 1 - (1 - p_tail) ^ turns

For a 10-turn call:

  • At or beyond p95: 1 - 0.95^10 = 40.1%.
  • At or beyond p99: 1 - 0.99^10 = 9.6%.

With Jev's OpenRouter p99 of 0.75 s, roughly one call in ten would hit at least one turn that adds three-quarters of a second, under these assumptions. For the Decisions API there is still no published p95 or p99, and no published rate limits, so you cannot yet size concurrency from docs. That is now the biggest open question for voice use.

Keep decisions off the critical path where you can

1. Run in parallel with STT finalization. Fire the decision on the interim transcript when the end-of-turn signal starts. If the final transcript matches, use the result. If not, re-run.

2. Run in parallel with the LLM. Start the response and the escalation check together. If the check says escalate, cancel the LLM stream before TTS speaks.

3. Move it after the call. Disposition codes, compliance flags and QA scores rarely need to block a turn.

4. Batch independent questions. Both APIs take several questions per request, so route, needs-human and urgency can share one round trip. On OpenAI, a question that depends on another's answer needs its own request, which means a second serial hop.

Always set a hard timeout on a blocking decision. If it returns late, take a safe default and log the miss. A decision model should never leave the caller in silence.

Cost at 50k to 500k calls a month

Both prices are now real. Both products bill only input tokens.

Labeled assumptions

  • Decisions per call: 6 (one route, three tool gates, one escalation check, one post-call disposition).
  • Input tokens per decision: 1,500 (recent turns, a few structured fields, option descriptions).
  • Same token count on both sides. In practice tokenizers differ, so count with each vendor's usage field.
  • Standard processing. OpenAI's regional processing premiums and long-context multipliers would raise its line; the docs do not list those multipliers on the Decisions page.

Input tokens per call = 6 x 1,500 = 9,000.

The arithmetic

Jev per call: 9,000 x $0.042 / 1,000,000 = $0.000378.

Decisions API per call: 9,000 x $0.10 / 1,000,000 = $0.000900.

Calls per monthJevDecisions APIDifference
50,000$18.90$45.00$26.10
150,000$56.70$135.00$78.30
500,000$189.00$450.00$261.00

The ratio is fixed at $0.10 / $0.042, about 2.4x. Batching independent questions into one request cuts both bills, because shared context is sent once instead of once per question.

What the numbers say

Put the decision layer next to the voice minutes. OpenAI's GPT-Live lists at $0.05 per minute. A 4-minute call is $0.20 in speech model cost alone. The decision layer at $0.0004 to $0.0009 per call is under 0.5% of that.

So at ICP3 volumes, the 2.4x ratio is real but the dollars are small. At 500,000 calls a month the gap is about $261. What decides is accuracy on your routes, calibration of the probabilities, p95 inside your turn budget, and whether a new vendor costs you more in security review than $261 a month. The formula, for your own traffic:

monthly cost = calls x decisions_per_call x input_tokens x input_rate / 1,000,000

Images and data controls

Image input: what the docs allow

Image input is the Decisions API's clearest functional advantage. Jev is text-only. The limits matter for voice flows:

  • Inline base64 only. Images must be data URLs. Hosted HTTP or HTTPS URLs and `file_id` inputs are not supported. If a caller uploads a photo to your bucket mid-call, your server fetches and encodes it before the request.
  • At most 128 images per request, combined with `input_text` parts in user messages. A `detail` setting (low, high, auto, original) trades cost against resolution.
  • No audio. Non-user roles, function calls, files and audio are not accepted. On a phone line, Decisions sees STT text, same as Jev.

Where images enter voice workflows:

  • Screen-share or co-browse support. "Is the user on the billing settings page?"
  • Document photo verification. The agent texts a link; the caller uploads an insurance card; decide "legible / not legible / wrong document."
  • Claims and field service. A photo of damage or a meter reading, classified before the agent continues.

If none of these are on your roadmap, images are not a reason to switch. If they are, Decisions removes a vision pre-step you would need with Jev. Image tokens cost more and may be slower, so do not reuse text latency figures for image decisions.

Data controls

The Decisions guide states support for Zero Data Retention and HIPAA use for eligible customers, and data residency and regional processing in the United States and Europe (EEA plus Switzerland). Eligibility requires the agreements described in OpenAI's data controls guide. If your company already has a BAA or ZDR arrangement with OpenAI, this can be the deciding factor for healthcare scheduling or collections lines. Check TypeSafe's current terms directly; this post does not cover them. EU residency may carry a regional processing premium, so rerun the cost table if you need it.

Probabilities, thresholds, refusals and abstention

A voice agent rarely wants just "the answer." It wants "the answer, and how sure." Both products now return a distribution, so the same threshold policy works on either.

The math of abstention

Geifman and El-Yaniv (2017), "Selective Classification for Deep Neural Networks" formalized the trade-off. Given a confidence score, you choose a threshold so the model answers only when confident, trading coverage for risk. On your own labeled data, you sweep the threshold and pick the point where error on answered cases meets your target.

For a voice agent, that becomes a three-band policy:

BandRule (illustrative)Agent action
Acttop probability at least 0.90 and margin over runner-up at least 0.20Route or run the tool
Confirmtop probability 0.60 to 0.90, or margin under 0.20Ask a short confirming question
Escalate or abstaintop probability under 0.60, refusal, or needs-human probability above your floorHand off or fall back

Tune these on your own calls. Set tighter bands for high-cost routes (cancellations, payments). The routing and escalation guide walks through per-route thresholds, and escalation accuracy covers how to measure them.

Use the per-option probabilities for the band, not only `confidence`. On OpenAI, `confidence` is a separate field whose derivation is not documented. On Jev, a Hacker News commenter noted confidence is derived from the probabilities. Either way, the margin between the top two options is the signal you control.

Calibration: is 0.9 really 90%?

Guo et al. (2017), "On Calibration of Modern Neural Networks" showed modern deep networks are often overconfident, and that temperature scaling recovers much of the gap. Bucket predictions by stated probability and check the 0.9 bucket is right about 90% of the time. Expected Calibration Error (ECE) summarizes the gap. With 500 to 1,000 labeled decisions per provider you can plot reliability yourself and fit an isotonic mapping on top.

Refusal vs escape options

OpenAI added something Jev's documented types do not have: a `refusal` answer. Treat it as a third outcome your code must handle, not an exception. Route a refused live decision to the Escalate band and log it.

A refusal is not a substitute for an escape option. A refusal means the model declined. It does not mean "none of these fit." OpenAI's own examples include `"other"` for support routing ("Requests outside these categories") and `noop` for voice actions ("Choose noop if no action fits"), and the guide recommends a fallback option when your categories do not cover every input. Our guidance stays the same for both vendors: always include an escape option, describe it precisely, and track its rate per day. A rising "other" rate is often the first sign callers are asking for something your flows do not cover.

Option order is a real risk for any decision model

A Hacker News commenter (hbrn) reported that Jev's probabilities shift when the answer list is reordered. That is not unique to Jev:

  • Pezeshkpour and Hruschka (2023) found gaps of roughly 13% to 75% across benchmarks when multiple-choice options were reordered, traced to positional bias when the model is torn between its top choices.
  • Zheng et al. (ICLR 2024) showed LLMs carry a prior toward specific option ID tokens and proposed PriDe to estimate and remove it.

GPT-6 Luna is a generative model, so assume the same risk until your own permutation test says otherwise. Fix option order in code, and include a permutation test in your evaluation for both vendors.

OpenAI's official voice pattern: GPT-Live plus Decisions

OpenAI published a dedicated guide, Connect voice to Decisions. It is the clearest reason to evaluate Decisions if you already run GPT-Live. The flow:

1. Open a Live session with client delegation. Create the GPT-Live session with `delegation: { type: "client" }` and tell GPT-Live which actions your app supports. GPT-Live keeps listening and speaking while your app decides.

2. Catch the delegation. On `session.delegation.created`, save `event.delegation.id` and start the Decisions request.

3. Build the input from your own state. Collect transcripts from `session.input_transcript.delta` and `session.output_transcript.delta`, then combine the recent conversation, the last user request, current app state and the available actions into one text input.

4. Ask a choice question with a `noop`. OpenAI's example offers `back`, `reload` and `noop`, with the instruction "Choose noop if no action fits."

5. Run the action in your app. Find the answer by name in `answers`. Skip the action if the request was canceled or no longer fits the current state.

6. Return the result. Send `session.commentary.append` with the `delegation_id` and content to prompt GPT-Live to tell the caller what happened, or `session.thinking.append` to update its context silently. Keep each append within 500 tokens.

The guide also shows a routing pattern. Add a `reason` choice ("Use a reasoning model for analysis, planning, or other requests"). Simple requests map to a direct action. Complex ones go to the Responses API with a reasoning model, under the same delegation ID.

Three voice-specific notes the guide implies but does not spell out:

  • The 500-token append limit shapes your tool output. A raw order lookup or policy document will not fit. Summarize to what GPT-Live needs to say.
  • The `reason` route adds a second, slower hop. GPT-Live keeps the line alive while you wait, but the caller still waits for the answer. Measure how often `reason` fires and its p95 separately.
  • Nothing here requires OpenAI's decision model. The delegation events and appends are GPT-Live features. You can make the same choice with Jev and return the result the same way. That makes this pattern a fair place for a head-to-head test.

Our sibling post on the Decisions API with GPT-Live covers the event flow, cancellation handling and tests in depth.

Voice use-case map: which fits today

Use caseWhen it runsLatency needImagesFit today
Routing and escalationEvery turn or first turnsTight, blockingNoEither; decide on your own p95 and accuracy
Tool-call gatingBefore each tool callTight, blockingNoEither; see tool calling with Jev
GPT-Live action selectionMid-call, delegatedModerate (GPT-Live keeps talking)SometimesDecisions is the documented path; Jev fits the same slot
Disposition codesPost-callLooseRarelyEither; Jev is about 2.4x cheaper per token
Compliance flagsLive or post-callModerateNoEither; `predicate` and Noul both return a probability
Live QA and call scoringPost-callLooseRarelyEither; see scoring every call
Photo or screen checksMid-callModerateYesDecisions API; Jev needs a vision pre-step
Regulated lines needing ZDR or HIPAA via OpenAIAnyAnyAnyDecisions, if you are eligible

On October 4, the blocking decisions leaned toward Jev because only it had documented probabilities. That gap has closed. What separates the two on live turns now is measured tail latency from your servers, and on that OpenAI has published nothing.

For the Jev side, see Jev for voice agents, Jev vs LLMs and call monitoring with Jev.

Where each side is stronger

OpenAI's advantages:

  • Image input, with documented limits.
  • Data controls stated on the product page: ZDR, HIPAA eligibility, US and EU residency.
  • One vendor, one bill, one security review. If your stack already runs on GPT-Live or a GPT-6 model, adding Decisions avoids a new DPA. For a lean security team, that can outweigh a $261-a-month gap.
  • An official voice integration with GPT-Live delegation, plus a `refusal` type and boolean choice values.
  • Five official SDKs.

Jev's advantages:

  • About 2.4x cheaper per input token.
  • Third-party latency telemetry (OpenRouter p50 0.21 s, p95 0.34 s, p99 0.75 s as of October 1), where OpenAI offers only relative claims.
  • Generally available, not beta, with a documented 255-option ceiling and published context size.
  • Available through OpenRouter if you already route models there.

The LangChain study reported 100% agreement with human labels over 500 decisions for Jev, versus 99.8% for GPT-5.6 Terra. That is one task, run by a partner, on non-voice data. No comparable third-party study of the Decisions API exists yet. Treat both as reasons to test.

Decision tree for choosing between Jev and the OpenAI Decisions API based on image input, OpenAI data-control or single-vendor needs, GPT-Live delegation, and price sensitivity, ending in a measured head-to-head

Decision matrix: what to choose now

Both products now return probabilities and confidence, so that is no longer a reason to pick one. Choose on price, images, data controls, ecosystem and the p95 you measure on your own calls.

Your situationChoose nowWhy
Live routing or escalation on a phone line, need to ship this monthEither, behind an adapter; pick on your p95Same answer types; tail latency is the open variable
High volume, text-only, cost-sensitiveJevAbout 2.4x lower input price, GA
Need image decisions (screen-share, document photo)Decisions APIOnly one that takes images directly
Healthcare or regulated line with an OpenAI BAA or ZDRDecisions API, after checking eligibilityDocumented ZDR, HIPAA and residency
Strict single-AI-vendor policy, stack on OpenAIDecisions APIAvoids a new vendor review
Building on GPT-Live client delegationStart with Decisions; benchmark Jev in the same slotOfficial pattern, but the slot is model-agnostic
Need a production SLA before GAJev, or Decisions with a fallbackDecisions is beta with no GA date
High-stakes compliance flags with audit needsEither; log full probability arraysLogged distributions make audits defensible

The principle behind every row: commit to an interface, not a vendor.

A provider-agnostic decision adapter in Python

A thin adapter lets your agent call one function with a provider-neutral question and run both vendors through the same evaluation.

The Jev call follows the TypeSafe quickstart: `TypeSafeClient`, `client.system_one(state=..., questions=...)`, `Choice(instructions=..., criteria=...)`, and `response.answers[name]`. The OpenAI call follows the Decisions guide: `client.decisions.create(model="gpt-6-luna", input=..., questions=[...])`, answers matched by `name`, refusals handled explicitly.

# Simplified, illustrative adapter.
# pip install typesafe-sdk "openai>=3.26.0"
# Clients read TYPESAFE_API_KEY and OPENAI_API_KEY from the environment.
import asyncio
import time
from dataclasses import dataclass
from typing import Optional, Protocol

from openai import OpenAI
from typesafe_sdk import Choice, TypeSafeClient


@dataclass(frozen=True)
class Question:
    name: str
    instructions: str
    options: dict[str, str]  # value -> description; keep order fixed in code


@dataclass
class Decision:
    name: str
    choice: Optional[str]
    probabilities: Optional[dict[str, float]] = None
    confidence: Optional[float] = None
    refused: bool = False
    provider: str = ""
    latency_ms: float = 0.0
    timed_out: bool = False


class DecisionProvider(Protocol):
    name: str

    def decide(self, state: str, questions: list[Question]) -> list[Decision]: ...


class JevProvider:
    name = "jev"

    def __init__(self) -> None:
        self.client = TypeSafeClient()

    def decide(self, state: str, questions: list[Question]) -> list[Decision]:
        start = time.perf_counter()
        response = self.client.system_one(
            state=state,
            questions={
                q.name: Choice(instructions=q.instructions, criteria=q.options)
                for q in questions
            },
        )
        elapsed = (time.perf_counter() - start) * 1000
        out = []
        for q in questions:
            a = response.answers[q.name]
            out.append(Decision(
                name=q.name,
                choice=a.choice,
                probabilities=dict(a.probabilities),
                confidence=a.confidence,
                provider=self.name,
                latency_ms=elapsed,
            ))
        return out


class OpenAIDecisionsProvider:
    name = "openai-decisions"

    def __init__(self) -> None:
        self.client = OpenAI()

    def decide(self, state: str, questions: list[Question]) -> list[Decision]:
        start = time.perf_counter()
        decision = self.client.decisions.create(
            model="gpt-6-luna",
            input=state,
            questions=[
                {
                    "type": "choice",
                    "name": q.name,
                    "instructions": q.instructions,
                    "choices": [
                        {"value": value, "description": desc}
                        for value, desc in q.options.items()
                    ],
                }
                for q in questions  # independent questions only
            ],
        )
        elapsed = (time.perf_counter() - start) * 1000
        by_name = {a.name: a for a in decision.answers}  # match by name, not index
        out = []
        for q in questions:
            a = by_name.get(q.name)
            if a is None or a.type == "refusal":
                out.append(Decision(name=q.name, choice=None, refused=True,
                                    provider=self.name, latency_ms=elapsed))
                continue
            out.append(Decision(
                name=q.name,
                choice=a.choice,
                probabilities={p.value: p.probability for p in a.probabilities},
                confidence=a.confidence,
                provider=self.name,
                latency_ms=elapsed,
            ))
        return out


async def decide_with_budget(
    provider: DecisionProvider,
    state: str,
    questions: list[Question],
    budget_ms: int,
    fallback: dict[str, str],
) -> list[Decision]:
    """Run a blocking decision with a hard timeout so the caller never hears silence."""
    try:
        return await asyncio.wait_for(
            asyncio.to_thread(provider.decide, state, questions),
            timeout=budget_ms / 1000,
        )
    except asyncio.TimeoutError:
        return [
            Decision(name=q.name, choice=fallback[q.name],
                     provider=provider.name, timed_out=True)
            for q in questions
        ]


def policy(d: Decision, act_p: float = 0.90, min_margin: float = 0.20,
           floor_p: float = 0.60) -> str:
    """Three-band policy. Illustrative thresholds; tune on your own labeled calls."""
    if d.refused:
        return "escalate"
    if d.timed_out or not d.probabilities:
        return "confirm"
    ranked = sorted(d.probabilities.values(), reverse=True)
    top = ranked[0]
    margin = top - (ranked[1] if len(ranked) > 1 else 0.0)
    if top >= act_p and margin >= min_margin:
        return "act"
    if top < floor_p:
        return "escalate"
    return "confirm"


ROUTE = Question(
    name="route",
    instructions="Pick the flow that best serves the caller's main need on this turn. "
                 "Choose other if no flow fits.",
    options={
        "billing": "Charges, refunds, invoices or payment methods.",
        "cancel": "Caller wants to end or downgrade a plan.",
        "schedule": "Book, move or cancel an appointment.",
        "support": "Something is broken: device, app or login.",
        "other": "Requests outside these categories.",
    },
)


async def on_user_turn(recent_turns: str, provider: DecisionProvider) -> str:
    [route] = await decide_with_budget(
        provider, recent_turns, [ROUTE], budget_ms=300,
        fallback={"route": "other"},
    )
    action = policy(route)
    # Log provider, choice, probabilities, refused, latency_ms and action every turn.
    return f"{action}:{route.choice}"

Design notes:

  • Option order is fixed in code. The `options` dict preserves insertion order on both providers.
  • Answers are matched by name. OpenAI returns an array in question order; matching by `name` survives future reordering.
  • Refusals and timeouts degrade safely. A refusal escalates; a timeout or missing distribution confirms. Neither auto-acts.
  • Latency is measured at your boundary, which is the number your callers feel.
  • Both calls are synchronous and run in a thread here. In production, use each SDK's async client if available, and keep the state short.

In a LiveKit agent server or a Pipecat pipeline, call `on_user_turn` from wherever you handle the final user transcript, and start it as early as your end-of-turn signal allows.

How to test the OpenAI Decisions API vs Jev on your own calls

Vendor numbers will not tell you which product is right for your calls. A one-week, side-by-side test will. Our post on benchmarking decision APIs for voice agents goes deeper on harness design.

1. Pick three to five decisions you actually make. For example: route, needs-human, refund tool gate and post-call disposition. Write each as a fixed-order option list with an escape option.

2. Build a labeled set from real calls. Pull 300 to 1,000 decision points, stratified by route and including hard cases (mixed intents, angry callers, noisy transcripts). Have two people label each and resolve disagreements. The golden dataset guide covers sampling.

3. Freeze the state window. Decide exactly what each decision sees, for example the last four turns plus account status, and send identical input to both providers.

4. Run both through the adapter. Record choice, full probabilities, confidence, refusals and latency measured from your agent server's region.

5. Measure accuracy and calibration. Report accuracy per decision and per route. Plot a reliability diagram and compute ECE for each provider. Check the Act band's error rate against your target.

6. Run the order test. Re-run 100 items with options in two other fixed orders. Count label changes and how far top probability moves.

7. Count refusals and escape-option use. Report the refusal rate and the "other" rate per provider. A provider that refuses on ordinary caller speech needs a wider Escalate path.

8. Measure latency where it hurts. Report p50, p95 and p99 over at least 1,000 requests per provider at your real concurrency, from your production region. Note any rate-limit errors during the Decisions beta. Plug p95 and p99 into the turn budget and the at-least-one-slow-turn formula.

9. Decide with a written rule. For example: choose the provider with higher accuracy on high-stakes routes, provided its p95 stays under 300 ms and its Act-band error is under 2%; break ties on price and data controls. Write the rule before you look at results.

If you want this done independently, this is the kind of bake-off Evalgent runs: same calls, same inputs, both providers, with accuracy, calibration, refusal rate and tail latency reported against your thresholds. The LLM-as-judge limits post explains why we do not score decision models with another LLM's opinion, and Jev as a judge covers when a decision model can itself be the scorer.

Frequently asked questions

Is the OpenAI Decisions API available to everyone?

Yes. Since October 6, 2026 it has been a public beta for all developers at `POST /v1/decisions`, with `gpt-6-luna` as the only model and a Playground at platform.openai.com/decisions. OpenAI says it expects general availability "in the coming weeks" but has not given a date.

How much does the OpenAI Decisions API cost?

$0.10 per million input tokens with `gpt-6-luna`. There are no output-token, cache-read or cache-write charges. Regional processing premiums and long-context multipliers apply. Under this post's assumptions, that is $450 a month at 500,000 calls, versus $189 for Jev at $0.042.

Does the Decisions API return probabilities or confidence scores?

Yes. Predicates return a probability that the condition is true. Choice and score answers return a probability for every option or level plus a separate confidence field. Any question can also return a refusal. OpenAI advises setting thresholds from your own labeled examples, which is also the right approach for Jev.

Is the Decisions API faster than Jev?

Unknown from public data. OpenAI says "about 10x faster than the Responses API," and a DevDay slide showed 150 ms, but neither is a p50 or p95. Jev has OpenRouter telemetry of 0.21 s p50 and 0.34 s p95 as of October 1. Measure both from your own servers.

Can Jev handle images?

No. Jev input is text only. For document photos or screen-share, run a vision model first, then decide with Jev. The Decisions API accepts images directly, as inline base64 data URLs only, up to 128 per request. Neither accepts audio; both decide on your STT transcript.

How consistent are Jev's answers?

Jev is highly consistent, though not perfectly fixed: the LangChain study found LLM judges' score variance 92 to 913 times higher than Jev's on its task. A Hacker News commenter showed probabilities shift when options are reordered, so fix option order in code and run permutation tests on both vendors.

Which should I use for voice agent escalation today?

Either can work, since both return per-option probabilities for a confirm band. Put both behind an adapter, run them on the same labeled calls, and pick on accuracy and measured p95. Lean to Decisions if you need images or OpenAI's ZDR and HIPAA terms; lean to Jev on price.

Will the cost difference matter at my call volume?

Usually not much. Jev is about 2.4 times cheaper per input token, but under this post's assumptions the gap is about $261 a month at 500,000 calls. Both are under 0.5% of a GPT-Live call's speech cost. Accuracy, p95 latency and procurement weigh more.

The bottom line

With the October 6 public beta, the OpenAI Decisions API and Jev now offer nearly the same typed answers with probabilities, so the choice comes down to a 2.4x input price gap, image support, data controls, ecosystem fit and tail latency that only Jev has third-party numbers for. Build behind a provider-agnostic adapter, run both on your own labeled calls, and let accuracy, calibration and p95 inside your turn budget decide.

Related Articles