> For the complete documentation index, see [llms.txt](https://docs.nexos.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.nexos.ai/gateway-api/code-examples/.net-sdk.md).

# .NET SDK

Runnable versions of every example on this page live in [nexos-ai/dotnet-examples](https://github.com/nexos-ai/dotnet-examples).

## Setup

Install the packages:

```bash
dotnet add package Microsoft.Extensions.AI
dotnet add package Microsoft.Extensions.AI.OpenAI
dotnet add package DotNetEnv
```

All examples read the API key from a `.env` file:

```
API_KEY=YOUR_NEXOS_API_KEY
```

OpenAI-compatible clients use the base URL `https://api.nexos.ai/v1`; the Anthropic Messages API takes the bare `https://api.nexos.ai` because the SDK appends `/v1/messages` itself.

Model IDs on nexos.ai are the model names shown in the console (e.g. `GPT 5.5`, `Claude Sonnet 5`, `Whisper`). List them with `GET /v1/models`.

The examples below use `Microsoft.Extensions.AI` — the provider-neutral abstraction layer — over an `OpenAIClient` pointed at the gateway. Where a capability has no `Microsoft.Extensions.AI` surface, the raw `OpenAI` SDK client is used directly.

## Chat Completion

Send a conversation and get the model's next reply — the standard OpenAI-compatible endpoint, supported by most chat models on the platform.

```csharp
using DotNetEnv;
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;

Env.TraversePath().Load();
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;

var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.nexos.ai/v1") };

IChatClient chatClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetChatClient("GPT 5.5")
    .AsIChatClient();

var response = await chatClient.GetResponseAsync(
    "how many letters 'r' in the word 'strawberry'");

Console.WriteLine(response.Text);
```

## Chat Completion with history

Keep the full message list between turns so the model can resolve references to earlier context.

```csharp
List<ChatMessage> messages =
[
    new(ChatRole.System, "You are a meticulous linguist who counts letters in words."),
    new(ChatRole.User, "how many letters 'r' in the word 'strawberry'"),
];

var first = await chatClient.GetResponseAsync(messages);
Console.WriteLine(first.Text);

messages.AddMessages(first);
messages.Add(new ChatMessage(ChatRole.User, "and how many 'b'?"));

var second = await chatClient.GetResponseAsync(messages);
Console.WriteLine(second.Text);
```

## Streaming

Receive the reply incrementally as server-sent events instead of waiting for the whole message — useful for chat UIs and long outputs.

```csharp
await foreach (var update in chatClient.GetStreamingResponseAsync(
    "how many letters 'r' in the word 'strawberry'"))
{
    Console.Write(update.Text);
}

Console.WriteLine();
```

## Reasoning

Let the model reason internally before answering — the response carries reasoning content alongside the final text. Reasoning effort is an experimental surface in the OpenAI SDK, so the `OPENAI001` diagnostic is suppressed.

```csharp
#pragma warning disable OPENAI001

using Microsoft.Extensions.AI;
using OpenAI.Chat;

var chatOptions = new ChatOptions
{
    RawRepresentationFactory = _ => new ChatCompletionOptions
    {
        ReasoningEffortLevel = ChatReasoningEffortLevel.Medium,
    },
};

var response = await chatClient.GetResponseAsync(
    "how many letters 'r' in the word 'strawberry'",
    chatOptions);

foreach (var content in response.Messages.SelectMany(m => m.Contents))
{
    if (content is TextReasoningContent reasoning)
    {
        Console.WriteLine($"[thinking] {reasoning.Text}");
    }
    else if (content is TextContent text)
    {
        Console.WriteLine(text.Text);
    }
}

#pragma warning restore OPENAI001
```

## Function calling

Let the model call functions you define. With `FunctionInvokingChatClient` in the pipeline, the round trip is automatic: the model requests the tool, the client runs your delegate, sends the result back, and returns the final answer.

```csharp
using Microsoft.Extensions.AI;

[Description("Count occurrences of a letter in a word")]
static int CountLetters(string word, string letter) =>
    word.Count(c => c.ToString().Equals(letter, StringComparison.OrdinalIgnoreCase));

IChatClient chatClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetChatClient("GPT 5.5")
    .AsIChatClient()
    .AsBuilder()
    .UseFunctionInvocation()
    .Build();

var chatOptions = new ChatOptions
{
    Tools = [AIFunctionFactory.Create(CountLetters)],
};

var response = await chatClient.GetResponseAsync(
    "Use the count_letters tool to count letters 'r' in the word 'strawberry'",
    chatOptions);

Console.WriteLine(response.Text);
```

## Caching

Wrap the client in `DistributedCachingChatClient` to serve identical requests from a local cache instead of calling the gateway again — the second call below returns without a network round trip.

```csharp
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

IDistributedCache cache = new MemoryDistributedCache(
    Options.Create(new MemoryDistributedCacheOptions()));

IChatClient chatClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetChatClient("GPT 5.5")
    .AsIChatClient()
    .AsBuilder()
    .UseDistributedCache(cache)
    .Build();

const string prompt = "how many letters 'r' in the word 'strawberry'";

var first = await chatClient.GetResponseAsync(prompt);   // hits the gateway
var second = await chatClient.GetResponseAsync(prompt);  // served from cache

Console.WriteLine(first.Text);
Console.WriteLine(second.Text);
```

## Structured output

Ask for the answer as a typed .NET object. The client derives a JSON schema from the record, sends it as the response format, and deserializes the reply.

```csharp
using Microsoft.Extensions.AI;

record LetterCount(string Word, string Letter, int Count);

var response = await chatClient.GetResponseAsync<LetterCount>(
    "how many letters 'r' in the word 'strawberry'");

var result = response.Result;
Console.WriteLine($"{result.Letter} appears {result.Count} times in {result.Word}");
```

## Embedding

Convert text into a numeric vector for semantic search, clustering, and RAG. One vector is returned per input.

```csharp
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;

var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.nexos.ai/v1") };

IEmbeddingGenerator<string, Embedding<float>> generator =
    new OpenAIClient(new ApiKeyCredential(apiKey), options)
        .GetEmbeddingClient("Text Embedding 3 Large")
        .AsIEmbeddingGenerator();

var embedding = await generator.GenerateVectorAsync(
    "There are three letters 'r' in the word 'strawberry'.");

Console.WriteLine($"dimensions: {embedding.Length}");
Console.WriteLine($"first values: {string.Join(", ", embedding.ToArray().Take(5))}");
```

## Image Generation

Generate an image from a text prompt. `IImageGenerator` is experimental in `Microsoft.Extensions.AI.Abstractions`, so the `MEAI001` diagnostic is suppressed.

```csharp
#pragma warning disable MEAI001

using Microsoft.Extensions.AI;

IImageGenerator imageGenerator = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetImageClient("GPT Image 2")
    .AsIImageGenerator();

var response = await imageGenerator.GenerateImagesAsync(
    "three letters 'r' with strawberry texture");

foreach (var content in response.Contents)
{
    if (content is UriContent uri)
    {
        Console.WriteLine(uri.Uri);
    }
    else if (content is DataContent data)
    {
        await File.WriteAllBytesAsync("generated.png", data.Data.ToArray());
        Console.WriteLine("saved to generated.png");
    }
}

#pragma warning restore MEAI001
```

## Audio Generation

Convert text to spoken audio (text-to-speech).

```csharp
using OpenAI;
using OpenAI.Audio;
using System.ClientModel;

var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.nexos.ai/v1") };
var audioClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetAudioClient("tts-1");

BinaryData speech = await audioClient.GenerateSpeechAsync(
    "There are three letters 'r' in the word 'strawberry'.",
    GeneratedSpeechVoice.Alloy);

await File.WriteAllBytesAsync("generated.mp3", speech.ToArray());
```

## Audio Transcription

Transcribe an audio file to text in its original language. The example reads `sound.mp3` — you can create one with the Audio Generation example above.

```csharp
using OpenAI.Audio;

var audioClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetAudioClient("Whisper");

var transcription = await audioClient.TranscribeAudioAsync("sound.mp3");

Console.WriteLine(transcription.Value.Text);
```

## Audio Translation

Transcribe an audio file and translate the text into English. The example reads `sound.mp3` — you can create one with the Audio Generation example above.

```csharp
using OpenAI.Audio;

var audioClient = new OpenAIClient(new ApiKeyCredential(apiKey), options)
    .GetAudioClient("Whisper");

var translation = await audioClient.TranslateAudioAsync("sound.mp3");

Console.WriteLine(translation.Value.Text);
```

## Dependency injection

In ASP.NET Core, register `IChatClient` once and inject it wherever it's needed. The key comes from configuration — user secrets, an environment variable, or `appsettings.Development.json`.

```csharp
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddChatClient(sp =>
{
    var apiKey = sp.GetRequiredService<IConfiguration>()["API_KEY"]!;
    var options = new OpenAIClientOptions { Endpoint = new Uri("https://api.nexos.ai/v1") };

    return new OpenAIClient(new ApiKeyCredential(apiKey), options)
        .GetChatClient("GPT 5.5")
        .AsIChatClient();
})
.UseFunctionInvocation()
.UseLogging();

var app = builder.Build();

app.MapPost("/chat", async (ChatRequest request, IChatClient chatClient) =>
{
    var response = await chatClient.GetResponseAsync(request.Message);
    return Results.Ok(new { reply = response.Text });
});

app.Run();

record ChatRequest(string Message);
```

Set the key and call the endpoint:

```bash
dotnet user-secrets set API_KEY "YOUR_NEXOS_API_KEY"
dotnet run

curl -X POST http://localhost:5000/chat \
  -H "Content-Type: application/json" \
  -d '{"message": "how many letters r in the word strawberry"}'
```

## Anthropic Messages API

The `/v1/messages` endpoint implements the Anthropic Messages API — including `cache_control` prompt caching, thinking blocks, and `tool_use` — and is available for models that list the messages endpoint in `GET /v1/models` (e.g. Claude models).

There is no Microsoft-owned provider for it. Use the community [`Anthropic.SDK`](https://www.nuget.org/packages/Anthropic.SDK) package against the bare base URL `https://api.nexos.ai`, or call the endpoint with a plain `HttpClient`.

```csharp
using Anthropic.SDK;
using Anthropic.SDK.Messaging;

// The Anthropic SDK appends /v1/messages itself, so the base URL must not include /v1
var anthropic = new AnthropicClient(new APIAuthentication(apiKey))
{
    ApiUrlFormat = "https://api.nexos.ai/{0}/{1}",
};

var response = await anthropic.Messages.GetClaudeMessageAsync(new MessageParameters
{
    Model = "Claude Sonnet 5",
    MaxTokens = 1024,
    Messages =
    [
        new Message(RoleType.User, "how many letters 'r' in the word 'strawberry'"),
    ],
});

Console.WriteLine(response.Message);
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.nexos.ai/gateway-api/code-examples/.net-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
