.NET SDK Quick Start
The TruthVouch .NET SDK provides drop-in replacements for OpenAI, Anthropic, and Google AI providers. All LLM calls are automatically governed with fact-checking, PII detection, and policy enforcement. Integrates with .NET’s dependency injection system.
Installation
dotnet add package TruthVouch.SdkRequires .NET 9.0+.
Basic Setup
1. Get an API Key
- Go to TruthVouch dashboard → Settings → API Keys
- Click Generate New Key
- Choose a Test key for development, Live key for production
- Copy the key (it won’t be shown again)
2. Register with Dependency Injection
In Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Register TruthVouchbuilder.Services.AddTruthVouch(options =>{ options.GatewayUrl = "https://gateway.truthvouch.com"; options.ApiKey = builder.Configuration["TruthVouch:ApiKey"] ?? Environment.GetEnvironmentVariable("TRUTHVOUCH_API_KEY") ?? throw new InvalidOperationException("API key not found");
options.FailMode = FailMode.Open; // Bypass on failure options.TimeoutSeconds = 30; options.MaxRetries = 3;});
var app = builder.Build();Store the API key in appsettings.json or environment variables (never hardcode):
{ "TruthVouch": { "ApiKey": "tv_live_7e9c4a2b8f1d5c3a9e7b2f4d6c1a8e3b" }}Or via environment variable:
export TRUTHVOUCH_API_KEY=tv_live_...3. Inject and Use
public class ChatService(ITruthVouchClient client){ public async Task<string> GetAnswerAsync(string question, CancellationToken ct) { var response = await client.Chat.CreateAsync(new ChatRequest { Model = "gpt-4o", Messages = [new("user", question)] }, ct);
if (response.Governance.Verdict == "blocked") throw new GovernancePolicyViolationException(response.Governance);
return response.Content; }}Drop-In Provider Replacement
OpenAI
Before (direct OpenAI):
var openaiClient = new OpenAIClient(new ApiKeyCredential(apiKey));var chatClient = openaiClient.GetChatClient("gpt-4o");
var completion = await chatClient.CompleteChatAsync( new UserChatMessage("What is quantum computing?"));
Console.WriteLine(completion.Value.Content[0].Text);After (with TruthVouch governance):
public class MyService(ITruthVouchClient client){ public async Task AskAsync(string question, CancellationToken ct) { var response = await client.Chat.CreateAsync(new ChatRequest { Model = "gpt-4o", Messages = [new("user", question)] }, ct);
Console.WriteLine(response.Content); Console.WriteLine($"Verdict: {response.Governance.Verdict}"); }}Zero code changes to your messages, temperature, max_tokens, or other parameters — the API is identical.
Anthropic
var response = await client.Messages.CreateAsync(new AnthropicMessageRequest{ Model = "claude-3-5-sonnet-20241022", MaxTokens = 1024, System = "You are a helpful assistant.", Messages = [new("user", "Explain AI governance")]}, ct);
Console.WriteLine(response.Content[0].Text);Console.WriteLine($"Verdict: {response.Governance.Verdict}");Google AI / Gemini
var response = await client.Google.GenerateContentAsync(new GoogleContentRequest{ Model = "gemini-1.5-pro", Contents = [new("user", "Explain AI governance")]}, ct);
Console.WriteLine(response.Text);Console.WriteLine($"Verdict: {response.Governance.Verdict}");Streaming
Stream responses with governance applied:
await using var stream = await client.Chat.CreateStreamAsync(new ChatRequest{ Model = "gpt-4o", Messages = [new("user", "Write a haiku about AI")],}, ct);
await foreach (var chunk in stream.ReadChunksAsync(ct)){ if (!string.IsNullOrEmpty(chunk.Content)) Console.Write(chunk.Content);
if (chunk.IsLast) { Console.WriteLine(); var gov = stream.GovernanceReport; Console.WriteLine($"Verdict: {gov?.Verdict}"); Console.WriteLine($"Alerts: {gov?.Alerts.Count ?? 0}"); }}Manual Content Scanning
For post-hoc fact-checking of any text:
var result = await client.Scan(new ScanRequest{ Prompt = "Tell me about the founding of NASA", Response = "NASA was founded in 1958 by President Eisenhower"}, ct);
Console.WriteLine($"Verdict: {result.Verdict}");Console.WriteLine($"Trust Score: {result.TrustScore}");Console.WriteLine($"Alerts: {result.Alerts.Count}");
foreach (var alert in result.Alerts){ Console.WriteLine($" - {alert.Severity}: {alert.Message}");}Response format:
public record ScanResult{ public string Verdict { get; init; } // "allowed", "blocked", "warning" public decimal TrustScore { get; init; } // 0.0 to 1.0 public List<Alert> Alerts { get; init; } public bool PiiDetected { get; init; } public List<PIIEntity> PIIEntities { get; init; }}Batch Scanning
For scanning large datasets offline:
// Submit a batch jobvar job = await client.Batch.SubmitAsync(new BatchScanRequest{ SourceUrl = "s3://my-bucket/documents.jsonl", Format = "jsonl", ScanMode = "deep", // "fast", "standard", "deep" CallbackUrl = "https://myapp.com/webhooks/scan-complete" // optional}, ct);
Console.WriteLine($"Job ID: {job.Id}");Console.WriteLine($"Estimated completion: {job.EstimatedCompletion}");
// Poll for statusvar completed = false;while (!completed){ var status = await client.Batch.GetStatusAsync(job.Id, ct); Console.WriteLine($"Progress: {status.ProgressPercent}% ({status.Scanned}/{status.Total})");
if (status.Completed) { Console.WriteLine($"Flagged items: {status.FlaggedCount}"); completed = true; } else { await Task.Delay(10000, ct); // Wait 10 seconds }}Input format (JSONL):
{"prompt": "Tell me about Paris", "response": "Paris is the capital of France"}{"prompt": "What is 2+2?", "response": "2+2 equals 4"}Error Handling
Handle governance blocks and other errors:
try{ var response = await client.Chat.CreateAsync(new ChatRequest { Model = "gpt-4o", Messages = [new("user", "...")] }, ct);}catch (GovernancePolicyViolationException ex){ _logger.LogWarning("Blocked by governance: {Violations}", string.Join(", ", ex.GovernanceReport!.PolicyViolations));}catch (TruthVouchAuthException ex){ _logger.LogError("Auth failed. RequestId: {RequestId}", ex.RequestId);}catch (QuotaExceededException ex){ _logger.LogWarning("Rate limited. RetryAfter: {RetryAfter}", ex.RetryAfterUnixSeconds);}catch (TruthVouchException ex){ _logger.LogError("TruthVouch error [{Code}]: {Message}", ex.ErrorCode, ex.Message);}Exception hierarchy:
TruthVouchException (base)├── GatewayUnreachableException├── GovernancePolicyViolationException├── TruthVouchAuthException├── QuotaExceededException├── InvalidRequestException└── UpstreamProviderExceptionConfiguration
Register with Custom Options
builder.Services.AddTruthVouch(options =>{ options.GatewayUrl = "https://gateway.truthvouch.com"; options.ApiKey = "tv_live_..."; options.TimeoutSeconds = 30; options.MaxRetries = 3; options.FailMode = FailMode.Open; options.CircuitBreakerFailureThreshold = 5; options.CircuitBreakerRecoverySeconds = 60; options.EnableRequestLogging = false; // Avoid logging sensitive data});Circuit Breaker
For production, configure fail mode and circuit breaker:
builder.Services.AddTruthVouch(options =>{ options.FailMode = FailMode.Open; // Bypass on failure (default) options.CircuitBreakerFailureThreshold = 5; options.CircuitBreakerRecoverySeconds = 60;});| FailMode | Circuit Open Behavior |
|---|---|
FailMode.Open (default) | Bypass gateway, call provider directly (degraded but operational) |
FailMode.Closed | Throw GatewayUnreachableException (safe but blocks traffic) |
Environment Variables
Configure SDK behavior via environment variables:
TRUTHVOUCH_API_KEY=tv_live_...TRUTHVOUCH_GATEWAY_URL=https://gateway.truthvouch.comTRUTHVOUCH_TIMEOUT_SECONDS=30TRUTHVOUCH_FAIL_MODE=openTRUTHVOUCH_MAX_RETRIES=3OpenTelemetry Integration
TruthVouch automatically exports OpenTelemetry spans:
var builder = WebApplication.CreateBuilder(args);
builder.Services .AddOpenTelemetry() .WithTracing(tracingBuilder => { tracingBuilder .AddConsoleExporter() .AddOtlpExporter() .AddSource("TruthVouch"); // Collect TruthVouch spans });
builder.Services.AddTruthVouch(options =>{ options.ServiceName = "my-chat-api"; // Propagates to traces});Complete Example
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTruthVouch(options =>{ options.GatewayUrl = "https://gateway.truthvouch.com"; options.ApiKey = builder.Configuration["TruthVouch:ApiKey"]!;});
builder.Services.AddScoped<ChatService>();
var app = builder.Build();
app.MapPost("/ask", Ask);app.Run();
async Task<IResult> Ask(string question, ChatService service, CancellationToken ct){ try { var answer = await service.GetAnswerAsync(question, ct); return Results.Ok(new { answer }); } catch (GovernancePolicyViolationException ex) { return Results.BadRequest(new { error = "Request blocked by policy" }); } catch (Exception ex) { return Results.InternalServerError(); }}
public class ChatService(ITruthVouchClient client){ public async Task<string> GetAnswerAsync(string question, CancellationToken ct) { var response = await client.Chat.CreateAsync(new ChatRequest { Model = "gpt-4o", Messages = [ new("system", "You are a helpful assistant about AI governance."), new("user", question) ], Temperature = 0.7m, MaxTokens = 500 }, ct);
if (response.Governance.Verdict == "blocked") { throw new GovernancePolicyViolationException(response.Governance); }
if (response.Governance.Alerts?.Any() == true) { foreach (var alert in response.Governance.Alerts) { // Log alerts for monitoring } }
return response.Content; }}