NetLedger.Sdk 4.0.0

dotnet add package NetLedger.Sdk --version 4.0.0
                    
NuGet\Install-Package NetLedger.Sdk -Version 4.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="NetLedger.Sdk" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NetLedger.Sdk" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="NetLedger.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 NetLedger.Sdk --version 4.0.0
                    
#r "nuget: NetLedger.Sdk, 4.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 NetLedger.Sdk@4.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=NetLedger.Sdk&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=NetLedger.Sdk&version=4.0.0
                    
Install as a Cake Tool

NetLedger SDK for .NET

A .NET SDK for interacting with the NetLedger Server REST API.

v4.0.0 Archive Support

NetLedger Server clients remain active-data clients. v4.0.0 adds Archive Server methods for cold reads, archive metadata inspection, and migration lifecycle operations. Keep active and archive clients configured with separate base URLs.

using NetLedger.Sdk;

using NetLedgerClient active = new NetLedgerClient("http://localhost:8080", token, tenantId);
using NetLedgerClient archive = new NetLedgerClient("http://localhost:8081", token, tenantId);

ArchiveHealth health = await archive.Archive.HealthAsync();
List<ArchiveManifestInfo> manifests = await archive.Archive.ManifestsAsync(new ArchiveQuery { MaxResults = 50 });
List<ArchiveStoragePoolInfo> pools = await archive.Archive.StoragePoolsAsync();
ArchiveStoragePoolHealthInfo poolHealth = await archive.Archive.StoragePoolHealthAsync(pools[0].Id!);
ArchiveExportResponse export = await active.Archive.ExportTenantAccountEntriesAsync(tenantId, accountId, new ArchiveExportRequest
{
    ToUtc = DateTime.UtcNow.AddDays(-365),
    DeleteAfterCommit = false
});
ArchiveMigrationInfo migration = await archive.Archive.MigrationAsync(export.MigrationId!);
List<ArchiveMigrationBatchInfo> batches = await archive.Archive.MigrationBatchesAsync(migration.Id!);
ArchiveExportResponse historyExport = await active.Archive.ExportRequestHistoryAsync(new ArchiveExportRequest
{
    TenantId = tenantId,
    ToUtc = DateTime.UtcNow.AddDays(-365)
});
EnumerationResult<Entry> coldEntries = await archive.Archive.TenantEntriesAsync(tenantId, accountId, new ArchiveQuery
{
    MaxResults = 25,
    Ordering = "CreatedDescending",
    AllowPartial = true
});
ArchiveBalanceInfo coldBalance = await archive.Archive.TenantBalanceAsOfAsync(tenantId, accountId, DateTime.UtcNow.AddDays(-365));
ArchiveVerificationResult verification = await archive.Archive.VerifyTenantAccountAsync(tenantId, accountId);
EnumerationResult<RequestHistoryEntry> coldHistory = await archive.Archive.RequestHistoryAsync(new RequestHistoryQuery
{
    MaxResults = 25,
    AllowPartial = true
});

For externally prepared archive payloads, the Archive Server client also exposes the lower-level migration lifecycle: CreateMigrationAsync, CreateMigrationBatchAsync, UploadMigrationBatchContentAsync, SealMigrationAsync, CommitMigrationAsync, and AbortMigrationAsync.

For an end-to-end local validation that starts disposable active and archive servers, exports old entries, and verifies hot/cold retrieval seams, run dotnet run --project src/ArchivalValidation/ArchivalValidation.csproj --framework net8.0 from the repository root.

Installation

dotnet add package NetLedger.Sdk

Or add a project reference:

dotnet add reference path/to/NetLedger.Sdk.csproj

Quick Start

using NetLedger.Sdk;

// Create a client
using NetLedgerClient client = new NetLedgerClient("http://localhost:8080", "your-api-key");

// Check server health
bool isHealthy = await client.Service.HealthCheckAsync();

// Create an account
Account account = await client.Account.CreateAsync("My Account", "Optional notes");

// Add credits and debits
Entry credit = await client.Entry.AddCreditAsync(account.Id, 100.00m, "Initial deposit");
Entry debit = await client.Entry.AddDebitAsync(account.Id, 25.50m, "Purchase");

// Get balance
Balance balance = await client.Balance.GetAsync(account.Id);
Console.WriteLine($"Committed: {balance.CommittedBalance}, Pending: {balance.PendingBalance}");

// Commit pending entries
CommitResult result = await client.Balance.CommitAsync(account.Id);

Features

Service Operations

// Health check
bool healthy = await client.Service.HealthCheckAsync();

// Get service info
ServiceInfo info = await client.Service.GetInfoAsync();

// Get the OpenAPI document JSON used by the dashboard API Explorer
string openApiJson = await client.Service.GetOpenApiJsonAsync();

Account Management

// Create account
Account account = await client.Account.CreateAsync("Account Name", "Notes");

// Get account by ID
Account account = await client.Account.GetAsync(accountId);

// Get account by name
Account account = await client.Account.GetByNameAsync("Account Name");

// Check if account exists
bool exists = await client.Account.ExistsAsync(accountId);

// Delete account
await client.Account.DeleteAsync(accountId);

// Enumerate accounts with pagination
EnumerationResult<Account> result = await client.Account.EnumerateAsync(new AccountEnumerationQuery
{
    MaxResults = 50,
    Skip = 0,
    SearchTerm = "search"
});

Entry Operations

// Add single credit
Entry credit = await client.Entry.AddCreditAsync(accountId, 100.00m, "Description");

// Add multiple credits
List<Entry> credits = await client.Entry.AddCreditsAsync(accountId, new List<EntryInput>
{
    new EntryInput(50.00m, "First credit"),
    new EntryInput(25.00m, "Second credit")
});

// Add single debit
Entry debit = await client.Entry.AddDebitAsync(accountId, 30.00m, "Description");

// Add multiple debits
List<Entry> debits = await client.Entry.AddDebitsAsync(accountId, new List<EntryInput>
{
    new EntryInput(10.00m, "First debit"),
    new EntryInput(15.00m, "Second debit")
});

// Get all entries
List<Entry> entries = await client.Entry.GetAllAsync(accountId);

// Enumerate with filters
EnumerationResult<Entry> result = await client.Entry.EnumerateAsync(accountId, new EntryEnumerationQuery
{
    MaxResults = 100,
    CreatedAfterUtc = DateTime.UtcNow.AddDays(-30),
    AmountMinimum = 10.00m,
    Ordering = EnumerationOrder.AmountDescending
});

// Enumerate debits between $5 and $50 with all metadata filters matched
EnumerationResult<Entry> blueDebits = await client.Entry.EnumerateAsync(accountId, new EntryEnumerationQuery
{
    MaxResults = 50,
    DebitMinimum = 5.00m,
    DebitMaximum = 50.00m,
    Labels = new List<string> { "blue" },
    Tags = new Dictionary<string, string> { { "color", "blue" } },
    Ordering = EnumerationOrder.AmountDescending
});

// Get pending entries
List<Entry> pending = await client.Entry.GetPendingAsync(accountId);
List<Entry> pendingCredits = await client.Entry.GetPendingCreditsAsync(accountId);
List<Entry> pendingDebits = await client.Entry.GetPendingDebitsAsync(accountId);

// Cancel a pending entry
await client.Entry.CancelAsync(accountId, entryId);

Balance Operations

// Get current balance
Balance balance = await client.Balance.GetAsync(accountId);

// Get historical balance
Balance historical = await client.Balance.GetAsOfAsync(accountId, DateTime.UtcNow.AddDays(-7));

// Get all account balances
List<Balance> balances = await client.Balance.GetAllAsync();

// Commit all pending entries
CommitResult result = await client.Balance.CommitAsync(accountId);

// Commit specific entries
CommitResult result = await client.Balance.CommitAsync(accountId, new List<string> { entry1Id, entry2Id });

// Verify balance chain integrity
bool isValid = await client.Balance.VerifyAsync(accountId);

API Key Management

// Create API key
CredentialCreateResponse apiKey = await client.ApiKey.CreateAsync("Key Name", isAdmin: false);
Console.WriteLine($"Key: {apiKey.SecretKey}"); // Only available on creation

// Enumerate API keys
EnumerationResult<ApiKeyInfo> result = await client.ApiKey.EnumerateAsync(new ApiKeyEnumerationQuery
{
    MaxResults = 50,
    Skip = 0
});

// Revoke API key
await client.ApiKey.RevokeAsync(apiKey.Credential?.Id ?? String.Empty);

Request History

Request history is available to administrators according to the server's tenant access rules. System administrators can inspect all tenants, while tenant administrators are scoped to their tenant.

EnumerationResult<RequestHistoryEntry> history = await client.RequestHistory.EnumerateAsync(new RequestHistoryQuery
{
    TenantId = "default",
    MaxResults = 25,
    Skip = 0
});

RequestHistorySummary summary = await client.RequestHistory.SummarizeAsync(new RequestHistoryQuery
{
    MaxResults = 100,
    BucketMinutes = 15
});

RequestHistoryEntry entry = await client.RequestHistory.ReadAsync(history.Objects[0].Id);

Error Handling

The SDK throws specific exceptions for different error scenarios:

try
{
    Account account = await client.Account.GetAsync(accountId);
}
catch (NetLedgerConnectionException ex)
{
    // Unable to connect to the server
    Console.WriteLine($"Connection error: {ex.Message}");
}
catch (NetLedgerApiException ex)
{
    // Server returned an error
    Console.WriteLine($"API error {ex.StatusCode}: {ex.Message}");
    if (ex.Details != null)
        Console.WriteLine($"Details: {ex.Details}");
}
catch (NetLedgerValidationException ex)
{
    // Invalid input parameters
    Console.WriteLine($"Validation error for {ex.ParameterName}: {ex.Message}");
}

Configuration

var client = new NetLedgerClient("http://localhost:8080", "your-api-key");

// Set custom timeout (default: 30 seconds)
client.TimeoutMs = 60000; // 60 seconds

Thread Safety

The NetLedgerClient is thread-safe and can be reused across multiple operations. It is recommended to create a single instance and share it across your application.

Disposal

The client implements IDisposable. Always dispose of it when done:

using NetLedgerClient client = new NetLedgerClient("http://localhost:8080", "your-api-key");
// Use client...
// Automatically disposed at end of scope

Or manually:

NetLedgerClient client = new NetLedgerClient("http://localhost:8080", "your-api-key");
try
{
    // Use client...
}
finally
{
    client.Dispose();
}

Running the Test Harness

dotnet run --project ../NetLedger.Sdk.Test/NetLedger.Sdk.Test.csproj -- http://localhost:8080 your-api-key
dotnet run --project ../NetLedger.Sdk.Test/NetLedger.Sdk.Test.csproj -- http://localhost:8080 your-api-key http://localhost:8081

The third argument is optional. When supplied, the harness also runs Archive Server health, storage-pool, metadata, request-history, and active export checks against the archive endpoint.

License

MIT License - see the LICENSE file for details.

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.

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
4.0.0 200 7/31/2026
2.0.2 388 12/28/2025
2.0.1 215 12/25/2025

NetLedger v4.0.0 SDK support for active/archive client separation, archive queries, archive metadata, and migration APIs.