Calco

PHP

The Calco client is a zero-dependency PHP connector. It requires PHP 8.1+ and the cURL extension.

Add the connector

There's no Composer package to require (yet) — the client is two small, dependency-free files. Copy Client.php and CalcoException.php (namespace Calco) into your project, then autoload them via PSR-4 or require them directly and use the Calco\Client class:

php
use Calco\Client;

Quick start

php
use Calco\Client;

$client = new Client('http://localhost:8080', apiKey: 'dc_live_...');

// Convert a local XLSX file to PDF
$pdf = $client->convert('/path/to/spreadsheet.xlsx', [
    'format' => 'pdf',
    'title'  => 'Monthly Report',
]);

file_put_contents('report.pdf', $pdf);

The constructor is new Client(baseUrl, timeout: 120, apiKey: null). The key is sent as Authorization: Bearer <key> on every request.

Converting a file

convert(string $filePath, array $options = []): string uploads a local file and returns the raw document bytes.

php
$bytes = $client->convert('/tmp/invoice.xlsx', [
    'format'    => 'pdf',      // pdf, html, csv, ods, xls
    'data_json' => '{"company":"Acme"}',
    'sheet'     => 'Sheet1',
    'title'     => 'Invoice',
]);

Extract the placeholder map with scaffold:

php
$scaffold = $client->scaffold('/tmp/template.xlsx');

Managing templates

php
// Create
$result = $client->createTemplate(
    filePath: '/tmp/invoice-template.xlsx',
    name:     'Invoice Template',
    category: 'billing',
    tags:     'invoice,pdf',
);
$id = $result['template']['id'];

// List
$response = $client->listTemplates();
foreach ($response['templates'] as $template) {
    echo $template['id'] . ' — ' . $template['name'] . PHP_EOL;
}

// Update metadata
$client->updateTemplate($id, ['name' => 'Updated Name', 'tags' => 'monthly,finance']);

// Add a new version
$client->addVersion($id, '/tmp/invoice-v2.xlsx');

// Scaffold for a specific version
$scaffold = $client->versionScaffold($id, 2);

// Delete
$client->deleteTemplate($id);

Rendering a stored template

Render via POST with renderTemplate, or fetch via GET with downloadVersion:

php
$pdf = $client->renderTemplate('abc-123', 2, ['name' => 'Jane', 'total' => 1500], [
    'format' => 'pdf',
    'title'  => 'Invoice for Jane',
]);

file_put_contents('jane-invoice.pdf', $pdf);

Batch rendering

Render multiple records and receive a ZIP archive:

php
$zip = $client->batchRender('abc-123', [
    ['name' => 'Alice', 'amount' => 100],
    ['name' => 'Bob',   'amount' => 250],
], [
    'version'        => 1,
    'format'         => 'pdf',
    'title_template' => 'invoice-{{name}}',
    'sheet'          => 'Sheet1',
]);

file_put_contents('batch.zip', $zip);

You can also pass an XLSX file directly instead of a template id:

php
$zip = $client->batchRender('', $records, [
    'file'           => '/tmp/my-template.xlsx',
    'format'         => 'pdf',
    'title_template' => 'report-{{index}}',
]);

By-hash workflow

Every stored version exposes a content hash (sha256 of the file). Address a version directly by hash:

php
$meta = $client->getTemplateByHash('3c9f...d1ab');
$pdf  = $client->renderByHash('3c9f...d1ab', ['customer' => 'Jane'], ['format' => 'pdf']);
$xlsx = $client->downloadByHash('3c9f...d1ab', ['format' => 'xlsx']);

Error handling

All methods throw CalcoException on HTTP errors. The message contains the API error text and the exception code is the HTTP status:

php
use Calco\CalcoException;

try {
    $client->convert('/tmp/bad-file.xlsx', ['format' => 'pdf']);
} catch (CalcoException $e) {
    echo 'Error ' . $e->getCode() . ': ' . $e->getMessage();
}

Framework integration

Laravel

Register the client as a singleton in a service provider:

php
// app/Providers/AppServiceProvider.php
use Calco\Client;

public function register(): void
{
    $this->app->singleton(Client::class, fn ($app) => new Client(
        baseUrl: config('services.calco.url', 'http://localhost:8080'),
        timeout: (int) config('services.calco.timeout', 120),
        apiKey:  config('services.calco.api_key'),
    ));
}

Add the config to config/services.php:

php
'calco' => [
    'url'     => env('CALCO_URL', 'http://localhost:8080'),
    'timeout' => env('CALCO_TIMEOUT', 120),
    'api_key' => env('CALCO_API_KEY'),
],

Then inject it into a controller:

php
use Calco\Client;
use Illuminate\Http\Response;

class ReportController extends Controller
{
    public function __construct(private readonly Client $compactor) {}

    public function download(Request $request): Response
    {
        $pdf = $this->compactor->renderTemplate(
            templateId: $request->input('template_id'),
            version:    (int) $request->input('version', 1),
            data:       $request->input('data', []),
            options:    ['format' => 'pdf'],
        );

        return response($pdf, 200, [
            'Content-Type'        => 'application/pdf',
            'Content-Disposition' => 'attachment; filename="report.pdf"',
        ]);
    }
}

Symfony

Define the service in config/services.yaml:

yaml
parameters:
    calco.base_url: '%env(CALCO_URL)%'
    calco.timeout: '%env(int:CALCO_TIMEOUT)%'

services:
    Calco\Client:
        arguments:
            $baseUrl: '%calco.base_url%'
            $timeout: '%calco.timeout%'

Then inject Calco\Client into any controller or service and call renderTemplate as above.