Decisions API in Azure OpenAI: A C# Walkthrough

Decisions API in Azure OpenAI - A CSharp Walkthrough

Recently I wrote about Microsoft-Decision-1, a model that scores a fixed set of options instead of writing text. That post used a Microsoft provider route. This week the Azure OpenAI Decisions API went generally available in Microsoft Foundry, and it solves the same problem through the Azure OpenAI resource you may already have.

Every agent makes small judgments: which team owns this ticket, does the customer want a refund, how severe is this issue, should the workflow continue or stop. Those steps still get sent to a chat model, which means you ask for JSON, parse text that almost looks like JSON, and trust a confidence number the model never really returned.

The Decisions API is built for those judgments. It evaluates text or images and returns a typed answer: a probability, a choice from your list, or a score against your rubric.

Sources for the numbers and endpoint details below: the Foundry announcement (October 22, 2026) and the Decisions API how-to. The official examples are in Python, JavaScript, and REST. This post gives you the same REST call in C#.


What the API returns

The announcement describes three outcomes, one per question type:

  • Probability: whether a condition is true, with a value from 0 to 1
  • Choice: one value from a list you define
  • Score: a position on an ordered rubric

You send one shared input and a questions array. Each question has a unique name, so you can match each answer back to its question. You get back an answers array in the same order, plus token usage.

The endpoint is:

The model field takes your deployment name. The docs use gpt-6-luna, which is currently the only supported model. It works with either an API key in the api-key header or a Microsoft Entra ID bearer token.

Two things to remember from the docs:

  • A refusal is a separate answer type. It is not a low probability, so handle it as its own case.
  • Each request supports up to 400 questions. Questions in one request all see the same input.

How this differs from Microsoft-Decision-1

Microsoft-Decision-1 (previous post) Azure OpenAI Decisions API (this post)
Endpoint {resource}/providers/microsoft/v1/systemone {resource}.openai.azure.com/openai/v1/decisions
Model field Your Decision-1 deployment Your gpt-6-luna deployment
Question types noul, choice, score predicate, choice, score
Auth Entra ID API key or Entra ID
SDK examples None in the quickstart, C# calls HttpClient Python, JavaScript, REST

Both return typed answers. The sample in this post uses the Azure OpenAI route, so the code and the JSON match the official docs.


Calling it from C#

The official docs don’t show a C# SDK for this endpoint, so the sample calls it with HttpClient. It uses the same pattern as the Decision-1 demo: Azure.Identity for Entra ID, with an API key as a shortcut when one is set.

The complete project is on GitHub at taswar/OpenAI-DecisionApi-CSharp-Sample. Clone it if you want to run the code without copying it from this post.

Create the project on .NET 10, then add the packages. The packages let the app read settings from user secrets, so the endpoint and key stay out of your shell and out of the code:

dotnet user-secrets init adds a UserSecretsId to the project file. Keep it as it is.

Store your settings with user secrets from the project folder. Use the base URL only, with no path:

The deployment name is optional, and the app defaults to gpt-6-luna. If you leave out AZURE_OPENAI_API_KEY, the app uses DefaultAzureCredential instead, so az login works.

To check what the app will read, run:

The project file is OpenAI-DecisionApi-CSharp-Sample.csproj. The UserSecretsId value is the one dotnet user-secrets init writes in your copy:

Then Program.cs. The ticket and the three questions are the same ones the official Python example uses, so the output can be compared directly:

Run it:

The output should look like this. Your numbers will differ:


What changed in the code

The first thing to notice is that all three questions go out in one request. The ticket is evaluated once, and the answers come back together. That matters when a workflow makes many small decisions.

The score answer is a weighted average of the level positions. A score of 0.98 sits close to “Workaround available” (position 1), but the confidence is only 13%. That low confidence is the signal to act on. Send this ticket to a person rather than trust the score. Use the score for thresholds and ordering, not as an exact label.

The refusal branch is there on purpose. The docs are clear that a refusal is different from a predicate answer with a low probability, so the code gives it its own case.

I kept the code as one file so it reads top to bottom. In a real service you would move the request into its own class and add a retry policy.


Where it fits

Use it when the next step is a closed choice:

  • Route a support ticket before a person or a bigger model sees it
  • Decide whether an agent should continue, retry, or hand off
  • Check a draft against a rubric before it goes out
  • Gate a tool call, such as one that touches production data

Keep a chat model for anything that needs words, such as summaries, rewrites, or explanations. The decision model decides, and the chat model writes.

Set your own thresholds with a labeled set of tickets from your application. The announcement and the docs both describe these probabilities as estimates, so do not treat 0.95 as a guarantee.


Final thoughts

The Decisions API is the same idea as Microsoft-Decision-1, but it sits on the Azure OpenAI resource with gpt-6-luna and the three question types from the docs. If your team already has an Azure OpenAI resource, this is the easier starting point.

Try it against your own tickets before you change any routing. If your team already has a classifier, put this next to it and compare the answers on the same data. 🙂


Resources

Taswar Bhatti Avatar

Written by