DirectivSys.Sdk 1.1.1

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

DirectivSys.Sdk

Official .NET SDK for DirectivSys Analytics Platform. Enables seamless integration with DirectivSys to receive AI-powered business intelligence directives and submit analytics payloads.

NuGet License: MIT

Features

Zero Boilerplate - Automatic webhook endpoint creation
🔒 Secure by Default - Built-in authentication and validation
📦 Strongly Typed - Complete DTOs for all analytics types
🚀 Easy Submission - Fluent API for submitting analytics payloads
📊 Inventory Ingestion - Real-time and batch data synchronization
Async/Await - Modern async patterns throughout
🎯 Enum Support - Type-safe analytics type selection

Installation

dotnet add package DirectivSys.Sdk

Or via Package Manager Console:

Install-Package DirectivSys.Sdk

Quick Start

1. Configure Services (Program.cs or Startup.cs)

using DirectivSys.Sdk.Extensions;

var builder = WebApplication.CreateBuilder(args);

// Add DirectivSys SDK
builder.Services.AddDirectivSys(options =>
{
    options.ApiKey = "your-directivsys-api-key";
    options.BaseUrl = "https://api.directivsys.com";
    // ClientId is automatically extracted from the API Key
});

var app = builder.Build();

// Map the webhook endpoint
app.MapDirectivWebhook();

// Set up your callback to handle incoming directives
var directivSys = app.GetDirectivSys();
directivSys.OnDirectiveReceived = HandleDirective;

app.Run();

void HandleDirective(GlobalRecord record)
{
    Console.WriteLine($"Received {record.Directives.Count} directives for {record.AnalyticsType}");
    
    foreach (var directive in record.Directives.OrderBy(d => d.Priority))
    {
        Console.WriteLine($"[Priority {directive.Priority}] {directive.Action}");
        Console.WriteLine($"  Rationale: {directive.Rationale}");
        
        // Execute your business logic based on the directive
        switch (directive.Action)
        {
            case "reorderSupplier":
                var sku = directive.Parameters["sku"].ToString();
                var quantity = Convert.ToInt32(directive.Parameters["quantity"]);
                // Your reorder logic here
                break;
                
            case "launchPromotion":
                var targets = directive.Targets;
                // Your promotion logic here
                break;
        }
    }
}

2. Submit Analytics Data

using DirectivSys.Sdk.Models.Payloads;
using DirectivSys.Sdk.Services;

public class InventoryService
{
    private readonly DirectivSysClient _client;

    public InventoryService(DirectivSysClient client)
    {
        _client = client;
    }

    public async Task AnalyzeInventoryAsync()
    {
        var payload = new InventoryHealthPayload
        {
            Sku = "SKU123",
            StockLevel = 20,
            ReorderPoint = 50,
            SafetyStock = 30,
            ForecastedDemandNext30Days = 200,
            AverageLeadTimeDays = 5
        };

        // Submit the payload for async processing
        // Instructions are pre-configured in AnalyticsConfig on the backend
        var response = await _client.SubmitInventoryHealthAsync(payload);
        Console.WriteLine($"Payload submitted: {response.PayloadId}, Status: {response.Status}");
        
        // Option 1: Poll for results
        var result = await PollForResultAsync(response.PayloadId);
        
        // Option 2: Process immediately (synchronous)
        // var result = await _client.ProcessLatestAsync("InventoryHealth");
    }
    
    private async Task<AnalyticsResult?> PollForResultAsync(Guid payloadId, int maxAttempts = 30)
    {
        for (int i = 0; i < maxAttempts; i++)
        {
            var response = await _client.GetResultByPayloadIdAsync(payloadId);
            
            if (response.IsComplete && response.Result != null)
            {
                return response.Result;
            }
            
            if (response.Status == "failed")
            {
                throw new Exception($"Processing failed: {response.Error}");
            }
            
            await Task.Delay(1000); // Wait 1 second before next poll
        }
        
        throw new TimeoutException("Polling timed out");
    }
}

Supported Analytics Types

The SDK includes strongly-typed payload classes for all DirectivSys analytics types:

Important: Instructions for each analytics type are configured once in the AnalyticsConfig on the backend, not sent with each payload submission. Payloads contain only data - this reduces payload size and allows updating instructions without changing client code.

Supply Chain Analytics

  • InventoryHealth - Stock level monitoring and reorder recommendations
  • SupplierReliability - Supplier performance evaluation
  • DemandForecast - Future demand prediction
  • CostOptimization - Order quantity and cost optimization

E-Commerce Analytics

  • CustomerSegmentation - Customer grouping by behavior and value
  • CartAbandonment - Abandoned cart detection and recovery
  • SalesFunnelConversion - Conversion rate tracking and optimization
  • ProductPerformance - Bestseller and slow-mover identification
  • CustomerLifetimeValue - Long-term customer value estimation
  • MarketingROI - Campaign effectiveness measurement

Inventory Data Ingestion

The SDK provides powerful methods for synchronizing inventory data into DirectivSys for real-time analytics. This enables you to keep your inventory data up-to-date with both single-item updates and efficient batch synchronization.

Features

  • Single Item Updates - Real-time updates for individual inventory items
  • Batch Synchronization - Efficient bulk data sync with progress tracking
  • Automatic Validation - Client-side validation before sending
  • Sync Status Monitoring - Track progress of batch operations
  • Error Handling - Detailed error reporting for failed items
  • Idempotent Operations - Safe to retry on network failures

Single Item Update

Update or insert individual inventory items in real-time (e.g., after a sale or restock):

using DirectivSys.Sdk.Models;

public class InventoryService
{
    private readonly DirectivSysClient _client;

    public InventoryService(DirectivSysClient client)
    {
        _client = client;
    }

    public async Task UpdateInventoryAsync(string sku, decimal newStockLevel)
    {
        var item = new InventoryItem
        {
            Sku = sku,
            ProductName = "Widget A",
            ProductCategory = "Tools",
            PriorityLevel = "high",
            StockLevel = newStockLevel,
            ReorderPoint = 50,
            SafetyStock = 20,
            ForecastedDemandNext30Days = 200,
            UnitCost = 5.5m,
            HoldingCostPerUnit = 0.2m,
            OrderCost = 15m,
            SupplierId = "SUP456",
            SupplierName = "Acme Supplies",
            AvgLeadTimeDays = 7,
            LateDeliveriesPercent = 0.05m,
            QualityDefectRate = 0.02m,
            SupplierReliabilityScore = 92m,
            MinOrderQty = 10,
            BudgetCap = 5000m
        };

        // Using enum (recommended)
        var response = await _client.UpdateInventoryItemAsync(
            AnalyticsType.InventoryHealth, 
            item);

        Console.WriteLine($"Item {response.Sku} {response.Status}");
    }
}

Required Fields:

  • Sku - Stock Keeping Unit (unique identifier)
  • StockLevel - Current stock level (must be >= 0)
  • ReorderPoint - Reorder threshold (must be >= 0)

All other fields are optional but recommended for comprehensive analytics.

Batch Synchronization

For syncing large datasets (e.g., nightly full inventory sync), use batch ingestion with automatic progress tracking:

public class InventorySyncService
{
    private readonly DirectivSysClient _client;
    private readonly ILogger<InventorySyncService> _logger;

    public InventorySyncService(
        DirectivSysClient client, 
        ILogger<InventorySyncService> logger)
    {
        _client = client;
        _logger = logger;
    }

    public async Task SyncFullInventoryAsync(List<InventoryItem> allItems)
    {
        // Generate unique sync ID
        var syncId = $"sync-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
        
        // Split into batches of 100-500 items
        var batchSize = 250;
        var batches = allItems
            .Select((item, index) => new { item, index })
            .GroupBy(x => x.index / batchSize)
            .Select(g => g.Select(x => x.item).ToList())
            .ToList();

        _logger.LogInformation(
            "Starting sync {SyncId} with {TotalBatches} batches ({TotalItems} items)",
            syncId, batches.Count, allItems.Count);

        // Process each batch
        for (int i = 0; i < batches.Count; i++)
        {
            var request = new BatchIngestionRequest
            {
                SyncId = syncId,
                TotalBatches = batches.Count,
                CurrentBatchIndex = i,
                Items = batches[i]
            };

            try
            {
                var response = await _client.SyncInventoryBatchAsync(
                    AnalyticsType.InventoryHealth,
                    request);

                _logger.LogInformation(
                    "Batch {Current}/{Total}: {Success} succeeded, {Failed} failed",
                    i + 1,
                    batches.Count,
                    response.SuccessfulCount,
                    response.FailedCount);

                // Log any failures
                foreach (var failure in response.FailedItems)
                {
                    _logger.LogWarning(
                        "Failed to sync {Sku}: {Error} ({ErrorType})",
                        failure.Sku,
                        failure.ErrorMessage,
                        failure.ErrorType);
                }

                // Check if sync is complete
                if (response.SyncStatus.IsComplete)
                {
                    _logger.LogInformation(
                        "Sync {SyncId} completed! Total: {Success} succeeded, {Failed} failed",
                        syncId,
                        response.SyncStatus.TotalSuccessfulItems,
                        response.SyncStatus.TotalFailedItems);
                }
            }
            catch (DirectivSysException ex)
            {
                _logger.LogError(ex, "Failed to sync batch {Index}", i);
                throw;
            }
        }

        // Verify completion
        var finalStatus = await _client.GetSyncStatusAsync(syncId);
        
        if (!finalStatus.IsComplete)
        {
            throw new Exception(
                $"Sync incomplete: {finalStatus.ReceivedBatches}/{finalStatus.TotalBatches} batches received");
        }

        return finalStatus;
    }
}

Sync Status Monitoring

Check the progress of a batch synchronization job:

public async Task<SyncStatus> CheckSyncProgressAsync(string syncId)
{
    var status = await _client.GetSyncStatusAsync(syncId);

    Console.WriteLine($"Status: {status.Status}");
    Console.WriteLine($"Progress: {status.ReceivedBatches}/{status.TotalBatches} batches");
    Console.WriteLine($"Items: {status.TotalSuccessfulItems} succeeded, {status.TotalFailedItems} failed");
    Console.WriteLine($"Complete: {status.IsComplete}");

    if (status.IsComplete)
    {
        Console.WriteLine($"Completed at: {status.CompletedAt}");
    }

    return status;
}

Resilient Batch Sync with Recovery

Handle network interruptions gracefully:

public async Task SyncWithRecoveryAsync(
    List<InventoryItem> allItems, 
    string? resumeSyncId = null)
{
    string syncId;
    int startBatchIndex = 0;

    // Check if resuming an existing sync
    if (!string.IsNullOrEmpty(resumeSyncId))
    {
        var status = await _client.GetSyncStatusAsync(resumeSyncId);
        syncId = resumeSyncId;
        startBatchIndex = status.ReceivedBatches; // Resume from where we left off
        
        _logger.LogInformation(
            "Resuming sync {SyncId} from batch {StartIndex}",
            syncId, startBatchIndex);
    }
    else
    {
        syncId = $"sync-{DateTime.UtcNow:yyyyMMdd-HHmmss}";
        _logger.LogInformation("Starting new sync {SyncId}", syncId);
    }

    var batchSize = 250;
    var batches = allItems
        .Select((item, index) => new { item, index })
        .GroupBy(x => x.index / batchSize)
        .Select(g => g.Select(x => x.item).ToList())
        .ToList();

    // Process from startBatchIndex onwards
    for (int i = startBatchIndex; i < batches.Count; i++)
    {
        var request = new BatchIngestionRequest
        {
            SyncId = syncId,
            TotalBatches = batches.Count,
            CurrentBatchIndex = i,
            Items = batches[i]
        };

        try
        {
            await _client.SyncInventoryBatchAsync(
                AnalyticsType.InventoryHealth,
                request);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, 
                "Network failure at batch {Index}. Resume with syncId: {SyncId}",
                i, syncId);
            throw;
        }
    }
}

Inventory Item Schema

public class InventoryItem
{
    // Required fields
    public string Sku { get; set; }                         // Stock Keeping Unit
    public decimal StockLevel { get; set; }                 // Current stock level
    public decimal ReorderPoint { get; set; }               // Reorder threshold

    // Product information
    public string? ProductName { get; set; }
    public string? ProductCategory { get; set; }
    public string? PriorityLevel { get; set; }              // "high", "medium", "low"

    // Inventory metrics
    public decimal? SafetyStock { get; set; }
    public decimal? ForecastedDemandNext30Days { get; set; }
    public decimal? UnitCost { get; set; }
    public decimal? HoldingCostPerUnit { get; set; }
    public decimal? OrderCost { get; set; }
    public int? MinOrderQty { get; set; }
    public decimal? BudgetCap { get; set; }

    // Supplier information
    public string? SupplierId { get; set; }
    public string? SupplierName { get; set; }
    public int? AvgLeadTimeDays { get; set; }
    public decimal? LateDeliveriesPercent { get; set; }     // 0-1
    public decimal? QualityDefectRate { get; set; }         // 0-1
    public decimal? SupplierReliabilityScore { get; set; }  // 0-100
}

Best Practices

Batch Sizing
  • 50-100 items: Better error isolation, more API calls
  • 100-500 items: Balanced approach (recommended)
  • 500-1000 items: Fewer API calls, harder to debug failures
Sync ID Naming

Use descriptive, unique identifiers:

✅ Good: "sync-20260118-nightly-001"
✅ Good: "sync-inventory-full-20260118143045"
❌ Bad:  "sync1" or "test"
Error Handling
try
{
    var response = await client.UpdateInventoryItemAsync(
        AnalyticsType.InventoryHealth, 
        item);
}
catch (ArgumentException ex)
{
    // Validation error (e.g., missing SKU, negative stock)
    logger.LogError(ex, "Invalid inventory item");
}
catch (DirectivSysException ex)
{
    // API error (e.g., authentication, server error)
    logger.LogError(ex, "Failed to update inventory");
}
Real-Time vs Batch Sync

Use Single Item Updates for:

  • Real-time inventory changes (sales, restocking)
  • Event-driven updates
  • Individual corrections

Use Batch Sync for:

  • Full inventory synchronization
  • Large dataset ingestion
  • Scheduled periodic syncs (e.g., nightly)

Rate Limits

  • Single Item Updates: 100 requests/minute
  • Batch Ingestion: 10 batches/minute
  • Sync Status: 60 requests/minute

Exceeding rate limits returns 429 Too Many Requests.

API Reference

Configuration

DirectivSysOptions
public class DirectivSysOptions
{
    public string ApiKey { get; set; }        // Required: Your DirectivSys API Key (X-API-Key header)
    public string BaseUrl { get; set; }       // Default: "https://api.directivsys.com"
    public Guid? ClientId { get; set; }       // Optional: Extracted from API Key if not provided
    public int TimeoutSeconds { get; set; }   // Default: 30
}

Core Models

GlobalRecord

The main container for analytics results received via webhook:

public class GlobalRecord
{
    public Guid RecordId { get; set; }
    public Guid ClientId { get; set; }
    public string AnalyticsType { get; set; }
    public List<Directive> Directives { get; set; }
    public List<Metric> Metrics { get; set; }
    public List<Observation> Observations { get; set; }
    public Provenance Provenance { get; set; }
    public DateTime CreatedAt { get; set; }
}
Directive

Actionable recommendations from the AI agent:

public class Directive
{
    public Guid DirectiveId { get; set; }
    public int Priority { get; set; }                    // 1 = highest priority
    public string Action { get; set; }                   // e.g., "reorderSupplier"
    public Dictionary<string, object> Parameters { get; set; }
    public List<string> Targets { get; set; }
    public string Rationale { get; set; }
    public string Status { get; set; }
}
Metric

Calculated business metrics:

public class Metric
{
    public string Name { get; set; }
    public double Value { get; set; }
    public string Unit { get; set; }
    public string Status { get; set; }      // "healthy", "warning", "critical"
    public string Explanation { get; set; }
}
Observation

Human-readable explanations and context:

public class Observation
{
    public string Text { get; set; }
    public string Severity { get; set; }    // "info", "warning", "critical"
    public string Scope { get; set; }       // "global", "product", "supplier"
}

DirectivSysClient Methods

Submit Analytics Payloads
// Specific analytics types - Submit for async processing
Task<IngestResponse> SubmitInventoryHealthAsync(InventoryHealthPayload payload, CancellationToken cancellationToken = default)
Task<IngestResponse> SubmitCustomerSegmentationAsync(CustomerSegmentationPayload payload, CancellationToken cancellationToken = default)
Task<IngestResponse> SubmitCartAbandonmentAsync(CartAbandonmentPayload payload, CancellationToken cancellationToken = default)
Task<IngestResponse> SubmitProductPerformanceAsync(ProductPerformancePayload payload, CancellationToken cancellationToken = default)

// Generic submission for any analytics type (with enum)
Task<IngestResponse> SubmitAnalyticsAsync<T>(AnalyticsType analyticsType, T payload, CancellationToken cancellationToken = default) 
    where T : AnalyticsPayloadBase

// Generic submission for any analytics type (with string)
Task<IngestResponse> SubmitAnalyticsAsync<T>(string analyticsType, T payload, CancellationToken cancellationToken = default) 
    where T : AnalyticsPayloadBase
Inventory Data Ingestion
// Update or insert a single inventory item (with enum)
Task<ItemUpdateResponse> UpdateInventoryItemAsync(
    AnalyticsType analyticsType, 
    InventoryItem item, 
    CancellationToken cancellationToken = default)

// Update or insert a single inventory item (with string)
Task<ItemUpdateResponse> UpdateInventoryItemAsync(
    string analyticsType, 
    InventoryItem item, 
    CancellationToken cancellationToken = default)

// Ingest a batch of inventory items (with enum)
Task<BatchIngestionResponse> SyncInventoryBatchAsync(
    AnalyticsType analyticsType, 
    BatchIngestionRequest request, 
    CancellationToken cancellationToken = default)

// Ingest a batch of inventory items (with string)
Task<BatchIngestionResponse> SyncInventoryBatchAsync(
    string analyticsType, 
    BatchIngestionRequest request, 
    CancellationToken cancellationToken = default)

// Get sync status and progress
Task<SyncStatus> GetSyncStatusAsync(
    string syncId, 
    CancellationToken cancellationToken = default)
Process and Retrieve Results
// Process the latest pending payload synchronously (with enum)
Task<AnalyticsResult> ProcessLatestAsync(AnalyticsType analyticsType, CancellationToken cancellationToken = default)

// Process the latest pending payload synchronously (with string)
Task<AnalyticsResult> ProcessLatestAsync(string analyticsType, CancellationToken cancellationToken = default)

// Get result for a specific payload (for polling after async submission)
Task<PayloadResultResponse> GetResultByPayloadIdAsync(Guid payloadId, CancellationToken cancellationToken = default)
Response Models
// Response from ingestion
public class IngestResponse
{
    public Guid PayloadId { get; set; }
    public string Status { get; set; }  // "pending"
}

// Response from single item update
public class ItemUpdateResponse
{
    public string Sku { get; set; }
    public string Status { get; set; }  // "upserted"
}

// Response from batch ingestion
public class BatchIngestionResponse
{
    public string SyncId { get; set; }
    public int BatchIndex { get; set; }
    public int TotalItemsInBatch { get; set; }
    public int SuccessfulCount { get; set; }
    public int FailedCount { get; set; }
    public List<FailedItem> FailedItems { get; set; }
    public SyncStatus SyncStatus { get; set; }
}

// Sync status information
public class SyncStatus
{
    public string Status { get; set; }         // "in-progress", "completed", "failed"
    public int TotalBatches { get; set; }
    public int ReceivedBatches { get; set; }
    public int TotalSuccessfulItems { get; set; }
    public int TotalFailedItems { get; set; }
    public bool IsComplete { get; set; }
    public DateTime? CompletedAt { get; set; }
}

// Failed item details
public class FailedItem
{
    public string Sku { get; set; }
    public string ErrorMessage { get; set; }
    public string ErrorType { get; set; }     // "validation", "processing", "conflict"
}

// Complete analytics result
public class AnalyticsResult
{
    public Guid ResultId { get; set; }
    public Guid PayloadId { get; set; }
    public string AnalyticsType { get; set; }
    public DateTime CreatedAt { get; set; }
    public string InputsSummary { get; set; }
    public string InstructionSet { get; set; }
    public string Provenance { get; set; }
    public List<MetricResult> Metrics { get; set; }
    public List<DirectiveResult> Directives { get; set; }
    public List<ObservationResult> Observations { get; set; }
}

// Polling response (can be complete or status)
public class PayloadResultResponse
{
    public bool IsComplete { get; set; }
    public AnalyticsResult? Result { get; set; }  // Available when IsComplete = true
    public string? Status { get; set; }            // "pending", "processing", "failed"
    public string? Error { get; set; }             // Available when Status = "failed"
}

Advanced Usage

Processing Patterns

The SDK supports three different processing patterns:

Pattern 1: Fire-and-Forget Ingestion

Submit payloads for async processing without waiting for results:

var response = await client.SubmitInventoryHealthAsync(payload);
// Store response.PayloadId for later retrieval if needed
Console.WriteLine($"Submitted: {response.PayloadId}");
Pattern 2: Synchronous Processing

Process immediately and get results:

// First ingest the payload
var ingestResponse = await client.SubmitInventoryHealthAsync(payload);

// Then immediately process the latest one
var result = await client.ProcessLatestAsync("InventoryHealth");

// Work with the result
foreach (var directive in result.Directives)
{
    Console.WriteLine($"{directive.Action}: {directive.Rationale}");
}
Pattern 3: Async with Polling

Submit for async processing and poll until complete:

var ingestResponse = await client.SubmitInventoryHealthAsync(payload);

// Poll for results
AnalyticsResult? result = null;
int attempts = 0;
while (result == null && attempts < 30)
{
    await Task.Delay(1000); // Wait 1 second
    
    var response = await client.GetResultByPayloadIdAsync(ingestResponse.PayloadId);
    
    if (response.IsComplete)
    {
        result = response.Result;
    }
    else if (response.Status == "failed")
    {
        throw new Exception($"Processing failed: {response.Error}");
    }
    
    attempts++;
}

Custom Analytics Types

For analytics types not included in the SDK, use the generic submission method:

public class CustomAnalyticsPayload : AnalyticsPayloadBase
{
    public string CustomField1 { get; set; }
    public int CustomField2 { get; set; }
}

var payload = new CustomAnalyticsPayload
{
    CustomField1 = "value",
    CustomField2 = 42
};

await client.SubmitAnalyticsAsync("CustomAnalyticsType", payload);

Accessing Services Directly

// In a controller or service
public class MyController : ControllerBase
{
    private readonly DirectivSysClient _client;
    private readonly IDirectivSysService _service;

    public MyController(DirectivSysClient client, IDirectivSysService service)
    {
        _client = client;
        _service = service;
        
        // Set callback dynamically
        _service.OnDirectiveReceived = HandleDirective;
    }
}

Error Handling

try
{
    var response = await client.SubmitInventoryHealthAsync(payload);
}
catch (DirectivSysException ex)
{
    // Handle DirectivSys-specific errors
    Console.WriteLine($"DirectivSys error: {ex.Message}");
}
catch (HttpRequestException ex)
{
    // Handle network errors
    Console.WriteLine($"Network error: {ex.Message}");
}

Configuration Best Practices

Using Configuration Files

appsettings.json:

{
  "DirectivSys": {
    "ApiKey": "your-api-key",
    "BaseUrl": "https://api.directivsys.com",
    "TimeoutSeconds": 30
  }
}

Program.cs:

builder.Services.AddDirectivSys(options =>
{
    builder.Configuration.GetSection("DirectivSys").Bind(options);
});

Using Environment Variables

builder.Services.AddDirectivSys(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("DIRECTIVSYS_API_KEY") 
        ?? throw new InvalidOperationException("DIRECTIVSYS_API_KEY not set");
    options.BaseUrl = Environment.GetEnvironmentVariable("DIRECTIVSYS_BASE_URL") 
        ?? "https://api.directivsys.com";
});

Webhook Security

The webhook endpoint (/directiv/webhook) validates incoming requests using the API key. DirectivSys includes the X-API-Key header automatically when sending webhook notifications.

The validation ensures:

  • Request includes X-API-Key: {your-api-key} header
  • The provided key matches your configured API key
  • Payload can be deserialized to GlobalRecord

Example: Complete Integration

using DirectivSys.Sdk.Extensions;
using DirectivSys.Sdk.Models;
using DirectivSys.Sdk.Models.Payloads;
using DirectivSys.Sdk.Services;

var builder = WebApplication.CreateBuilder(args);

// Configure DirectivSys
builder.Services.AddDirectivSys(options =>
{
    options.ApiKey = builder.Configuration["DirectivSys:ApiKey"]!;
    options.BaseUrl = builder.Configuration["DirectivSys:BaseUrl"] ?? "https://api.directivsys.com";
});

// Add your services
builder.Services.AddControllers();
builder.Services.AddHostedService<InventoryAnalysisWorker>();

var app = builder.Build();

app.UseRouting();
app.MapControllers();
app.MapDirectivWebhook();

// Configure directive callback
var directivSys = app.GetDirectivSys();
directivSys.OnDirectiveReceived = record =>
{
    var logger = app.Services.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("Received analytics: {Type} with {Count} directives",
        record.AnalyticsType, record.Directives.Count);

    foreach (var directive in record.Directives)
    {
        // Process each directive
        ProcessDirective(directive, app.Services);
    }
};

app.Run();

void ProcessDirective(Directive directive, IServiceProvider services)
{
    var scope = services.CreateScope();
    
    switch (directive.Action)
    {
        case "reorderSupplier":
            var inventoryService = scope.ServiceProvider.GetRequiredService<IInventoryService>();
            inventoryService.Reorder(directive.Parameters["sku"].ToString()!);
            break;
            
        case "launchPromotion":
            var marketingService = scope.ServiceProvider.GetRequiredService<IMarketingService>();
            marketingService.LaunchCampaign(directive.Targets);
            break;
    }
}

// Background worker to periodically submit analytics
public class InventoryAnalysisWorker : BackgroundService
{
    private readonly DirectivSysClient _client;
    private readonly ILogger<InventoryAnalysisWorker> _logger;

    public InventoryAnalysisWorker(DirectivSysClient client, ILogger<InventoryAnalysisWorker> logger)
    {
        _client = client;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                // Aggregate your data
                var inventoryData = await GetCurrentInventoryAsync();
                
                // Submit to DirectivSys
                await _client.SubmitInventoryHealthAsync(inventoryData, stoppingToken);
                
                _logger.LogInformation("Inventory analysis submitted successfully");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to submit inventory analysis");
            }

            // Run daily
            await Task.Delay(TimeSpan.FromHours(24), stoppingToken);
        }
    }

    private async Task<InventoryHealthPayload> GetCurrentInventoryAsync()
    {
        // Your logic to aggregate inventory data
        return new InventoryHealthPayload
        {
            Sku = "SKU123",
            StockLevel = 45,
            ReorderPoint = 50,
            SafetyStock = 30,
            ForecastedDemandNext30Days = 200
        };
    }
}

Requirements

  • .NET 6.0 or higher
  • ASP.NET Core application (for webhook endpoint)

Support

For issues, questions, or feature requests, please visit:

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


Made with ❤️ by DirectivSys Inc

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
1.2.9 144 4/21/2026
1.2.8 147 4/10/2026
1.2.7 129 3/14/2026
1.2.6 131 3/9/2026
1.2.5 118 3/9/2026
1.2.4 108 3/8/2026
1.2.3 116 3/7/2026
1.2.1 318 2/4/2026
1.2.0 161 2/2/2026
1.1.9 136 2/1/2026
1.1.8 134 2/1/2026
1.1.7 137 2/1/2026
1.1.6 142 1/31/2026
1.1.5 151 1/20/2026
1.1.4 130 1/20/2026
1.1.3 127 1/19/2026
1.1.2 138 1/19/2026
1.1.1 136 1/18/2026
1.1.0 135 1/10/2026
1.0.1 139 1/9/2026
Loading failed