Skip to content

.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

Terminal window
dotnet add package TruthVouch.Sdk

Requires .NET 9.0+.

Basic Setup

1. Get an API Key

  1. Go to TruthVouch dashboard → Settings → API Keys
  2. Click Generate New Key
  3. Choose a Test key for development, Live key for production
  4. Copy the key (it won’t be shown again)

2. Register with Dependency Injection

In Program.cs:

var builder = WebApplication.CreateBuilder(args);
// Register TruthVouch
builder.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:

Terminal window
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 job
var 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 status
var 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
└── UpstreamProviderException

Configuration

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;
});
FailModeCircuit Open Behavior
FailMode.Open (default)Bypass gateway, call provider directly (degraded but operational)
FailMode.ClosedThrow GatewayUnreachableException (safe but blocks traffic)

Environment Variables

Configure SDK behavior via environment variables:

Terminal window
TRUTHVOUCH_API_KEY=tv_live_...
TRUTHVOUCH_GATEWAY_URL=https://gateway.truthvouch.com
TRUTHVOUCH_TIMEOUT_SECONDS=30
TRUTHVOUCH_FAIL_MODE=open
TRUTHVOUCH_MAX_RETRIES=3

OpenTelemetry 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;
}
}

Next Steps