Snippet
Call Azure OpenAI for a meeting summary
A complete C# provider that sends a transcript to Azure OpenAI chat completions and returns structured meeting-summary text.
AI-generated, human-reviewed: the first draft was generated by an AI agent; I fact-checked, tested, and edited it. AI use policy
What it does#
Calls Azure OpenAI chat completions from the backend to summarize a transcript. The provider fails before making a network call if the endpoint, API key, or deployment name is missing, or if the endpoint is not an absolute HTTPS URI without user info.
You need#
- .NET SDK 10
- A project with
ImplicitUsingsenabled (thedotnet newdefault) - An Azure OpenAI or Foundry model deployment that supports chat completions
- Server-side configuration placeholders:
AzureOpenAI:Endpoint:https://<your-resource>.openai.azure.comAzureOpenAI:ApiKey:<your-api-key>AzureOpenAI:DeploymentName:<your-deployment-name>AzureOpenAI:ApiVersion:2024-10-21
Where it goes#
Save it in the API project as MeetingSummaryProvider.cs. Register it as a typed HttpClient service or construct it with an HttpClient in your composition root.
// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Sarang Datye
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
public sealed class MeetingSummaryProvider(HttpClient httpClient, AzureOpenAISettings settings)
{
private const string SystemPrompt =
"You summarize meeting transcripts. Produce exactly these sections: " +
"'Overview:' (2-4 sentences), 'Key points:' (3-7 bullets), " +
"'Decisions:' (bullets or 'None identified.'), " +
"'Follow-ups:' (bullets or 'None identified.'). " +
"Do not invent facts not present in the transcript. " +
"If the transcript is too short or unclear, say so. " +
"Do not produce final meeting minutes. Do not produce email text.";
public async Task<MeetingSummaryResult> SummarizeAsync(
string transcriptText,
string locale,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(settings.Endpoint)
|| string.IsNullOrWhiteSpace(settings.ApiKey)
|| string.IsNullOrWhiteSpace(settings.DeploymentName))
{
throw new InvalidOperationException(
"Azure OpenAI is not configured. Set endpoint, API key, and deployment name.");
}
if (!Uri.TryCreate(settings.Endpoint, UriKind.Absolute, out var endpointUri)
|| endpointUri.Scheme != Uri.UriSchemeHttps
|| !string.IsNullOrEmpty(endpointUri.UserInfo))
{
throw new InvalidOperationException(
"Azure OpenAI endpoint must be an absolute HTTPS URI without user info.");
}
var requestUri =
$"{endpointUri.AbsoluteUri.TrimEnd('/')}/openai/deployments/{settings.DeploymentName}/chat/completions?api-version={settings.ApiVersion}";
var payload = new
{
messages = new object[]
{
new { role = "system", content = SystemPrompt },
new { role = "user", content = $"Transcript locale: {locale}\n\nTranscript:\n{transcriptText}" }
}
};
using var request = new HttpRequestMessage(HttpMethod.Post, requestUri)
{
Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
};
request.Headers.Add("api-key", settings.ApiKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using var response = await httpClient.SendAsync(request, cancellationToken);
var body = await response.Content.ReadAsStringAsync(cancellationToken);
if (!response.IsSuccessStatusCode)
{
throw new InvalidOperationException(
$"Azure OpenAI summary request failed with status {(int)response.StatusCode}.");
}
var summaryText = ExtractSummaryText(body);
if (string.IsNullOrWhiteSpace(summaryText))
{
throw new ProviderResponseException(
"Azure OpenAI summary response did not include summary content.");
}
return new MeetingSummaryResult(summaryText, settings.DeploymentName);
}
private static string ExtractSummaryText(string responseBody)
{
using var document = JsonDocument.Parse(responseBody);
if (document.RootElement.TryGetProperty("choices", out var choices)
&& choices.ValueKind == JsonValueKind.Array
&& choices.GetArrayLength() > 0)
{
var first = choices[0];
if (first.TryGetProperty("message", out var message)
&& message.TryGetProperty("content", out var content)
&& content.ValueKind == JsonValueKind.String)
{
return content.GetString()?.Trim() ?? string.Empty;
}
}
return string.Empty;
}
}
public sealed record AzureOpenAISettings(
string Endpoint,
string ApiKey,
string DeploymentName,
string ApiVersion = "2024-10-21");
public sealed record MeetingSummaryResult(string SummaryText, string Provider);
public sealed class ProviderResponseException(string message) : InvalidOperationException(message);
Check that it works#
Construct the provider with blank settings and call SummarizeAsync. It throws before making any network call. Then test with a fake HttpMessageHandler that returns a chat-completions JSON body and assert the summary text is extracted.