Skip to main content

C# / .NET Examples

Complete examples for integrating DocBit AI into your .NET application.

Installation

dotnet add package System.Net.Http.Json

Basic Client

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

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

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

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

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

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

// 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

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

[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

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, use IHttpClientFactory:
// 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

// appsettings.json
{
  "DocBit": {
    "ApiKey": "sk_yourpartner_abc123..."
  }
}
// Use user secrets for development
dotnet user-secrets set "DocBit:ApiKey" "sk_yourpartner_abc123..."