leadtext
leadtext
leadtext
Invoices, receipts, statements
Auto-title papers
Matter-ready naming
Field, onboarding, reporting ops
Shot logs, call sheets, deliverables
Configs, audit logs, incidents
Transcripts, lesson plans, records
Invoices, product sheets, shipping
Leases, inspections, work orders
Receipts, reports, reimbursements
BOLs, PODs, carrier invoices
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.
Rename PDFs, split multi-page documents, and extract structured data using the official C# SDK for Renamed.to.
- 1Install Renamed.Sdk via dotnet add package or NuGet Package Manager
- 2Call RenameAsync(), PdfSplitAsync(), or ExtractAsync() with byte arrays
- 3Use dependency injection in ASP.NET Core with CancellationToken support
On this page
Installation
dotnet add package Renamed.SdkInstall-Package Renamed.SdkQuickstart
Initialize the client and start making API calls.
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.
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.
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.
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.50Framework 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.
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.
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.
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.
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 docsFrequently 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.