Com.H.GraphAPI 10.0.0

dotnet add package Com.H.GraphAPI --version 10.0.0
                    
NuGet\Install-Package Com.H.GraphAPI -Version 10.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Com.H.GraphAPI" Version="10.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Com.H.GraphAPI" Version="10.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Com.H.GraphAPI" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Com.H.GraphAPI --version 10.0.0
                    
#r "nuget: Com.H.GraphAPI, 10.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Com.H.GraphAPI@10.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Com.H.GraphAPI&version=10.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Com.H.GraphAPI&version=10.0.0
                    
Install as a Cake Tool

Com.H.GraphAPI

Simplified way to use Microsoft Graph API in .NET applications. In its current form it supports app-only authentication and sending email.

  • AuthenticationCom.H.GraphAPI.Identity.GIExtensions, OAuth 2.0 client credentials (app-only, no signed-in user), wrapping MSAL.
  • EmailCom.H.GraphAPI.Mail.Message, posting to the Graph sendMail endpoint.

Kindly visit the project's github page for full documentation https://github.com/H7O/Com.H.GraphAPI

A single package multi-targets netstandard2.0, net8.0, net9.0 and net10.0. That covers .NET 8 and later, and — through .NET Standard 2.0 — .NET Framework 4.6.2+ (4.7.2 or later recommended). Earlier releases shipped as two separate lines; from 10.0.0 there is just one package.

Sending requires an app registration with the Mail.Send application permission and admin consent granted.

Installation

Nuget

Install-Package Com.H.GraphAPI

.NET CLI

dotnet add package Com.H.GraphAPI

That's all — there is nothing to add to your project file, on any framework.

Quick start

using Com.H.GraphAPI.Identity;
using Com.H.GraphAPI.Mail;

string clientId = "Your Client ID";
string clientSecret = "Your Client Secret";
string tenantId = "Your Tenant ID";

var accessToken = await GIExtensions.GetAccessTokenAsync(clientId, clientSecret, tenantId);

var msg = new Message
{
    From = "yourName@yourCompany.com",
    Subject = "Testing MS Graph API",
    Body = "This is a test email sent via <strong>MS Graph API</strong>"
};

msg.To.Add("yourName@yourCompany.com");

// read from a file path
msg.Attachments.Add(@"c:\temp\email\test1.txt");

// add content directly
msg.Attachments.AddContent("direct content test", "test2.txt");

// read from an IO Stream
using (var fs = new FileStream(@"c:\temp\email\test3.txt", FileMode.Open, FileAccess.Read))
{
    await msg.Attachments.AddAsync(fs, "test3.txt");
    // the stream can be safely closed here as it is read immediately.
}

using var result = await msg.SendAsync(accessToken);

Console.WriteLine(result.IsSuccessStatusCode
    ? "Email sent successfully"
    : "Email failed to send");

Every async method has a synchronous counterpart — GetAccessToken, Send.

Re-using an access token

Tokens last about an hour. Set a delegate and it is called during send, but only when no token was passed to SendAsync:

msg.GetAccessTokenAsyncDelegate = async cancellationToken =>
{
    var token = await GIExtensions.GetAccessTokenWithExpiryDateAsync(
        clientId, clientSecret, tenantId, cancellationToken);

    Console.WriteLine($"access token expires on {token.ExpiresOn}");
    return token.AccessToken;
};

using var result = await msg.SendAsync();   // no token argument

GIExtensions also keeps one MSAL application per credential set, so repeated calls are served from MSAL's token cache rather than hitting Entra ID every time.

Error handling

msg.ThrowOnFailure = true;   // default is false: SendAsync returns the response instead

try
{
    using var result = await msg.SendAsync(accessToken, cancellationToken);
}
catch (GraphApiException ex)      // Com.H.GraphAPI
{
    // Graph explains *why* it refused in the body, not the status line.
    Console.WriteLine($"{ex.StatusCode}: {ex.ResponseBody}");
}

Supplying your own HttpClient

You don't have to do anything here — the library creates one HttpClient and reuses it for every message. Swap in your own if you need a different timeout, a proxy, or IHttpClientFactory:

// used by every message from now on
Message.SharedHttpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(2) };

// ...or just by this one message
msg.HttpClient = httpClientFactory.CreateClient("graph");

If you're on .NET Framework: mentioning HttpClient or HttpResponseMessage by name in your own code means adding using System.Net.Http; to the top of that file. .NET 8 and later add that line for you behind the scenes; .NET Framework doesn't. If the compiler says it can't find the type HttpClient, that's why — and letting var figure out the types avoids it entirely.

API at a glance

MessageFrom (required) · To / Cc / Bcc / ReplyTo (List<string>) · Subject · Body · DisableHtmlBody · Importance (Low/Normal/High) · SaveToSentItems · Attachments · GetAccessTokenDelegate · GetAccessTokenAsyncDelegate · ThrowOnFailure · MaxRequestSizeBytes (default 4 MB, 0 disables) · GraphApiBaseAddress · HttpClient and static SharedHttpClient · SendAsync(accessToken?, cancellationToken?) · Send(accessToken?, cancellationToken?).

Message.Attachments (MailAttachmentCollection, an IReadOnlyList<MailAttachment>) — Add(filePath, fileName?) · Add(stream, fileName) · AddContent(string|byte[], fileName) · AddAsync(...) variants of each · Remove(fileNameOrFilePath) · Clear() · Count · indexer · TotalContentLength. Each MailAttachment has FileName, FilePath, Content (byte[]), ContentType, IsInline and ContentId for cid: inline images.

GIExtensionsGetAccessTokenAsync · GetAccessTokenWithExpiryDateAsync (returns (AccessToken, ExpiresOn)) · RequestAccessTokenAsync (MSAL AuthenticationResult) · ClearApplicationCache(), each with a synchronous counterpart. All take (clientId, clientSecret, tenantId, cancellationToken = default, scopes = null, authorityHost = "https://login.microsoftonline.com").

MailExtensionsbool IsEmail(this string?).

Notes — a Message is a builder, not a one-shot: sending doesn't modify it, so the same instance can be sent repeatedly. Malformed recipients are dropped from the payload. Graph caps sendMail at 4 MB including base64-expanded attachments.

Upgrading from 2.0.0.x

10.0.0 fixes payload corruption when a subject, body or attachment name contained a quote, backslash or newline; adds CancellationToken support; shares one HttpClient instead of creating one per send; and stops mutating the message while sending. See the GitHub page for the full list, including the handful of breaking changes.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.0 39 8/2/2026
2.0.0 413 7/9/2023

Single multi-targeted package (netstandard2.0 / net8.0 / net9.0 / net10.0) replacing the separate 2.0.0.x .NET Standard line. Payloads are now built with Utf8JsonWriter, so subjects, bodies and file names containing quotes, backslashes or newlines are escaped correctly instead of corrupting the request. Adds CancellationToken support, shared HttpClient reuse, MSAL token-cache reuse, and an injectable HttpClient for testing.