WizardM365API.Client 8.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package WizardM365API.Client --version 8.0.1
                    
NuGet\Install-Package WizardM365API.Client -Version 8.0.1
                    
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="WizardM365API.Client" Version="8.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WizardM365API.Client" Version="8.0.1" />
                    
Directory.Packages.props
<PackageReference Include="WizardM365API.Client" />
                    
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 WizardM365API.Client --version 8.0.1
                    
#r "nuget: WizardM365API.Client, 8.0.1"
                    
#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 WizardM365API.Client@8.0.1
                    
#: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=WizardM365API.Client&version=8.0.1
                    
Install as a Cake Addin
#tool nuget:?package=WizardM365API.Client&version=8.0.1
                    
Install as a Cake Tool

WizardM365API.Client

A .NET client SDK for interacting with the Wizard M365 API. This package provides simple and clean interfaces to interact with all Microsoft 365 services exposed by the Wizard M365 API.

Features

  • Teams: Post, reply, edit, and retrieve Teams channel messages
  • OneNote: Create and manage notebooks, sections, and pages
  • SharePoint: Create folders, get site information, and manage SharePoint objects
  • Chat: Harvest chat messages and retrieve chat information
  • Defender: Retrieve machines, alerts, vulnerabilities, software, and security recommendations
  • Authentication: OAuth2 client credentials flow with automatic token management
  • Health: Check API health and status

Installation

dotnet add package WizardM365API.Client

Quick Start

1. Configuration

Add the M365 API settings to your appsettings.json:

{
  "M365Api": {
    "BaseUrl": "https://your-m365-api.com",
    "ApiKey": "your-api-key",
    "SystemId": "your-system-id",
    "DefaultUserEmail": "user@example.com",
    "TimeoutSeconds": 30,
    "RetryAttempts": 3,
    "ThrowOnApiError": false,
    "OAuth2": {
      "TenantId": "your-azure-ad-tenant-id",
      "ClientId": "your-azure-ad-application-client-id",
      "ClientSecret": "your-azure-ad-application-client-secret",
      "Scope": "https://graph.microsoft.com/.default",
      "TokenCacheDurationMinutes": 55
    },
    "Persistence": {
      "Enabled": true,
      "FailSilently": true,
      "PersistTeamsOperations": false,
      "PersistOneNoteOperations": false,
      "PersistSharePointOperations": false,
      "PersistChatOperations": false,
      "TimeoutSeconds": 10,
      "SetAuditFields": true
    }
  }
}

2. Dependency Injection Setup

In your Program.cs or Startup.cs:

using WizardM365API.Client.Extensions;

// Register the M365 API client
builder.Services.AddM365ApiClient(builder.Configuration);

// Or configure manually
builder.Services.AddM365ApiClient(options =>
{
    options.BaseUrl = "https://your-m365-api.com";
    options.ApiKey = "your-api-key";
    options.SystemId = "your-system-id";
    options.DefaultUserEmail = "user@example.com";
});

// Enable persistence using configuration
builder.Services.AddM365ApiClientWithPersistence(builder.Configuration);

// Or configure persistence manually
builder.Services.AddM365ApiClientWithPersistence(options =>
{
    options.BaseUrl = "https://your-m365-api.com";
    options.ApiKey = "your-api-key";
    options.SystemId = "your-system-id";
    options.Persistence.Enabled = true;
    options.Persistence.PersistTeamsOperations = true; // Enable Teams persistence if needed
});

3. Using the Client

using WizardM365API.Client;
using WizardM365API.Client.Models.Teams;

public class TeamsService
{
    private readonly IM365ApiClient _m365Client;

    public TeamsService(IM365ApiClient m365Client)
    {
        _m365Client = m365Client;
    }

    public async Task PostMessageAsync()
    {
        var request = new PostChannelMessageRequest
        {
            TeamId = "team-id",
            ChannelId = "channel-id",
            Subject = "Hello from Client SDK",
            Message = "This message was sent using the M365 API Client SDK!",
            UserEmail = "user@example.com"
        };

        var response = await _m365Client.Teams.PostChannelMessageAsync(request);
        
        if (response.IsSuccess)
        {
            Console.WriteLine($"Message posted successfully: {response.Data}");
        }
        else
        {
            Console.WriteLine($"Error: {response.ErrorMessage}");
        }
    }
}

API Reference

Teams Client

// Post a message to a Teams channel
var postRequest = new PostChannelMessageRequest
{
    TeamId = "team-id",
    ChannelId = "channel-id",
    Subject = "Message Subject",
    Message = "Message content",
    EmailsToMention = new List<string> { "user@example.com" }
};
var response = await client.Teams.PostChannelMessageAsync(postRequest);

// Get a channel message using the generic M365 object endpoint
var getMessageRequest = new GetM365ObjectRequest
{
    ObjectTypeId = 1,           // Required: Object type for channel message
    ObjectId = "1259337",       // Required: Your object ID
    UserEmail = "mcjoseph.agbanlog@wizard-ai.com" // Required: User email
};
var channelMessageResponse = await client.Teams.GetChannelMessageObjectAsync(getMessageRequest);

// Reply to a message
var replyRequest = new ReplyChannelMessageRequest
{
    TeamId = "team-id",
    ChannelId = "channel-id",
    MessageId = "message-id",
    Message = "Reply content"
};
var replyResponse = await client.Teams.ReplyChannelMessageAsync(replyRequest);

// Edit a message
var editRequest = new EditChannelMessageRequest
{
    TeamId = "team-id",
    ChannelId = "channel-id",
    MessageId = "message-id",
    Message = "Updated content"
};
var editResponse = await client.Teams.EditChannelMessageAsync(editRequest);

// Get messages
var getRequest = new GetChannelMessagesRequest
{
    TeamId = "team-id",
    ChannelId = "channel-id",
    Top = 50
};
var messagesResponse = await client.Teams.GetChannelMessagesAsync(getRequest);

OneNote Client

// Create a notebook
var notebookRequest = new CreateNotebookRequest
{
    DisplayName = "My Notebook",
    SiteId = "optional-site-id"
};
var notebookResponse = await client.OneNote.CreateNotebookAsync(notebookRequest);

// Create a section
var sectionRequest = new CreateSectionRequest
{
    DisplayName = "My Section",
    NotebookId = "notebook-id"
};
var sectionResponse = await client.OneNote.CreateSectionAsync(sectionRequest);

// Create a page
var pageRequest = new CreatePageRequest
{
    Title = "My Page",
    Content = "<html><body><h1>Hello World</h1></body></html>",
    SectionId = "section-id"
};
var pageResponse = await client.OneNote.CreatePageAsync(pageRequest);

SharePoint Client

// Get site ID
var siteIdRequest = new GetSiteIdRequest
{
    HostName = "contoso.sharepoint.com",
    SiteRelativePath = "sites/teamsite"
};
var siteIdResponse = await client.SharePoint.GetSiteIdAsync(siteIdRequest);

// Create a folder
var folderRequest = new CreateFolderRequest
{
    SiteId = "site-id",
    FolderName = "New Folder",
    ParentPath = "/Shared Documents"
};
var folderResponse = await client.SharePoint.CreateFolderAsync(folderRequest);

Chat Client

// Harvest chat messages
var harvestRequest = new HarvestMessagesRequest
{
    UserId = "user-id",
    StartDateTime = DateTime.UtcNow.AddDays(-7),
    EndDateTime = DateTime.UtcNow,
    Top = 100
};
var harvestResponse = await client.Chat.HarvestMessagesAsync(harvestRequest);

Defender Client

// Get machines
var machinesRequest = new GetMachinesRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id", 
    ClientSecret = "your-client-secret",
    UserEmail = "user@example.com",
    Top = 50,
    HealthStatus = "Active",
    OsPlatform = "Windows10"
};
var machinesResponse = await client.Defender.GetMachinesAsync(machinesRequest);

if (machinesResponse.IsSuccess)
{
    // machinesResponse.Data is List<DefenderMachine>
    foreach (var machine in machinesResponse.Data)
    {
        Console.WriteLine($"Machine: {machine.ComputerDnsName} - Status: {machine.HealthStatus}");
    }
}

// Get machine alerts
var alertsRequest = new GetMachineAlertsRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret", 
    MachineId = "machine-id",
    UserEmail = "user@example.com"
};
var alertsResponse = await client.Defender.GetMachineAlertsAsync(alertsRequest);

if (alertsResponse.IsSuccess)
{
    // alertsResponse.Data is List<DefenderMachineAlert>
    foreach (var alert in alertsResponse.Data)
    {
        Console.WriteLine($"Alert: {alert.Title} - Severity: {alert.Severity}");
    }
}

// Get machine vulnerabilities
var vulnerabilitiesRequest = new GetMachineVulnerabilitiesRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret",
    MachineId = "machine-id", 
    UserEmail = "user@example.com"
};
var vulnerabilitiesResponse = await client.Defender.GetMachineVulnerabilitiesAsync(vulnerabilitiesRequest);

// Get machine software
var softwareRequest = new GetMachineSoftwareRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret",
    MachineId = "machine-id",
    UserEmail = "user@example.com"
};
var softwareResponse = await client.Defender.GetMachineSoftwareAsync(softwareRequest);

// Get machine logon users  
var logsRequest = new GetMachineLogsRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id", 
    ClientSecret = "your-client-secret",
    MachineId = "machine-id",
    UserEmail = "user@example.com"
};
var logsResponse = await client.Defender.GetMachineLogsAsync(logsRequest);

// Get security recommendations
var recommendationsRequest = new GetRecommendationsRequest
{
    TenantId = "your-tenant-id",
    ClientId = "your-client-id",
    ClientSecret = "your-client-secret", 
    UserEmail = "user@example.com",
    Top = 100,
    RecommendationCategory = "Security"
};
var recommendationsResponse = await client.Defender.GetRecommendationsAsync(recommendationsRequest);

if (recommendationsResponse.IsSuccess)
{
    // recommendationsResponse.Data is List<DefenderRecommendation>
    foreach (var recommendation in recommendationsResponse.Data)
    {
        Console.WriteLine($"Recommendation: {recommendation.RecommendationName} - Severity: {recommendation.SeverityScore}");
    }
}
Defender Response Types

The Defender client returns strongly-typed models for all operations:

  • List<DefenderMachine> - For GetMachinesAsync()
  • List<DefenderMachineAlert> - For GetMachineAlertsAsync()
  • List<DefenderMachineLog> - For GetMachineLogsAsync()
  • List<DefenderMachineVulnerability> - For GetMachineVulnerabilitiesAsync()
  • List<DefenderMachineSoftware> - For GetMachineSoftwareAsync()
  • List<DefenderRecommendation> - For GetRecommendationsAsync()

Each model provides IntelliSense support and compile-time type safety for all properties returned from the Microsoft Defender API.

Authentication Client

// Generate API key
var apiKeyRequest = new GenerateApiKeyRequest
{
    SystemId = "my-system",
    Description = "API key for my application",
    ExpirationDays = 90,
    IsAdmin = false
};
var apiKeyResponse = await client.Auth.GenerateApiKeyAsync(apiKeyRequest);

// Get all API keys
var keysResponse = await client.Auth.GetApiKeysAsync();

// Renew API key
var renewRequest = new RenewApiKeyRequest
{
    AdditionalDays = 30
};
var renewResponse = await client.Auth.RenewApiKeyAsync("key-id", renewRequest);

Health Client

// Basic health check
var healthResponse = await client.Health.GetHealthAsync();

// Detailed health check
var detailedHealthResponse = await client.Health.GetDetailedHealthAsync();

Error Handling

The client returns ApiResponse<T> objects that contain:

public class ApiResponse<T>
{
    public bool IsSuccess { get; set; }
    public T? Data { get; set; }
    public string? ErrorType { get; set; }
    public int? ErrorCode { get; set; }
    public string? ErrorMessage { get; set; }
    public int StatusCode { get; set; }
}

Always check IsSuccess before accessing Data:

var response = await client.Teams.PostChannelMessageAsync(request);

if (response.IsSuccess)
{
    // Use response.Data
    var messageData = response.Data;
}
else
{
    // Handle error
    Console.WriteLine($"Error {response.StatusCode}: {response.ErrorMessage}");
}

Configuration Options

Option Description Default
BaseUrl Base URL of the M365 API Required
ApiKey API key for authentication Required
SystemId System ID for the client Required
DefaultUserEmail Default user email for requests null
TimeoutSeconds HTTP request timeout 30
RetryAttempts Number of retry attempts 3
ThrowOnApiError Whether to throw exceptions on API errors false
Enabled Whether persistence is enabled false
FailSilently Whether to fail silently if persistence fails true
PersistTeamsOperations Whether to persist Teams operations false
PersistOneNoteOperations Whether to persist OneNote operations false
PersistSharePointOperations Whether to persist SharePoint operations false
PersistChatOperations Whether to persist Chat operations false
TimeoutSeconds Timeout for persistence operations 10
SetAuditFields Whether to automatically set audit fields true

Persistence Feature

The M365 API Client now supports automatic persistence of API operation results to your database. This feature allows you to automatically store information about Teams messages, OneNote pages, SharePoint folders, and other M365 objects in your database entities after successful API operations.

Enabling Persistence

1. Basic Setup with Configuration
{
  "M365Api": {
    "BaseUrl": "https://your-m365-api.com",
    "ApiKey": "your-api-key",
    "SystemId": "your-system-id",
    "Persistence": {
      "Enabled": true,
      "FailSilently": true,
      "PersistTeamsOperations": false,
      "PersistOneNoteOperations": false,
      "PersistSharePointOperations": false,
      "PersistChatOperations": false,
      "TimeoutSeconds": 10,
      "SetAuditFields": true
    }
  }
}
// Enable persistence using configuration
builder.Services.AddM365ApiClientWithPersistence(builder.Configuration);

// Or configure persistence manually
builder.Services.AddM365ApiClientWithPersistence(options =>
{
    options.BaseUrl = "https://your-m365-api.com";
    options.ApiKey = "your-api-key";
    options.SystemId = "your-system-id";
    options.Persistence.Enabled = true;
    options.Persistence.PersistTeamsOperations = true; // Enable Teams persistence if needed
});
2. Entity Setup

Your entities must implement the required interfaces:

using WizardM365API.Client.Persistence.Entities;

public class MsTeamsTeam : IMsTeamsTeam
{
    public Guid Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string? Description { get; set; }
    public string? Link { get; set; }
    public string ExternalId { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public DateTime? UpdatedAt { get; set; }
    public string? UpdatedBy { get; set; }

    // Your additional properties
    public bool IsActive { get; set; } = true;
    public virtual ICollection<MsTeamsChannel> Channels { get; set; } = new List<MsTeamsChannel>();
}

public class MsTeamsChannel : IMsTeamsChannel
{
    public Guid Id { get; set; }
    public Guid TeamId { get; set; }
    public string Name { get; set; } = string.Empty;
    public string? Type { get; set; }
    public string? Link { get; set; }
    public string ExternalId { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public DateTime? UpdatedAt { get; set; }
    public string? UpdatedBy { get; set; }

    // Your additional properties and navigation properties
    public virtual MsTeamsTeam Team { get; set; } = null!;
    public virtual ICollection<MsTeamsConversation> Conversations { get; set; } = new List<MsTeamsConversation>();
}

public class MsTeamsConversation : IMsTeamsConversation
{
    public Guid Id { get; set; }
    public Guid ChannelId { get; set; }
    public string? Link { get; set; }
    public string ExternalId { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
    public string? CreatedBy { get; set; }
    public DateTime? UpdatedAt { get; set; }
    public string? UpdatedBy { get; set; }
    public bool IsQueued { get; set; }
    public bool Processed { get; set; }
    public int? ObjectId { get; set; }
    public int? ObjectTypeId { get; set; }

    // Your additional properties
    public virtual MsTeamsChannel Channel { get; set; } = null!;
}
3. DbContext Setup

Ensure your DbContext includes DbSets for entities that implement the required interfaces:

public class MyDbContext : DbContext
{
    public DbSet<MsTeamsTeam> MsTeamsTeams { get; set; }
    public DbSet<MsTeamsChannel> MsTeamsChannels { get; set; }
    public DbSet<MsTeamsConversation> MsTeamsConversations { get; set; }
    public DbSet<MsOneNoteNotebook> MsOneNoteNotebooks { get; set; }
    public DbSet<MsOneNoteSection> MsOneNoteSections { get; set; }
    public DbSet<MsOneNotePage> MsOneNotePages { get; set; }
    // ... other DbSets
}

Using Persistence-Enabled Methods

Use the new overloads that accept a DbContext parameter:

public class TeamsService
{
    private readonly IM365ApiClient _m365Client;
    private readonly MyDbContext _dbContext;

    public TeamsService(IM365ApiClient m365Client, MyDbContext dbContext)
    {
        _m365Client = m365Client;
        _dbContext = dbContext;
    }

    public async Task PostMessageWithPersistenceAsync()
    {
        var request = new PostChannelMessageRequest
        {
            TeamId = "team-id",
            ChannelId = "channel-id",
            Subject = "Hello from Client SDK",
            Message = "This message will be automatically saved to database!",
            UserEmail = "user@example.com"
        };

        // This will post the message AND automatically save it to your database
        var response = await _m365Client.Teams.PostChannelMessageAsync(request, _dbContext);
        
        if (response.IsSuccess)
        {
            // Message posted and persisted successfully
            Console.WriteLine($"Message posted and saved: {response.Data?.Id}");
        }
    }

    public async Task GetChannelWithPersistenceAsync()
    {
        var request = new GetM365ObjectRequest
        {
            ObjectId = "channel-id",
            UserEmail = "user@example.com"
        };

        // This will get the channel AND automatically save it to your database
        var response = await _m365Client.Teams.GetChannelAsync(request, _dbContext);
        
        if (response.IsSuccess)
        {
            // Channel retrieved and persisted successfully
            Console.WriteLine($"Channel retrieved and saved: {response.Data?.DisplayName}");
        }
    }
}

Available Persistence Interfaces

  • Teams: IMsTeamsTeam, IMsTeamsChannel, IMsTeamsConversation
  • OneNote: IMsOneNoteNotebook, IMsOneNoteSection, IMsOneNotePage
  • SharePoint: IMsSharePointSite, IMsSharePointList, IMsSharePointFolder, IMsSharePointFile
  • Chat: IMsChatConversation, IMsChatMessage

Configuration Options

Option Description Default
Enabled Whether persistence is enabled false
FailSilently Whether to fail silently if persistence fails true
PersistTeamsOperations Whether to persist Teams operations false
PersistOneNoteOperations Whether to persist OneNote operations false
PersistSharePointOperations Whether to persist SharePoint operations false
PersistChatOperations Whether to persist Chat operations false
TimeoutSeconds Timeout for persistence operations 10
SetAuditFields Whether to automatically set audit fields true

How It Works

  1. Automatic Detection: The persistence service uses reflection to find entity types in your DbContext that implement the required interfaces
  2. Mapping: API responses are automatically mapped to your entity properties
  3. Upsert Logic: Existing entities are updated, new ones are created
  4. Error Handling: Persistence failures don't break API operations (configurable)
  5. Audit Fields: Automatically sets CreatedAt, UpdatedAt, UpdatedBy fields
  6. Object IDs: Uses ObjectId and ObjectTypeId from the request DTOs

Benefits

  • Zero Code Changes: Existing API calls work unchanged
  • Opt-in Persistence: Use persistence-enabled methods only when needed
  • Flexible Entity Design: Your entities can have additional properties beyond the required interface
  • Robust Error Handling: Persistence failures don't break your application flow
  • Automatic Relationships: Handles parent-child relationships (Team → Channel → Conversation)

License

MIT License

Support

For issues and questions, please refer to the main Wizard M365 API documentation or create an issue in the repository.

Product Compatible and additional computed target framework versions.
.NET 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 was computed.  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 was computed.  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. 
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 161 8/29/2025
9.0.0 165 8/29/2025
8.1.0 113 8/22/2025
8.0.7 117 8/18/2025
8.0.6 125 8/18/2025
8.0.5 123 8/18/2025
8.0.4 123 8/18/2025
8.0.3 123 8/18/2025
8.0.2 125 8/18/2025
8.0.1 130 8/15/2025
8.0.0 131 8/12/2025
7.4.1 127 8/11/2025
7.4.0 124 8/11/2025
7.3.0 124 8/11/2025
7.2.0 126 8/11/2025
7.1.1 217 8/6/2025
7.1.0 98 7/31/2025
7.0.2 96 7/30/2025
7.0.1 123 7/29/2025
7.0.0 96 7/29/2025
6.0.1 96 7/29/2025
6.0.0 96 7/28/2025
5.5.0 125 7/17/2025