Official SDK

PHP

PHP 8.0+ with PSR-18 HTTP client support. Works with Guzzle, Symfony HTTP Client, or any PSR-18 compatible client.

PHP 8.0+MIT licensed

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

  1. 1Install with composer require renamed/sdk and autoload
  2. 2Call rename(), pdfSplit(), or extract() with file contents
  3. 3Integrate with Laravel service container or any PSR-18 HTTP client

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

Installation

Shell
composer require renamed/sdk

Quickstart

Initialize the client and start making API calls.

example.php
PHP
<?phprequire_once 'vendor/autoload.php';use Renamed\Client;// Initialize the client$client = new Client(apiKey: getenv('RENAMED_API_KEY'));// Rename a PDF$result = $client->rename(file_get_contents('invoice.pdf'));echo $result->suggestedFilename;// -> "2024-01-15_AcmeCorp_INV-1234.pdf"// Split a multi-page PDF$job = $client->pdfSplit(    file_get_contents('documents.pdf'),    mode: 'smart');print_r($job->documents);// -> [["filename" => "...", "downloadUrl" => "..."], ...]// Extract structured data$data = $client->extract(file_get_contents('receipt.pdf'));print_r($data);// -> ["vendor" => "...", "date" => "...", "amount" => "..."]

Rename a PDF

Pass PDF bytes to get an AI-generated filename. Use custom instructions to control the naming format.

rename.php
PHP
<?phpdeclare(strict_types=1);require_once 'vendor/autoload.php';use Renamed\Client;use Renamed\Options\RenameOptions;// Initialize the client$client = new Client(apiKey: getenv('RENAMED_API_KEY'));// Read the PDF file$pdfBytes = file_get_contents('./documents/invoice.pdf');// Rename the PDF$result = $client->rename($pdfBytes);echo "Suggested filename: {$result->suggestedFilename}\n";echo "Confidence: {$result->confidence}\n";// Output:// Suggested filename: 2024-01-15_AcmeCorp_INV-1234.pdf// Confidence: 0.95// Access extracted metadataecho "Vendor: {$result->metadata['vendor']}\n";echo "Date: {$result->metadata['date']}\n";echo "Type: {$result->metadata['type']}\n";// Use custom naming instructions$customResult = $client->rename($pdfBytes, new RenameOptions(    instructions: 'Format: YYYY-MM-DD_VendorName_Amount'));echo $customResult->suggestedFilename;// -> "2024-01-15_AcmeCorp_$1250.pdf"// Rename from a URL$urlResult = $client->renameFromUrl('https://example.com/document.pdf');// Using a stream instead of loading entire file into memory$stream = fopen('./documents/large-invoice.pdf', 'rb');$streamResult = $client->rename($stream);fclose($stream);

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.

split.php
PHP
<?phpdeclare(strict_types=1);require_once 'vendor/autoload.php';use Renamed\Client;use Renamed\Options\SplitOptions;$client = new Client(apiKey: getenv('RENAMED_API_KEY'));// Read a multi-page PDF$pdfBytes = file_get_contents('./documents/combined.pdf');// Submit the split job with smart mode (AI detects document boundaries)$job = $client->pdfSplit($pdfBytes, new SplitOptions(mode: 'smart'));echo "Job ID: {$job->jobId}\n";echo "Status: {$job->state}\n";// Option 1: Wait for completion (built-in polling)$completed = $job->waitForCompletion();// Option 2: Manual polling for progress updates// while ($job->state === 'processing') {//     echo "Progress: {$job->progress}%\n";//     sleep(1);//     $job = $client->getJob($job->jobId);// }// Process the split documentsecho "Split into " . count($completed->documents) . " documents:\n";foreach ($completed->documents as $i => $doc) {    $num = $i + 1;    $pages = implode(', ', $doc->pages);    echo "  [{$num}] {$doc->filename} (pages {$pages})\n";    // Output:    // [1] invoice_1.pdf (pages 1, 2)    // [2] invoice_2.pdf (pages 3, 4)    // Download each split document    $docBytes = $client->downloadFile($doc->downloadUrl);    // Save to output directory    $outputPath = "./output/{$doc->filename}";    if (!is_dir('./output')) {        mkdir('./output', 0755, true);    }    file_put_contents($outputPath, $docBytes);    echo "  Saved: {$outputPath}\n";}// With a timeout (in seconds)$completed = $job->waitForCompletion(timeout: 300); // 5 minutes

Extract Data

Extract structured data from invoices, receipts, and other documents. Use PHP classes for type-safe extraction.

extract.php
PHP
<?phpdeclare(strict_types=1);require_once 'vendor/autoload.php';use Renamed\Client;// Define typed classes for invoice extractionreadonly class LineItem{    public function __construct(        public string $description,        public int $quantity,        public float $unitPrice,    ) {}    public static function fromArray(array $data): self    {        return new self(            description: $data['description'],            quantity: $data['quantity'],            unitPrice: $data['unitPrice'],        );    }}readonly class Invoice{    /**     * @param LineItem[] $lineItems     */    public function __construct(        public string $invoiceNumber,        public string $vendor,        public string $date,        public array $lineItems,        public float $subtotal,        public float $tax,        public float $total,    ) {}    public static function fromArray(array $data): self    {        return new self(            invoiceNumber: $data['invoiceNumber'],            vendor: $data['vendor'],            date: $data['date'],            lineItems: array_map(                fn($item) => LineItem::fromArray($item),                $data['lineItems'] ?? []            ),            subtotal: $data['subtotal'],            tax: $data['tax'],            total: $data['total'],        );    }}// Initialize the client$client = new Client(apiKey: getenv('RENAMED_API_KEY'));// Read the PDF file$pdfBytes = file_get_contents('./documents/invoice.pdf');// Basic extraction (returns associative array)$data = $client->extract($pdfBytes);echo "Vendor: {$data['vendor']}\n";echo "Date: {$data['date']}\n";echo "Total: {$data['total']}\n";// Output:// Vendor: Acme Corp// Date: 2024-01-15// Total: 1250.00// Typed extraction: convert to a class$invoice = Invoice::fromArray($data);// Now you have typed accessecho "Invoice #{$invoice->invoiceNumber} from {$invoice->vendor}\n";echo "Line items: " . count($invoice->lineItems) . "\n";foreach ($invoice->lineItems as $item) {    $price = number_format($item->unitPrice, 2);    echo "  - {$item->description}: {$item->quantity} x ${$price}\n";}echo "Total: $" . number_format($invoice->total, 2) . "\n";// 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.

Laravel Controller

Handle PDF uploads in a Laravel controller with proper validation and error handling.

app/Http/Controllers/Api/DocumentController.php
PHP
<?phpdeclare(strict_types=1);namespace App\Http\Controllers\Api;use App\Http\Controllers\Controller;use Illuminate\Http\JsonResponse;use Illuminate\Http\Request;use Renamed\Client;use Renamed\Options\SplitOptions;use Renamed\Exception\RateLimitException;use Renamed\Exception\ApiException;class DocumentController extends Controller{    public function __construct(        private readonly Client $renamedClient,    ) {}    public function rename(Request $request): JsonResponse    {        $request->validate([            'file' => 'required|file|mimes:pdf|max:10240', // 10MB max            'instructions' => 'nullable|string|max:500',        ]);        try {            $pdfBytes = $request->file('file')->getContent();            $options = null;            if ($request->has('instructions')) {                $options = new \Renamed\Options\RenameOptions(                    instructions: $request->input('instructions')                );            }            $result = $this->renamedClient->rename($pdfBytes, $options);            return response()->json([                'suggested_filename' => $result->suggestedFilename,                'confidence' => $result->confidence,                'metadata' => $result->metadata,            ]);        } catch (RateLimitException $e) {            return response()->json(                ['error' => 'Rate limited', 'retry_after' => $e->getRetryAfter()],                429,                ['Retry-After' => $e->getRetryAfter()]            );        } catch (ApiException $e) {            return response()->json(                ['error' => $e->getMessage(), 'code' => $e->getCode()],                502            );        }    }    public function split(Request $request): JsonResponse    {        $request->validate([            'file' => 'required|file|mimes:pdf|max:51200', // 50MB max            'mode' => 'nullable|string|in:smart,fixed,page',        ]);        try {            $pdfBytes = $request->file('file')->getContent();            $mode = $request->input('mode', 'smart');            $job = $this->renamedClient->pdfSplit(                $pdfBytes,                new SplitOptions(mode: $mode)            );            $completed = $job->waitForCompletion();            return response()->json([                'job_id' => $completed->jobId,                'documents' => array_map(fn($doc) => [                    'filename' => $doc->filename,                    'pages' => $doc->pages,                    'download_url' => $doc->downloadUrl,                ], $completed->documents),            ]);        } catch (ApiException $e) {            return response()->json(['error' => $e->getMessage()], 502);        }    }    public function extract(Request $request): JsonResponse    {        $request->validate([            'file' => 'required|file|mimes:pdf|max:10240',        ]);        try {            $pdfBytes = $request->file('file')->getContent();            $data = $this->renamedClient->extract($pdfBytes);            return response()->json($data);        } catch (ApiException $e) {            return response()->json(['error' => $e->getMessage()], 502);        }    }}

Laravel Service Provider

Register the SDK client in Laravel service container with configuration from environment.

app/Providers/RenamedServiceProvider.php
PHP
<?phpdeclare(strict_types=1);namespace App\Providers;use Illuminate\Support\ServiceProvider;use Renamed\Client;use Renamed\ClientOptions;class RenamedServiceProvider extends ServiceProvider{    public function register(): void    {        $this->app->singleton(Client::class, function ($app) {            return new Client(                apiKey: config('services.renamed.api_key'),                options: new ClientOptions(                    timeout: config('services.renamed.timeout', 30),                    maxRetries: config('services.renamed.max_retries', 3),                )            );        });    }    public function boot(): void    {        // Verify configuration on boot (optional)        if (empty(config('services.renamed.api_key'))) {            logger()->warning('[Renamed] API key not configured!');        }    }}// config/services.php// 'renamed' => [//     'api_key' => env('RENAMED_API_KEY'),//     'timeout' => env('RENAMED_TIMEOUT', 30),//     'max_retries' => env('RENAMED_MAX_RETRIES', 3),// ],

Laravel Queue Job

Process PDF files asynchronously using Laravel queues for background processing.

app/Jobs/RenameDocumentJob.php
PHP
<?phpdeclare(strict_types=1);namespace App\Jobs;use App\Models\Document;use App\Notifications\DocumentProcessed;use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Foundation\Bus\Dispatchable;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Queue\SerializesModels;use Renamed\Client;use Renamed\Options\RenameOptions;use Renamed\Exception\RateLimitException;use Renamed\Exception\ApiException;class RenameDocumentJob implements ShouldQueue{    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;    public int $tries = 5;    public int $backoff = 60;    public function __construct(        public readonly Document $document,        public readonly ?string $instructions = null,    ) {}    public function handle(Client $client): void    {        // Download the file from storage        $pdfBytes = Storage::get($this->document->file_path);        // Rename via the API        $options = $this->instructions            ? new RenameOptions(instructions: $this->instructions)            : null;        $result = $client->rename($pdfBytes, $options);        // Update the document record        $this->document->update([            'suggested_filename' => $result->suggestedFilename,            'confidence' => $result->confidence,            'metadata' => $result->metadata,            'processed_at' => now(),        ]);        // Notify the user        $this->document->user->notify(            new DocumentProcessed($this->document)        );    }    public function failed(\Throwable $exception): void    {        $this->document->update([            'processing_error' => $exception->getMessage(),            'processed_at' => now(),        ]);    }    public function retryUntil(): \DateTime    {        return now()->addMinutes(30);    }}// Dispatching the job// RenameDocumentJob::dispatch($document, 'Format: YYYY-MM-DD_VendorName');

Error Handling

The SDK throws typed exceptions that you can catch and handle appropriately. Use try-catch blocks for error handling.

error_handling.php
PHP
<?phpdeclare(strict_types=1);require_once 'vendor/autoload.php';use Renamed\Client;use Renamed\Exception\RateLimitException;use Renamed\Exception\InvalidFileException;use Renamed\Exception\AuthenticationException;use Renamed\Exception\ApiException;use Renamed\Exception\NetworkException;$client = new Client(apiKey: getenv('RENAMED_API_KEY'));try {    $pdfBytes = file_get_contents('invoice.pdf');    $result = $client->rename($pdfBytes);    echo "Renamed to: {$result->suggestedFilename}\n";} catch (RateLimitException $e) {    // Rate limited - wait and retry    echo "Rate limited. Retry after {$e->getRetryAfter()} seconds\n";    sleep($e->getRetryAfter());    // Retry the request...} catch (InvalidFileException $e) {    // File is not a valid PDF or is corrupted    echo "Invalid file: {$e->getMessage()}\n";} catch (AuthenticationException $e) {    // API key is invalid or expired    echo "Authentication failed: {$e->getMessage()}\n";} catch (ApiException $e) {    // Other API errors    echo "API Error [{$e->getCode()}]: {$e->getMessage()}\n";    if ($e->getDetails()) {        echo "Details: " . json_encode($e->getDetails()) . "\n";    }} catch (NetworkException $e) {    // Network/connection errors    echo "Network error: {$e->getMessage()}\n";} catch (\Exception $e) {    // Other errors (file I/O, etc.)    echo "Unexpected error: {$e->getMessage()}\n";}// Helper function with retry logicfunction renameWithRetry(Client $client, string $pdfBytes, int $maxRetries = 3): ?object{    $retries = 0;    while (true) {        try {            return $client->rename($pdfBytes);        } catch (RateLimitException $e) {            $retries++;            if ($retries > $maxRetries) {                throw $e;            }            $sleepTime = $e->getRetryAfter() ?? (2 ** $retries);            echo "Rate limited, retrying in {$sleepTime}s (attempt {$retries}/{$maxRetries})\n";            sleep($sleepTime);        } catch (ApiException $e) {            $retries++;            if ($retries > $maxRetries || !$e->isRetryable()) {                throw $e;            }            $sleepTime = 2 ** $retries;            echo "API error, retrying in {$sleepTime}s (attempt {$retries}/{$maxRetries})\n";            sleep($sleepTime);        }    }}

Full documentation on GitHub

For more examples, advanced usage patterns, and detailed API documentation, see the full PHP SDK README on GitHub.

Read the PHP SDK docs

Frequently asked questions

What PHP versions are supported?
The PHP SDK supports PHP 8.0 and above. It uses modern PHP features including named arguments and constructor property promotion.
What HTTP client does it use?
The SDK uses PSR-18 HTTP client interface, so it works with any compatible client like Guzzle, Symfony HTTP Client, or HTTPlug.
Does it work with Laravel?
Yes, the SDK works with Laravel and any PHP framework. We recommend using the configuration facade or service container for initialization.
Can I use streams instead of loading files into memory?
Yes, the SDK accepts both strings and stream resources. Use streams for large files to reduce memory usage.
How do I handle async processing?
For async processing in PHP, use queue systems like Laravel Queues, Symfony Messenger, or any job queue. The SDK provides polling helpers for long-running operations.

Related resources

Other languages