WizardM365API.Client
8.0.1
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
<PackageReference Include="WizardM365API.Client" Version="8.0.1" />
<PackageVersion Include="WizardM365API.Client" Version="8.0.1" />
<PackageReference Include="WizardM365API.Client" />
paket add WizardM365API.Client --version 8.0.1
#r "nuget: WizardM365API.Client, 8.0.1"
#:package WizardM365API.Client@8.0.1
#addin nuget:?package=WizardM365API.Client&version=8.0.1
#tool nuget:?package=WizardM365API.Client&version=8.0.1
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>
- ForGetMachinesAsync()
List<DefenderMachineAlert>
- ForGetMachineAlertsAsync()
List<DefenderMachineLog>
- ForGetMachineLogsAsync()
List<DefenderMachineVulnerability>
- ForGetMachineVulnerabilitiesAsync()
List<DefenderMachineSoftware>
- ForGetMachineSoftwareAsync()
List<DefenderRecommendation>
- ForGetRecommendationsAsync()
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
- Automatic Detection: The persistence service uses reflection to find entity types in your DbContext that implement the required interfaces
- Mapping: API responses are automatically mapped to your entity properties
- Upsert Logic: Existing entities are updated, new ones are created
- Error Handling: Persistence failures don't break API operations (configurable)
- Audit Fields: Automatically sets CreatedAt, UpdatedAt, UpdatedBy fields
- 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 | Versions 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. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Http (>= 8.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 8.0.0)
- Microsoft.Extensions.Options (>= 8.0.0)
- Microsoft.Graph (>= 5.77.0)
- System.Text.Json (>= 9.0.5)
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 |