> For the complete documentation index, see [llms.txt](https://docs.umbraco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.umbraco.com/ai-in-umbraco/17.latest/using-the-api/chat/basic-chat.md).

# Basic Chat

Send messages and receive a complete response from AI models. This is suitable for most use cases where you don't need real-time streaming.

## Basic Request

{% code title="SimpleChat.cs" %}

```csharp
using Microsoft.Extensions.AI;
using Umbraco.AI.Core.Chat;

public class ChatController : UmbracoApiController
{
    private readonly IAIChatService _chatService;

    public ChatController(IAIChatService chatService)
    {
        _chatService = chatService;
    }

    [HttpPost]
    public async Task<IActionResult> Ask([FromBody] string question)
    {
        var messages = new List<ChatMessage>
        {
            new(ChatRole.User, question)
        };

        var response = await _chatService.GetChatResponseAsync(
            chat => chat.WithAlias("chat-api"),
            messages);

        return Ok(new
        {
            Answer = response.Message.Text,
            TokensUsed = response.Usage?.TotalTokenCount
        });
    }
}
```

{% endcode %}

## Understanding ChatResponse

The `ChatResponse` object contains:

| Property       | Type                | Description                           |
| -------------- | ------------------- | ------------------------------------- |
| `Message`      | `ChatMessage`       | The AI's response message             |
| `FinishReason` | `ChatFinishReason?` | Why the response ended                |
| `Usage`        | `UsageDetails?`     | Token usage statistics                |
| `ModelId`      | `string?`           | The model that generated the response |

{% code title="ResponseDetails.cs" %}

```csharp
var response = await _chatService.GetChatResponseAsync(
    chat => chat.WithAlias("response-details"),
    messages);

// The response text
string? text = response.Message.Text;

// Why the response finished
ChatFinishReason? reason = response.FinishReason;
// Possible values: Stop, Length, ToolCalls, ContentFilter

// Token usage
if (response.Usage is { } usage)
{
    int? input = usage.InputTokenCount;
    int? output = usage.OutputTokenCount;
    int? total = usage.TotalTokenCount;
}
```

{% endcode %}

## With System Prompt

Add a system message to set the AI's behavior:

{% code title="WithSystemPrompt.cs" %}

```csharp
var messages = new List<ChatMessage>
{
    new(ChatRole.System, "You are a helpful assistant that writes concise answers. " +
                         "Keep responses under 100 words."),
    new(ChatRole.User, "Explain what a CMS is.")
};

var response = await _chatService.GetChatResponseAsync(
    chat => chat.WithAlias("system-prompt-example"),
    messages);
```

{% endcode %}

{% hint style="info" %}
System prompts can also be configured in the profile settings, so they're applied automatically without including them in every request.
{% endhint %}

## Multi-Turn Conversation

Include previous messages to maintain context:

{% code title="Conversation.cs" %}

```csharp
public class ConversationService
{
    private readonly IAIChatService _chatService;
    private readonly List<ChatMessage> _history = new();

    public ConversationService(IAIChatService chatService)
    {
        _chatService = chatService;
        _history.Add(new ChatMessage(ChatRole.System,
            "You are a helpful assistant."));
    }

    public async Task<string> SendMessage(string userMessage)
    {
        // Add user message to history
        _history.Add(new ChatMessage(ChatRole.User, userMessage));

        // Send entire conversation
        var response = await _chatService.GetChatResponseAsync(
            chat => chat.WithAlias("conversation"),
            _history);

        // Add assistant response to history
        _history.Add(response.Message);

        return response.Message.Text ?? string.Empty;
    }
}
```

{% endcode %}

## Using a Specific Profile

### By Profile ID

{% code title="ByProfileId.cs" %}

```csharp
public async Task<string> GetResponse(Guid profileId, string question)
{
    var messages = new List<ChatMessage>
    {
        new(ChatRole.User, question)
    };

    var response = await _chatService.GetChatResponseAsync(
        chat => chat
            .WithAlias("profiled-chat")
            .WithProfile(profileId),
        messages);

    return response.Message.Text ?? string.Empty;
}
```

{% endcode %}

### By Profile Alias

{% code title="ByProfileAlias.cs" %}

```csharp
public async Task<string> GetCreativeResponse(string prompt)
{
    var messages = new List<ChatMessage>
    {
        new(ChatRole.User, prompt)
    };

    var response = await _chatService.GetChatResponseAsync(
        chat => chat
            .WithAlias("creative-response")
            .WithProfile("creative-writer"),
        messages);

    return response.Message.Text ?? string.Empty;
}
```

{% endcode %}

## Overriding Profile Settings

Pass `ChatOptions` to override profile defaults:

{% code title="WithOptions.cs" %}

```csharp
var messages = new List<ChatMessage>
{
    new(ChatRole.User, "Write a creative tagline for a coffee shop.")
};

var options = new ChatOptions
{
    Temperature = 0.9f,        // More creative
    MaxOutputTokens = 50       // Keep it short
};

var response = await _chatService.GetChatResponseAsync(
    chat => chat
        .WithAlias("creative-tagline")
        .WithChatOptions(options),
    messages);
```

{% endcode %}

## Error Handling

{% code title="ErrorHandling.cs" %}

```csharp
public async Task<string?> SafeGetResponse(string question)
{
    try
    {
        var messages = new List<ChatMessage>
        {
            new(ChatRole.User, question)
        };

        var response = await _chatService.GetChatResponseAsync(
            chat => chat.WithAlias("safe-chat"),
            messages);
        return response.Message.Text;
    }
    catch (InvalidOperationException ex) when (ex.Message.Contains("profile"))
    {
        // Profile not found or not configured
        _logger.LogError(ex, "Chat profile configuration error");
        return null;
    }
    catch (HttpRequestException ex)
    {
        // Network or API error
        _logger.LogError(ex, "Failed to reach AI provider");
        return null;
    }
}
```

{% endcode %}

## Next Steps

{% content-ref url="/pages/9JeI0TCCBVwc7JOtD2gd" %}
[Streaming](/ai-in-umbraco/17.latest/using-the-api/chat/streaming.md)
{% endcontent-ref %}

{% content-ref url="/pages/5KZY2zDToU0W7NeZ2W1j" %}
[System Prompts](/ai-in-umbraco/17.latest/using-the-api/chat/system-prompts.md)
{% endcontent-ref %}


---

# 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.umbraco.com/ai-in-umbraco/17.latest/using-the-api/chat/basic-chat.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.
