This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Official .NET SDK for the Klau API — a roll-off waste hauling platform. The SDK wraps REST endpoints into typed C# clients. Dependencies: Microsoft.Extensions.Logging.Abstractions and Microsoft.Extensions.DependencyInjection.Abstractions.
dotnet build # Build solution
dotnet test # Run all tests
dotnet test --filter "FullyQualifiedName~JobClient" # Run tests matching a pattern
dotnet test --filter "DisplayName=ListAsync_SendsCorrectPath" # Run a single test
dotnet pack -c Release # Create NuGet packageTarget framework: .NET 9.0. Solution file: Klau.Sdk.sln.
src/Klau.Sdk/— The SDK library (ships as NuGet packageKlau.Sdk)tests/Klau.Sdk.Tests/— xUnit tests usingMockHttpHandler(no mocking framework)examples/CsvJobImport/— Console app: CSV → batch create → optimize → read assignmentsexamples/WebhookIntegration/— Kestrel web app: bidirectional sync with webhooks
KlauClient is the single entry point. It owns a KlauHttpClient and exposes domain-specific clients as properties (Jobs, Customers, Dispatches, Orders, Materials, Storefronts, DumpTickets, Proposals, Divisions, Webhooks, Auth).
Construction patterns (in preference order):
services.AddKlauClient(opts => config.GetSection("Klau").Bind(opts))— ASP.NET Core DIKlauClient.CreateFromEnvironment()— ReadsKLAU_API_KEYenv varKlauClient.Create(new KlauClientOptions { ... })— Explicit optionsnew KlauClient("kl_live_...")— Direct construction
API keys must start with kl_live_ — validation happens at construction time (fail-fast). The KlauClientOptions class controls BaseUrl, TimeoutSeconds, and WebhookSecret in addition to the key.
Enterprise multi-tenant: KlauClient.ForTenant(id) returns a TenantScope — an isolated set of sub-clients that pass the tenant ID as a per-request header without mutating the parent client. SetTenant/ClearTenant mutate default headers instead.
All HTTP goes through KlauHttpClient. It handles:
- Auth via Bearer token (API keys starting with
kl_live_) - User-Agent header (
Klau-DotNet-SDK/{version}) - Tenant header injection (
Klau-Tenant-Id) - JSON serialization with
camelCaseproperties +SNAKE_CASE_UPPERenums - Configurable request timeout (default 30s for SDK-created HttpClients)
- Automatic retry (3 retries with exponential backoff) for 429, 502, 503, 504, and network errors
- API envelope unwrapping: all responses are
{ "data": T, "meta": { ... } } - Error mapping to
KlauApiException
Each domain follows the same file pair pattern:
{Domain}Models.cs— Immutablesealed recordtypes with[JsonPropertyName]on every property. Response models use{ get; init; }. Request models userequiredon mandatory fields.{Domain}Client.cs— Stateless client that takesKlauHttpClient+ optionaltenantIdin its constructor. Methods delegate to_http.GetAsync<T>,PostAsync<T>,PatchAsync<T>, etc.
After creating the pair, wire it into KlauClient (constructor + property) and TenantScope (if tenant-scopeable).
Webhook support has two halves:
- Receiving events —
KlauWebhookValidatorverifies HMAC-SHA256 signatures (Klau-Signature: t={ts},v1={hex}) and parses into typed event models (JobAssignedEvent,JobCompletedEvent,DispatchOptimizedEvent, etc.). This is standalone — noKlauClientneeded. - Managing endpoints —
WebhookClient(onKlauClient.Webhooks) calls the Developer Settings API to create/delete/test webhook registrations.
ApiResponse<T>/ResponseMeta— API envelope deserializationApiError/KlauApiException— Structured error handlingPagedResult<T>— List endpoint pagination wrapper (constructed fromResponseMeta)QueryBuilder— Builds URL query strings from optional paramsEnums.cs— All shared enums (JobType,JobStatus,OrderStatus, etc.)
Tests use MockHttpHandler (a DelegatingHandler in tests/Helpers/) instead of a mocking library. Pattern:
var handler = new MockHttpHandler();
var httpClient = new HttpClient(handler);
var client = new KlauClient("kl_live_test", "https://api.test.com", httpClient);
handler.EnqueueResponse(HttpStatusCode.OK, responseBody, optionalMeta);
// call client method, then assert on handler.SentRequests / handler.SentBodiesResponses passed to EnqueueResponse are automatically wrapped in { "data": ... } envelope. Use EnqueueRawResponse for non-JSON error cases.
- API keys must start with
kl_live_— validated at construction, not at first request - All models are
sealed recordwithinit-only properties — never mutable classes - Every JSON property gets an explicit
[JsonPropertyName("...")]attribute (camelCase) - Enums serialize as
SNAKE_CASE_UPPERstrings (configured inKlauHttpClient.JsonOptions) - API paths follow
api/v1/{resource}pattern - All async methods accept an optional
CancellationToken ctas last parameter - List endpoints that return paginated data use
GetResponseAsync<List<T>>+PagedResult<T> - The SDK does NOT dispose a caller-provided
HttpClient(ownership tracking via_ownsHttpClient) - Environment variable fallbacks:
KLAU_API_KEYfor the API key,KLAU_WEBHOOK_SECRETfor webhook signing