Official SDK
Java
Java 11+ with CompletableFuture async support. Designed for enterprise applications with Spring Boot integration and automatic retries.
Rename PDFs, split multi-page documents, and extract structured data using the official Java SDK for Renamed.to.
- 1Add to.renamed:renamed-sdk via Maven or Gradle and build the client
- 2Call rename(), pdfSplit(), or extract() with byte arrays or use async variants
- 3Integrate with Spring Boot or use CompletableFuture for non-blocking operations
MIT licensed, open source on GitHub, published on Maven Central.
On this page
Installation
<dependency> <groupId>to.renamed</groupId> <artifactId>renamed-sdk</artifactId> <version>0.1.0</version></dependency>Quickstart
Initialize the client and start making API calls.
import to.renamed.RenamedClient;import to.renamed.model.*;import java.nio.file.Files;import java.nio.file.Path;public class Main { public static void main(String[] args) throws Exception { // Initialize the client var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .build(); // Rename a PDF byte[] pdfBytes = Files.readAllBytes(Path.of("invoice.pdf")); RenameResult result = client.rename(pdfBytes); System.out.println(result.getSuggestedFilename()); // -> "2024-01-15_AcmeCorp_INV-1234.pdf" // Split a multi-page PDF byte[] docBytes = Files.readAllBytes(Path.of("documents.pdf")); SplitJob job = client.pdfSplit(docBytes, SplitOptions.builder() .mode("smart") .build()); System.out.println(job.getDocuments()); // Extract structured data byte[] receiptBytes = Files.readAllBytes(Path.of("receipt.pdf")); var data = client.extract(receiptBytes); System.out.println(data); }}Rename a PDF
Pass a byte array to get an AI-generated filename. Use custom instructions to control the naming format.
import to.renamed.RenamedClient;import to.renamed.model.RenameOptions;import to.renamed.model.RenameResult;import to.renamed.exception.RenamedApiException;import java.nio.file.Files;import java.nio.file.Path;public class RenameExample { public static void main(String[] args) { // Initialize the client var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .build(); try { // Read the PDF file byte[] pdfBytes = Files.readAllBytes( Path.of("./documents/invoice.pdf") ); // Rename the PDF RenameResult result = client.rename(pdfBytes); System.out.println("Suggested filename: " + result.getSuggestedFilename()); System.out.println("Confidence: " + result.getConfidence()); // Output: // Suggested filename: 2024-01-15_AcmeCorp_INV-1234.pdf // Confidence: 0.95 // Access extracted metadata var metadata = result.getMetadata(); System.out.println("Vendor: " + metadata.get("vendor")); System.out.println("Date: " + metadata.get("date")); System.out.println("Type: " + metadata.get("type")); // Use custom naming instructions RenameResult customResult = client.rename(pdfBytes, RenameOptions.builder() .instructions("Format: YYYY-MM-DD_VendorName_Amount") .build()); System.out.println(customResult.getSuggestedFilename()); // -> "2024-01-15_AcmeCorp_$1250.pdf" } catch (RenamedApiException e) { System.err.println("API Error: " + e.getCode() + " - " + e.getMessage()); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } }}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.
import to.renamed.RenamedClient;import to.renamed.model.SplitOptions;import to.renamed.model.SplitJob;import to.renamed.model.SplitDocument;import to.renamed.exception.RenamedApiException;import java.nio.file.Files;import java.nio.file.Path;import java.time.Duration;import java.util.concurrent.TimeUnit;public class SplitExample { public static void main(String[] args) { var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .timeout(Duration.ofMinutes(5)) .build(); try { // Read a multi-page PDF byte[] pdfBytes = Files.readAllBytes( Path.of("./documents/combined.pdf") ); // Submit the split job with smart mode (AI detects document boundaries) SplitJob job = client.pdfSplit(pdfBytes, SplitOptions.builder() .mode("smart") .build()); System.out.println("Job ID: " + job.getJobId()); System.out.println("Status: " + job.getState()); // Option 1: Wait for completion (built-in polling) SplitJob completed = job.waitForCompletion(); // Option 2: Manual polling for progress updates // while (job.getState().equals("processing")) { // System.out.println("Progress: " + job.getProgress() + "%"); // TimeUnit.SECONDS.sleep(1); // job = client.getJob(job.getJobId()); // } // Process the split documents System.out.println("Split into " + completed.getDocuments().size() + " documents:"); for (int i = 0; i < completed.getDocuments().size(); i++) { SplitDocument doc = completed.getDocuments().get(i); System.out.printf(" [%d] %s (pages %s)%n", i + 1, doc.getFilename(), doc.getPages()); // Output: // [1] invoice_1.pdf (pages [1, 2]) // [2] invoice_2.pdf (pages [3, 4]) // Download each split document byte[] docBytes = client.downloadFile(doc.getDownloadUrl()); // Save to output directory Path outputPath = Path.of("./output", doc.getFilename()); Files.createDirectories(outputPath.getParent()); Files.write(outputPath, docBytes); } } catch (RenamedApiException e) { System.err.println("API Error: " + e.getCode() + " - " + e.getMessage()); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } }}Extract Data
Extract structured data from invoices, receipts, and other documents. Define Java records for type-safe extraction.
import to.renamed.RenamedClient;import to.renamed.model.ExtractOptions;import to.renamed.exception.RenamedApiException;import com.fasterxml.jackson.databind.ObjectMapper;import java.nio.file.Files;import java.nio.file.Path;import java.util.List;import java.util.Map;public class ExtractExample { // Define typed records for invoice extraction public record LineItem( String description, int quantity, double unitPrice ) {} public record Invoice( String invoiceNumber, String vendor, String date, List<LineItem> lineItems, double subtotal, double tax, double total ) {} public static void main(String[] args) { var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .build(); var objectMapper = new ObjectMapper(); try { // Read the PDF file byte[] pdfBytes = Files.readAllBytes( Path.of("./documents/invoice.pdf") ); // Basic extraction (returns Map<String, Object>) Map<String, Object> data = client.extract(pdfBytes); System.out.println("Vendor: " + data.get("vendor")); System.out.println("Date: " + data.get("date")); System.out.println("Total: " + data.get("total")); // Output: // Vendor: Acme Corp // Date: 2024-01-15 // Total: 1250.00 // Typed extraction: convert to a record Invoice invoice = objectMapper.convertValue(data, Invoice.class); // Now you have fully typed access System.out.printf("Invoice #%s from %s%n", invoice.invoiceNumber(), invoice.vendor()); System.out.println("Line items: " + invoice.lineItems().size()); for (LineItem item : invoice.lineItems()) { System.out.printf(" - %s: %d x $%.2f%n", item.description(), item.quantity(), item.unitPrice()); } System.out.printf("Total: $%.2f%n", invoice.total()); // Output: // Invoice #INV-2024-1234 from Acme Corp // Line items: 2 // - Widget Pro: 5 x $199.00 // - Support Plan: 1 x $342.50 // Total: $1337.50 } catch (RenamedApiException e) { System.err.println("API Error: " + e.getCode() + " - " + e.getMessage()); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); } }}Framework Integrations
Common integration patterns for popular frameworks.
Spring Boot RestController
Handle PDF uploads in a Spring Boot application with proper validation and error handling.
package com.example.demo.controller;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import org.springframework.web.multipart.MultipartFile;import to.renamed.RenamedClient;import to.renamed.model.RenameResult;import to.renamed.model.SplitJob;import to.renamed.model.SplitOptions;import to.renamed.exception.RenamedApiException;import java.io.IOException;import java.util.List;import java.util.Map;@RestController@RequestMapping("/api/documents")public class DocumentController { private final RenamedClient renamedClient; public DocumentController(RenamedClient renamedClient) { this.renamedClient = renamedClient; } @PostMapping("/rename") public ResponseEntity<?> renameDocument(@RequestParam("file") MultipartFile file) { // Validate file type if (file.isEmpty()) { return ResponseEntity.badRequest() .body(Map.of("error", "No file provided")); } String contentType = file.getContentType(); if (contentType == null || !contentType.equals("application/pdf")) { return ResponseEntity.badRequest() .body(Map.of("error", "Only PDF files are supported")); } try { byte[] pdfBytes = file.getBytes(); RenameResult result = renamedClient.rename(pdfBytes); return ResponseEntity.ok(Map.of( "suggestedFilename", result.getSuggestedFilename(), "confidence", result.getConfidence(), "metadata", result.getMetadata() )); } catch (RenamedApiException e) { return ResponseEntity.status(502) .body(Map.of("error", e.getMessage(), "code", e.getCode())); } catch (IOException e) { return ResponseEntity.status(500) .body(Map.of("error", "Failed to read file")); } } @PostMapping("/split") public ResponseEntity<?> splitDocument( @RequestParam("file") MultipartFile file, @RequestParam(value = "mode", defaultValue = "smart") String mode) { if (file.isEmpty()) { return ResponseEntity.badRequest() .body(Map.of("error", "No file provided")); } try { byte[] pdfBytes = file.getBytes(); SplitJob job = renamedClient.pdfSplit(pdfBytes, SplitOptions.builder() .mode(mode) .build()); // Wait for completion SplitJob completed = job.waitForCompletion(); return ResponseEntity.ok(Map.of( "jobId", completed.getJobId(), "documents", completed.getDocuments().stream() .map(doc -> Map.of( "filename", doc.getFilename(), "pages", doc.getPages(), "downloadUrl", doc.getDownloadUrl() )) .toList() )); } catch (RenamedApiException e) { return ResponseEntity.status(502) .body(Map.of("error", e.getMessage())); } catch (IOException e) { return ResponseEntity.status(500) .body(Map.of("error", "Failed to read file")); } } @PostMapping("/extract") public ResponseEntity<?> extractData(@RequestParam("file") MultipartFile file) { if (file.isEmpty()) { return ResponseEntity.badRequest() .body(Map.of("error", "No file provided")); } try { byte[] pdfBytes = file.getBytes(); Map<String, Object> data = renamedClient.extract(pdfBytes); return ResponseEntity.ok(data); } catch (RenamedApiException e) { return ResponseEntity.status(502) .body(Map.of("error", e.getMessage())); } catch (IOException e) { return ResponseEntity.status(500) .body(Map.of("error", "Failed to read file")); } }}Spring Boot Configuration
Configure the Renamed client as a Spring Bean with properties-based configuration.
package com.example.demo.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import to.renamed.RenamedClient;import java.time.Duration;@Configurationpublic class RenamedConfig { @Bean public RenamedClient renamedClient( @Value("${renamed.api-key}") String apiKey, @Value("${renamed.timeout:30}") int timeoutSeconds, @Value("${renamed.max-retries:3}") int maxRetries) { return RenamedClient.builder() .apiKey(apiKey) .timeout(Duration.ofSeconds(timeoutSeconds)) .maxRetries(maxRetries) .build(); }}// application.properties or application.yml:// renamed.api-key=${RENAMED_API_KEY}// renamed.timeout=30// renamed.max-retries=3Async with CompletableFuture
Use CompletableFuture for non-blocking operations and parallel processing.
import to.renamed.RenamedClient;import to.renamed.model.RenameResult;import to.renamed.model.SplitJob;import to.renamed.model.SplitOptions;import java.nio.file.Files;import java.nio.file.Path;import java.util.List;import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class AsyncExample { public static void main(String[] args) throws Exception { var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .build(); // Single async operation byte[] pdfBytes = Files.readAllBytes(Path.of("invoice.pdf")); CompletableFuture<RenameResult> future = client.renameAsync(pdfBytes); future .thenAccept(result -> { System.out.println("Renamed to: " + result.getSuggestedFilename()); System.out.println("Confidence: " + result.getConfidence()); }) .exceptionally(e -> { System.err.println("Error: " + e.getMessage()); return null; }); // Process multiple files in parallel List<Path> files = List.of( Path.of("doc1.pdf"), Path.of("doc2.pdf"), Path.of("doc3.pdf") ); List<CompletableFuture<RenameResult>> futures = files.stream() .map(path -> { try { byte[] bytes = Files.readAllBytes(path); return client.renameAsync(bytes); } catch (Exception e) { return CompletableFuture.<RenameResult>failedFuture(e); } }) .toList(); // Wait for all to complete CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenRun(() -> { futures.forEach(f -> { try { RenameResult result = f.join(); System.out.println("Result: " + result.getSuggestedFilename()); } catch (Exception e) { System.err.println("Failed: " + e.getMessage()); } }); }) .join(); // Async PDF split with progress monitoring byte[] combinedPdf = Files.readAllBytes(Path.of("combined.pdf")); client.pdfSplitAsync(combinedPdf, SplitOptions.builder().mode("smart").build()) .thenCompose(job -> { System.out.println("Job started: " + job.getJobId()); return job.waitForCompletionAsync(); }) .thenAccept(completed -> { System.out.println("Split into " + completed.getDocuments().size() + " documents"); completed.getDocuments().forEach(doc -> System.out.println(" - " + doc.getFilename()) ); }) .exceptionally(e -> { System.err.println("Split failed: " + e.getMessage()); return null; }) .join(); }}Error Handling
The SDK throws typed exceptions that you can catch and handle appropriately. Use try-catch for synchronous calls or handle CompletableFuture exceptions for async.
import to.renamed.RenamedClient;import to.renamed.model.RenameResult;import to.renamed.exception.RateLimitException;import to.renamed.exception.RenamedApiException;import to.renamed.exception.InvalidFileException;import to.renamed.exception.AuthenticationException;import java.nio.file.Files;import java.nio.file.Path;import java.util.concurrent.CompletableFuture;import java.util.concurrent.TimeUnit;public class ErrorHandling { public static void main(String[] args) { var client = RenamedClient.builder() .apiKey(System.getenv("RENAMED_API_KEY")) .build(); try { byte[] pdfBytes = Files.readAllBytes(Path.of("invoice.pdf")); RenameResult result = client.rename(pdfBytes); System.out.println("Renamed to: " + result.getSuggestedFilename()); } catch (RateLimitException e) { // Rate limited - wait and retry System.out.printf("Rate limited. Retry after %d seconds%n", e.getRetryAfter()); try { TimeUnit.SECONDS.sleep(e.getRetryAfter()); // Retry the request... } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } catch (InvalidFileException e) { // File is not a valid PDF or is corrupted System.out.println("Invalid file: " + e.getMessage()); } catch (AuthenticationException e) { // API key is invalid or expired System.out.println("Authentication failed: " + e.getMessage()); } catch (RenamedApiException e) { // Other API errors System.out.printf("API Error [%s]: %s%n", e.getCode(), e.getMessage()); if (e.getDetails() != null) { System.out.println("Details: " + e.getDetails()); } } catch (Exception e) { // Network errors, file I/O errors, etc. System.out.println("Unexpected error: " + e.getMessage()); } // Async error handling with CompletableFuture CompletableFuture<RenameResult> future = client.renameAsync(new byte[0]); future.handle((result, throwable) -> { if (throwable != null) { Throwable cause = throwable.getCause(); if (cause instanceof RateLimitException rle) { System.out.println("Async rate limited: " + rle.getRetryAfter()); } else if (cause instanceof RenamedApiException rae) { System.out.println("Async API error: " + rae.getMessage()); } else { System.out.println("Async error: " + throwable.getMessage()); } return null; } System.out.println("Async success: " + result.getSuggestedFilename()); return result; }); }}Full documentation on GitHub
For more examples, advanced usage patterns, and detailed API documentation, see the full Java SDK README on GitHub.
Read the Java SDK docsFrequently asked questions
- What Java versions are supported?
- The Java SDK supports Java 11 and above. It uses modern Java features like records and CompletableFuture for async operations.
- Does it work with Spring Boot?
- Yes, the SDK integrates seamlessly with Spring Boot. You can configure it as a Bean and inject it where needed.
- Which build tools are supported?
- The SDK is available on Maven Central and works with Maven, Gradle, and any other build tool that supports Maven repositories.
- How do I handle async operations?
- All methods have async variants that return CompletableFuture. Use thenAccept(), thenCompose(), or handle() for chaining operations.
- Is the client thread-safe?
- Yes, the RenamedClient is thread-safe and can be shared across threads. Use a single instance per application.