C# / .NET Examples
Complete examples for integrating DocBit AI into your .NET application.Installation
Copy
dotnet add package System.Net.Http.Json
Basic Client
Copy
using System.Net.Http.Json;
using System.Text.Json;
public class DocBitClient
{
private readonly HttpClient _httpClient;
private readonly string _apiKey;
public DocBitClient(string apiKey)
{
_apiKey = apiKey;
_httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.docbit.ai")
};
_httpClient.DefaultRequestHeaders.Add("Authorization", $"ApiKey {apiKey}");
}
public async Task<ChatResponse> ChatAsync(
string orgId,
string userId,
string[] roles,
string message,
string? conversationId = null)
{
var request = new HttpRequestMessage(HttpMethod.Post, "/api/ai/chat");
request.Headers.Add("X-External-Org-Id", orgId);
request.Headers.Add("X-External-User-Id", userId);
request.Headers.Add("X-External-Roles", JsonSerializer.Serialize(roles));
request.Content = JsonContent.Create(new
{
message,
conversationId
});
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<ChatResponse>()
?? throw new Exception("Invalid response");
}
}
public record ChatResponse(
string ConversationId,
string Content,
Citation[] Citations,
string Confidence
);
public record Citation(
string DocumentId,
string DocumentTitle,
int? Page,
string? Section,
string Excerpt
);
Usage Example
Copy
var client = new DocBitClient(Environment.GetEnvironmentVariable("DOCBIT_API_KEY")!);
var response = await client.ChatAsync(
orgId: "acme",
userId: "user-456",
roles: new[] { "hr", "all-staff" },
message: "What is our vacation policy?"
);
Console.WriteLine(response.Content);
Console.WriteLine($"Confidence: {response.Confidence}");
foreach (var citation in response.Citations)
{
Console.WriteLine($"Source: {citation.DocumentTitle} (Page {citation.Page})");
}
Streaming Chat
Copy
public async IAsyncEnumerable<StreamEvent> StreamChatAsync(
string orgId,
string userId,
string[] roles,
string message)
{
var request = new HttpRequestMessage(HttpMethod.Post, "/api/ai/chat/stream");
request.Headers.Add("X-External-Org-Id", orgId);
request.Headers.Add("X-External-User-Id", userId);
request.Headers.Add("X-External-Roles", JsonSerializer.Serialize(roles));
request.Headers.Add("Accept", "text/event-stream");
request.Content = JsonContent.Create(new { message });
var response = await _httpClient.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead
);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync();
if (string.IsNullOrEmpty(line) || !line.StartsWith("data: "))
continue;
var json = line[6..];
var data = JsonSerializer.Deserialize<JsonElement>(json);
if (data.TryGetProperty("token", out var token))
{
yield return new TokenEvent(token.GetString()!);
}
else if (data.TryGetProperty("citations", out _))
{
var done = JsonSerializer.Deserialize<DoneEvent>(json)!;
yield return done;
}
}
}
public abstract record StreamEvent;
public record TokenEvent(string Token) : StreamEvent;
public record DoneEvent(
string ConversationId,
Citation[] Citations,
string Confidence
) : StreamEvent;
Using the Stream
Copy
var fullResponse = new StringBuilder();
await foreach (var evt in client.StreamChatAsync("acme", "user-456", roles, question))
{
switch (evt)
{
case TokenEvent token:
Console.Write(token.Token);
fullResponse.Append(token.Token);
break;
case DoneEvent done:
Console.WriteLine($"\n\nConfidence: {done.Confidence}");
break;
}
}
Upload Document
Copy
public async Task<UploadResponse> UploadDocumentAsync(
string orgId,
string userId,
string[] roles,
string filePath,
string[]? aclRoles = null,
string? folderPath = null)
{
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(await File.ReadAllBytesAsync(filePath));
fileContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
content.Add(fileContent, "file", Path.GetFileName(filePath));
if (aclRoles != null)
{
foreach (var role in aclRoles)
{
content.Add(new StringContent(role), "aclRoles");
}
}
if (folderPath != null)
{
content.Add(new StringContent(folderPath), "folderPath");
}
var request = new HttpRequestMessage(HttpMethod.Post, "/api/documents/upload")
{
Content = content
};
request.Headers.Add("X-External-Org-Id", orgId);
request.Headers.Add("X-External-User-Id", userId);
request.Headers.Add("X-External-Roles", JsonSerializer.Serialize(roles));
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<UploadResponse>()
?? throw new Exception("Invalid response");
}
public record UploadResponse(string DocumentId, string Status);
Usage
Copy
var result = await client.UploadDocumentAsync(
orgId: "acme",
userId: "admin-1",
roles: new[] { "admin" },
filePath: "./hr-policy.pdf",
aclRoles: new[] { "hr" },
folderPath: "/hr/policies"
);
Console.WriteLine($"Document ID: {result.DocumentId}");
Console.WriteLine($"Status: {result.Status}");
ASP.NET Core Integration
Service Registration
Copy
// Program.cs
builder.Services.AddSingleton<DocBitClient>(sp =>
{
var apiKey = builder.Configuration["DocBit:ApiKey"]
?? throw new InvalidOperationException("DocBit API key not configured");
return new DocBitClient(apiKey);
});
Middleware for Context
Copy
public class DocBitContextMiddleware
{
private readonly RequestDelegate _next;
public DocBitContextMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
if (context.User.Identity?.IsAuthenticated == true)
{
var user = context.User;
context.Items["DocBit.OrgId"] = user.FindFirst("tenant_id")?.Value;
context.Items["DocBit.UserId"] = user.FindFirst("sub")?.Value;
context.Items["DocBit.Roles"] = user.FindAll("role")
.Select(c => c.Value)
.ToArray();
}
await _next(context);
}
}
// Extension method
public static class DocBitContextMiddlewareExtensions
{
public static IApplicationBuilder UseDocBitContext(this IApplicationBuilder builder)
=> builder.UseMiddleware<DocBitContextMiddleware>();
}
Controller
Copy
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class AskController : ControllerBase
{
private readonly DocBitClient _client;
public AskController(DocBitClient client) => _client = client;
[HttpPost]
public async Task<ActionResult<ChatResponse>> Ask([FromBody] AskRequest request)
{
var orgId = HttpContext.Items["DocBit.OrgId"] as string
?? throw new UnauthorizedAccessException();
var userId = HttpContext.Items["DocBit.UserId"] as string
?? throw new UnauthorizedAccessException();
var roles = HttpContext.Items["DocBit.Roles"] as string[]
?? Array.Empty<string>();
try
{
var response = await _client.ChatAsync(
orgId, userId, roles,
request.Question,
request.ConversationId
);
return Ok(response);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.TooManyRequests)
{
return StatusCode(429, new { error = "Please try again later" });
}
}
}
public record AskRequest(string Question, string? ConversationId);
Error Handling
Copy
public async Task<ChatResponse> ChatWithRetryAsync(
string orgId,
string userId,
string[] roles,
string message,
int maxRetries = 3)
{
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
return await ChatAsync(orgId, userId, roles, message);
}
catch (HttpRequestException ex)
{
if (ex.StatusCode == HttpStatusCode.TooManyRequests)
{
var waitTime = (int)Math.Pow(2, attempt) * 1000;
Console.WriteLine($"Rate limited, waiting {waitTime}ms...");
await Task.Delay(waitTime);
continue;
}
if (ex.StatusCode == HttpStatusCode.Unauthorized)
{
throw new InvalidOperationException("Invalid API key");
}
if ((int?)ex.StatusCode >= 500)
{
var waitTime = (int)Math.Pow(2, attempt) * 1000;
await Task.Delay(waitTime);
continue;
}
throw;
}
}
throw new Exception("Max retries exceeded");
}
Dependency Injection with IHttpClientFactory
For production applications, useIHttpClientFactory:
Copy
// Program.cs
builder.Services.AddHttpClient<DocBitClient>(client =>
{
client.BaseAddress = new Uri("https://api.docbit.ai");
client.DefaultRequestHeaders.Add(
"Authorization",
$"ApiKey {builder.Configuration["DocBit:ApiKey"]}"
);
});
// Updated client
public class DocBitClient
{
private readonly HttpClient _httpClient;
public DocBitClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
// ... rest of implementation
}
Configuration
Copy
// appsettings.json
{
"DocBit": {
"ApiKey": "sk_yourpartner_abc123..."
}
}
Copy
// Use user secrets for development
dotnet user-secrets set "DocBit:ApiKey" "sk_yourpartner_abc123..."