Official SDK

C# / .NET

.NET 6+ with async/await patterns. Designed for ASP.NET Core with built-in dependency injection support and proper error handling.

.NET 6+MIT licensed

Rename PDFs, split multi-page documents, and extract structured data using the official C# SDK for Renamed.to.

  1. 1Install Renamed.Sdk via dotnet add package or NuGet Package Manager
  2. 2Call RenameAsync(), PdfSplitAsync(), or ExtractAsync() with byte arrays
  3. 3Use dependency injection in ASP.NET Core with CancellationToken support

MIT licensed, open source on GitHub, published on NuGet.

Installation

Shell
dotnet add package Renamed.Sdk

Quickstart

Initialize the client and start making API calls.

Program.cs
C#
using Renamed;// Initialize the clientvar client = new RenamedClient(    apiKey: Environment.GetEnvironmentVariable("RENAMED_API_KEY"));// Rename a PDFvar pdfBytes = await File.ReadAllBytesAsync("invoice.pdf");var result = await client.RenameAsync(pdfBytes);Console.WriteLine(result.SuggestedFilename);// -> "2024-01-15_AcmeCorp_INV-1234.pdf"// Split a multi-page PDFvar docBytes = await File.ReadAllBytesAsync("documents.pdf");var job = await client.PdfSplitAsync(docBytes, new SplitOptions{    Mode = "smart"});Console.WriteLine(job.Documents);// Extract structured datavar receiptBytes = await File.ReadAllBytesAsync("receipt.pdf");var data = await client.ExtractAsync(receiptBytes);Console.WriteLine(data);

Rename a PDF

Pass a byte array to get an AI-generated filename. Use custom instructions to control the naming format.

RenameExample.cs
C#
using Renamed;using Renamed.Models;// Initialize the clientvar client = new RenamedClient(    apiKey: Environment.GetEnvironmentVariable("RENAMED_API_KEY"));// Read the PDF filevar pdfBytes = await File.ReadAllBytesAsync("./documents/invoice.pdf");// Rename the PDFvar result = await client.RenameAsync(pdfBytes);Console.WriteLine($"Suggested filename: {result.SuggestedFilename}");Console.WriteLine($"Confidence: {result.Confidence}");// Output:// Suggested filename: 2024-01-15_AcmeCorp_INV-1234.pdf// Confidence: 0.95// Access extracted metadataConsole.WriteLine($"Vendor: {result.Metadata["vendor"]}");Console.WriteLine($"Date: {result.Metadata["date"]}");Console.WriteLine($"Type: {result.Metadata["type"]}");// Use custom naming instructionsvar customResult = await client.RenameAsync(pdfBytes, new RenameOptions{    Instructions = "Format: YYYY-MM-DD_VendorName_Amount"});Console.WriteLine(customResult.SuggestedFilename);// -> "2024-01-15_AcmeCorp_$1250.pdf"// With cancellation token supportusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));var timedResult = await client.RenameAsync(pdfBytes, cancellationToken: cts.Token);

Split a PDF

Split multi-page PDFs into separate documents. The API uses async jobs for large files—poll for completion or use the built-in wait helper.

SplitExample.cs
C#
using Renamed;using Renamed.Models;var client = new RenamedClient(    apiKey: Environment.GetEnvironmentVariable("RENAMED_API_KEY"));// Read a multi-page PDFvar pdfBytes = await File.ReadAllBytesAsync("./documents/combined.pdf");// Submit the split job with smart mode (AI detects document boundaries)var job = await client.PdfSplitAsync(pdfBytes, new SplitOptions{    Mode = "smart"});Console.WriteLine($"Job ID: {job.JobId}");Console.WriteLine($"Status: {job.State}");// Option 1: Wait for completion (built-in polling)var completed = await job.WaitForCompletionAsync();// Option 2: Manual polling for progress updates// while (job.State == "processing")// {//     Console.WriteLine($"Progress: {job.Progress}%");//     await Task.Delay(1000);//     job = await client.GetJobAsync(job.JobId);// }// Process the split documentsConsole.WriteLine($"Split into {completed.Documents.Count} documents:");for (int i = 0; i < completed.Documents.Count; i++){    var doc = completed.Documents[i];    Console.WriteLine($"  [{i + 1}] {doc.Filename} (pages {string.Join(", ", doc.Pages)})");    // Output:    // [1] invoice_1.pdf (pages 1, 2)    // [2] invoice_2.pdf (pages 3, 4)    // Download each split document    var docBytes = await client.DownloadFileAsync(doc.DownloadUrl);    // Save to output directory    var outputPath = Path.Combine("./output", doc.Filename);    Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);    await File.WriteAllBytesAsync(outputPath, docBytes);}

Extract Data

Extract structured data from invoices, receipts, and other documents. Use C# records for type-safe extraction.

ExtractExample.cs
C#
using System.Text.Json;using Renamed;using Renamed.Models;// Define typed records for invoice extractionpublic record LineItem(string Description, int Quantity, decimal UnitPrice);public record Invoice(    string InvoiceNumber,    string Vendor,    string Date,    List<LineItem> LineItems,    decimal Subtotal,    decimal Tax,    decimal Total);// Initialize the clientvar client = new RenamedClient(    apiKey: Environment.GetEnvironmentVariable("RENAMED_API_KEY"));// Read the PDF filevar pdfBytes = await File.ReadAllBytesAsync("./documents/invoice.pdf");// Basic extraction (returns Dictionary<string, object>)var data = await client.ExtractAsync(pdfBytes);Console.WriteLine($"Vendor: {data["vendor"]}");Console.WriteLine($"Date: {data["date"]}");Console.WriteLine($"Total: {data["total"]}");// Output:// Vendor: Acme Corp// Date: 2024-01-15// Total: 1250.00// Typed extraction: deserialize to a recordvar jsonString = JsonSerializer.Serialize(data);var invoice = JsonSerializer.Deserialize<Invoice>(jsonString, new JsonSerializerOptions{    PropertyNameCaseInsensitive = true});// Now you have fully typed accessConsole.WriteLine($"Invoice #{invoice!.InvoiceNumber} from {invoice.Vendor}");Console.WriteLine($"Line items: {invoice.LineItems.Count}");foreach (var item in invoice.LineItems){    Console.WriteLine($"  - {item.Description}: {item.Quantity} x {item.UnitPrice:C}");}Console.WriteLine($"Total: {invoice.Total:C}");// Output:// Invoice #INV-2024-1234 from Acme Corp// Line items: 2//   - Widget Pro: 5 x $199.00//   - Support Plan: 1 x $342.50// Total: $1,337.50

Framework Integrations

Common integration patterns for popular frameworks.

ASP.NET Core Minimal API

Handle PDF uploads with ASP.NET Core minimal APIs for a lightweight, modern approach.

Program.cs
C#
using Renamed;using Renamed.Models;using Renamed.Exceptions;var builder = WebApplication.CreateBuilder(args);// Register the Renamed client with DIbuilder.Services.AddRenamedClient(options =>{    options.ApiKey = builder.Configuration["Renamed:ApiKey"]!;    options.Timeout = TimeSpan.FromSeconds(30);    options.MaxRetries = 3;});var app = builder.Build();// Rename endpointapp.MapPost("/api/rename", async (    IFormFile file,    IRenamedClient client,    CancellationToken ct) =>{    if (file.Length == 0)        return Results.BadRequest(new { error = "No file provided" });    if (file.ContentType != "application/pdf")        return Results.BadRequest(new { error = "Only PDF files are supported" });    try    {        using var stream = new MemoryStream();        await file.CopyToAsync(stream, ct);        var result = await client.RenameAsync(stream.ToArray(), cancellationToken: ct);        return Results.Ok(new        {            suggestedFilename = result.SuggestedFilename,            confidence = result.Confidence,            metadata = result.Metadata        });    }    catch (RenamedApiException e)    {        return Results.Problem(e.Message, statusCode: 502);    }});// Split endpointapp.MapPost("/api/split", async (    IFormFile file,    string? mode,    IRenamedClient client,    CancellationToken ct) =>{    if (file.Length == 0)        return Results.BadRequest(new { error = "No file provided" });    try    {        using var stream = new MemoryStream();        await file.CopyToAsync(stream, ct);        var job = await client.PdfSplitAsync(stream.ToArray(), new SplitOptions        {            Mode = mode ?? "smart"        }, ct);        var completed = await job.WaitForCompletionAsync(ct);        return Results.Ok(new        {            jobId = completed.JobId,            documents = completed.Documents.Select(d => new            {                filename = d.Filename,                pages = d.Pages,                downloadUrl = d.DownloadUrl            })        });    }    catch (RenamedApiException e)    {        return Results.Problem(e.Message, statusCode: 502);    }});// Extract endpointapp.MapPost("/api/extract", async (    IFormFile file,    IRenamedClient client,    CancellationToken ct) =>{    if (file.Length == 0)        return Results.BadRequest(new { error = "No file provided" });    try    {        using var stream = new MemoryStream();        await file.CopyToAsync(stream, ct);        var data = await client.ExtractAsync(stream.ToArray(), cancellationToken: ct);        return Results.Ok(data);    }    catch (RenamedApiException e)    {        return Results.Problem(e.Message, statusCode: 502);    }});app.Run();

ASP.NET Core Controller

Use traditional MVC controllers with dependency injection and proper validation.

DocumentsController.cs
C#
using Microsoft.AspNetCore.Mvc;using Renamed;using Renamed.Models;using Renamed.Exceptions;namespace MyApp.Controllers;[ApiController][Route("api/[controller]")]public class DocumentsController : ControllerBase{    private readonly IRenamedClient _client;    private readonly ILogger<DocumentsController> _logger;    public DocumentsController(IRenamedClient client, ILogger<DocumentsController> logger)    {        _client = client;        _logger = logger;    }    [HttpPost("rename")]    [RequestSizeLimit(10 * 1024 * 1024)] // 10MB limit    public async Task<IActionResult> Rename(        IFormFile file,        [FromQuery] string? instructions,        CancellationToken ct)    {        if (file.Length == 0)            return BadRequest(new { error = "No file provided" });        if (file.ContentType != "application/pdf")            return BadRequest(new { error = "Only PDF files are supported" });        try        {            using var stream = new MemoryStream();            await file.CopyToAsync(stream, ct);            var options = instructions != null                ? new RenameOptions { Instructions = instructions }                : null;            var result = await _client.RenameAsync(stream.ToArray(), options, ct);            _logger.LogInformation(                "Renamed {OriginalName} to {SuggestedName} with confidence {Confidence}",                file.FileName, result.SuggestedFilename, result.Confidence);            return Ok(new            {                suggestedFilename = result.SuggestedFilename,                confidence = result.Confidence,                metadata = result.Metadata            });        }        catch (RateLimitException e)        {            _logger.LogWarning("Rate limited, retry after {RetryAfter}s", e.RetryAfter);            Response.Headers.Append("Retry-After", e.RetryAfter.ToString());            return StatusCode(429, new { error = "Rate limited", retryAfter = e.RetryAfter });        }        catch (RenamedApiException e)        {            _logger.LogError(e, "API error during rename: {Code}", e.Code);            return StatusCode(502, new { error = e.Message, code = e.Code });        }    }    [HttpPost("split")]    [RequestSizeLimit(50 * 1024 * 1024)] // 50MB limit for multi-page PDFs    public async Task<IActionResult> Split(        IFormFile file,        [FromQuery] string mode = "smart",        CancellationToken ct = default)    {        if (file.Length == 0)            return BadRequest(new { error = "No file provided" });        try        {            using var stream = new MemoryStream();            await file.CopyToAsync(stream, ct);            var job = await _client.PdfSplitAsync(stream.ToArray(), new SplitOptions            {                Mode = mode            }, ct);            var completed = await job.WaitForCompletionAsync(ct);            return Ok(new            {                jobId = completed.JobId,                documents = completed.Documents.Select(d => new                {                    filename = d.Filename,                    pages = d.Pages,                    downloadUrl = d.DownloadUrl                })            });        }        catch (RenamedApiException e)        {            _logger.LogError(e, "API error during split: {Code}", e.Code);            return StatusCode(502, new { error = e.Message });        }    }    [HttpPost("extract")]    public async Task<IActionResult> Extract(IFormFile file, CancellationToken ct)    {        if (file.Length == 0)            return BadRequest(new { error = "No file provided" });        try        {            using var stream = new MemoryStream();            await file.CopyToAsync(stream, ct);            var data = await _client.ExtractAsync(stream.ToArray(), cancellationToken: ct);            return Ok(data);        }        catch (RenamedApiException e)        {            return StatusCode(502, new { error = e.Message });        }    }}

Dependency Injection Configuration

Configure the Renamed client as a service with options from configuration.

Program.cs
C#
using Renamed;var builder = WebApplication.CreateBuilder(args);// Option 1: Use AddRenamedClient extension methodbuilder.Services.AddRenamedClient(options =>{    options.ApiKey = builder.Configuration["Renamed:ApiKey"]!;    options.Timeout = TimeSpan.FromSeconds(30);    options.MaxRetries = 3;});// Option 2: Register manually with custom HttpClientbuilder.Services.AddHttpClient<IRenamedClient, RenamedClient>(client =>{    client.Timeout = TimeSpan.FromMinutes(5);}).ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler{    AutomaticDecompression = System.Net.DecompressionMethods.All});builder.Services.Configure<RenamedClientOptions>(    builder.Configuration.GetSection("Renamed"));// Option 3: Use IOptions pattern for configurationbuilder.Services.AddSingleton<IRenamedClient>(sp =>{    var options = sp.GetRequiredService<IOptions<RenamedClientOptions>>().Value;    var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient("Renamed");    return new RenamedClient(options, httpClient);});var app = builder.Build();// appsettings.json:// {//   "Renamed": {//     "ApiKey": "your-api-key",//     "Timeout": 30,//     "MaxRetries": 3//   }// }

Error Handling

The SDK throws typed exceptions that you can catch and handle appropriately. All async methods support CancellationToken.

ErrorHandling.cs
C#
using Renamed;using Renamed.Exceptions;var client = new RenamedClient(    apiKey: Environment.GetEnvironmentVariable("RENAMED_API_KEY"));try{    var pdfBytes = await File.ReadAllBytesAsync("invoice.pdf");    var result = await client.RenameAsync(pdfBytes);    Console.WriteLine($"Renamed to: {result.SuggestedFilename}");}catch (RateLimitException e){    // Rate limited - wait and retry    Console.WriteLine($"Rate limited. Retry after {e.RetryAfter} seconds");    await Task.Delay(TimeSpan.FromSeconds(e.RetryAfter));    // Retry the request...}catch (InvalidFileException e){    // File is not a valid PDF or is corrupted    Console.WriteLine($"Invalid file: {e.Message}");}catch (AuthenticationException e){    // API key is invalid or expired    Console.WriteLine($"Authentication failed: {e.Message}");}catch (RenamedApiException e){    // Other API errors    Console.WriteLine($"API Error [{e.Code}]: {e.Message}");    if (e.Details != null)    {        Console.WriteLine($"Details: {e.Details}");    }}catch (OperationCanceledException){    // Request was cancelled    Console.WriteLine("Request was cancelled");}catch (HttpRequestException e){    // Network errors    Console.WriteLine($"Network error: {e.Message}");}// Using CancellationToken for timeoutusing var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));try{    var pdfBytes = await File.ReadAllBytesAsync("large-document.pdf");    var result = await client.RenameAsync(pdfBytes, cancellationToken: cts.Token);}catch (OperationCanceledException) when (cts.IsCancellationRequested){    Console.WriteLine("Request timed out after 30 seconds");}

Full documentation on GitHub

For more examples, advanced usage patterns, and detailed API documentation, see the full C# / .NET SDK README on GitHub.

Read the C# / .NET SDK docs

Frequently asked questions

What .NET versions are supported?
The C# SDK supports .NET 6 and above, including .NET 7, .NET 8, and future versions. It also works with .NET Standard 2.1 compatible frameworks.
Does it support dependency injection?
Yes, the SDK provides extension methods for IServiceCollection to easily register the client in ASP.NET Core applications.
How do I handle cancellation?
All async methods accept a CancellationToken parameter. Pass your token to support graceful cancellation of API calls.
Is the client thread-safe?
Yes, the RenamedClient is thread-safe and designed to be used as a singleton. Register it with AddSingleton or use the AddRenamedClient extension.
How do I configure retries?
Set the MaxRetries option when configuring the client. The SDK uses exponential backoff for transient errors automatically.

Related resources

Other languages