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:
use Calco\Client;Quick start
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.
$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:
$scaffold = $client->scaffold('/tmp/template.xlsx');Managing templates
// 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:
$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:
$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:
$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:
$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:
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:
// 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:
'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:
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:
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.