Skip to content

.NET SDK

The official .NET SDK targets net8.0 and mirrors the Python / PHP SDK shape.

Terminal window
dotnet add package Zutax
using Zutax;
using var client = new ZutaxClient(apiKey: "zk_test_...", baseUrl: "https://staging-api.getzutax.com");
var payload = new Dictionary<string, object?>
{
["external_id"] = "INV-2026-0042",
["issue_date"] = "2026-05-03",
["due_date"] = "2026-06-03",
["customer"] = new Dictionary<string, object?>
{
["external_id"] = "C-100",
["legal_name"] = "Acme Industries Limited",
["tin"] = "12345678-0001",
},
["lines"] = new[]
{
new Dictionary<string, object?>
{
["name"] = "Consulting services — May 2026",
["quantity"] = "1",
["unit_price_naira"] = "250000.00",
["vat_rate"] = "7.5",
},
},
};
var result = await client.Invoices.TransmitAsync(payload, idempotencyKey: "INV-2026-0042");
Console.WriteLine(result["invoice_id"]);
using Zutax.Exceptions;
try
{
await client.Invoices.TransmitAsync(payload);
}
catch (ValidationException e)
{
foreach (var err in e.Errors)
Console.WriteLine($"{err["loc"]}: {err["msg"]}");
}
catch (RateLimitException e)
{
Console.WriteLine($"Slow down, retry in {e.RetryAfter}s");
}

| Exception | When it fires | |---|---| | AuthException | 401 / 403 | | NotFoundException | 404 | | ValidationException | 400 / 422 | | ConflictException | 409 | | RateLimitException | 429 (after retries) | | ServerException | 5xx (after retries) | | ZutaxException | base class |

IdempotencyKey.ScopedAsync uses AsyncLocal<T>, so the key flows through await chains correctly:

await IdempotencyKey.ScopedAsync("INV-2026-0042", async () =>
{
await client.Invoices.TransmitAsync(payload);
});
client.Invoices // TransmitAsync, GetAsync, GetByExternalIdAsync, ListAsync, CancelAsync
client.Parties // CreateAsync, GetAsync, GetByExternalIdAsync, ListAsync
client.Products // CreateAsync, GetAsync, GetByExternalIdAsync, ListAsync
client.CreditNotes // CreateAsync, GetAsync, GetByExternalIdAsync, ListAsync

Zulaiy/zutax/zutax-sdks/dotnet/