filecheck/filecheck-php

Filecheck server SDK for PHP — jobs, uploads, webhooks, and server-side job verification

Maintainers

Package info

github.com/PrintApp/filecheck-php

Homepage

pkg:composer/filecheck/filecheck-php

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-07-30 20:08 UTC

This package is auto-updated.

Last update: 2026-07-30 20:27:15 UTC


README

Filecheck server SDK for PHP 8.1+ — jobs, uploads, webhooks, and the server-side job verification your fulfillment path must run before trusting a browser-submitted job id. Mirrors filecheck-node method-for-method.

HTTP via any PSR-18 client (auto-discovered with php-http/discovery, or injected), with a bundled cURL fallback so the package works with zero extra dependencies.

Install

composer require filecheck/filecheck-php

Quickstart — verify a job in 5 lines

$fc = new \Filecheck\FilecheckClient(getenv('FILECHECK_SECRET_KEY'));

$result = $fc->jobs->verify($_POST['filecheck_job_id'], ['workflow_id' => 'wf_…']);
if (!$result->ok) {
    http_response_code(422);
    exit("Files not accepted ({$result->reason})");
}
// fulfil the order; $result->state is 'ready' or 'partial' (warnings accepted by policy)

Laravel

// AppServiceProvider
$this->app->singleton(FilecheckClient::class,
    fn () => new FilecheckClient(config('services.filecheck.secret')));

// CheckoutController
public function store(Request $request, FilecheckClient $fc)
{
    $result = $fc->jobs->verify($request->input('filecheck_job_id'), ['workflow_id' => 'wf_…']);
    abort_unless($result->ok, 422, "Files not accepted ({$result->reason})");
    // …
}

verify() is the encoded docs checklist: the job must be terminal (status ∈ done|skipped|error), proceedable (the browser Element's canProceed equivalent — ready/partial after applying the job's onFail policy), and — with workflow_id — must have run the expected workflow. Options: policy (override the resolved onFail), strict (fail closed on status: 'error' jobs).

Uploading + processing files

// Two-leg upload (presign + S3) in one call → fileRef
$upload = $fc->uploads->create('/tmp/artwork.pdf', ['mime_type' => 'application/pdf']);

// Validate against PDF/X profiles — waits for the result by default
$res = $fc->jobs->validate(['sources' => [['fileRef' => $upload->fileRef, 'profile' => ['1b']]]]);
echo $res->job->outcome;

// Preflight + autofix — async by default; 'wait' => true to block until terminal
$fixed = $fc->jobs->fix([
    'sources' => [['fileRef' => $upload->fileRef, 'profileId' => 'default']],
    'wait' => true,
]);

The wait option

The raw API inconsistently flips sync/async with sync: true (create/preflight/previews/fix — async default) and async: true (validate/optimize — sync default). The SDK normalizes all six behind 'wait' / 'wait_timeout' (seconds, default 120) with defaults matching each endpoint. When the server's ~27 s wait budget expires (HTTP 202), the SDK keeps polling GET /jobs/{id} client-side. Every submit returns a JobResult { job, pending }.

Blocking waits tie up the PHP worker — mind FPM time limits; prefer webhooks for long jobs.

API surface

Method Endpoint
$fc->jobs->create($params) POST /jobs
$fc->jobs->preflight/previews/fix/validate/optimize($params) POST /jobs/… sugar endpoints
$fc->jobs->retrieve($id) GET /jobs/{id}Data\Job
$fc->jobs->retrieveRuns($id) GET /jobs/{id}?expand=runsData\JobRuns
$fc->jobs->list(['limit', 'next_key']) / ->iterate() GET /jobs (+ auto-pagination)
$fc->jobs->delete($id) DELETE /jobs/{id}
$fc->jobs->waitUntilTerminal($id, $opts) polling helper
$fc->jobs->verify($id, $opts) fulfillment gate → Data\VerifyResult
$fc->uploads->create($pathOrBytesOrStream, $opts) POST /uploads + S3 leg → Data\Upload
$fc->orders->create($orderId, $params) POST /orders/{id}
$fc->workflows/connectors/rules/profiles/optimizePresets->all()/->get($id) read-only library
Webhooks::constructEvent($rawBody, $sig, $secret, $opts) webhook parsing/verification

Client options: new FilecheckClient('sk_…', ['base_url', 'timeout' /* seconds */, 'max_retries', 'http_client' /* PSR-18 */, 'transport']). Secret keys are sk_…; passing a publishable pk_… key throws immediately. Keys are never echoed in full — masked to sk_…abc4.

Responses are readonly value objects (Data\Job, Data\Task, Data\Step, Data\JobRuns, Data\VerifyResult, …) that keep the raw payload in ->raw for forward compatibility.

Webhooks

Signing status: Filecheck's per-job job.completed webhooks are delivered unsigned today; the signature scheme is not finalized. constructEvent is structured so verification becomes the default without breaking changes once it ships. Until then, ['verify' => false] is the explicit, temporary escape hatch.

$event = \Filecheck\Webhook\Webhooks::constructEvent(
    file_get_contents('php://input'),          // ALWAYS the raw body
    $_SERVER['HTTP_X_FILECHECK_SIGNATURE'] ?? null,
    null,
    ['verify' => false], // TEMPORARY until Filecheck ships webhook signing
);
if ($event->type === \Filecheck\Data\WebhookEvent::TYPE_JOB_COMPLETED) {
    // $event->payload: id, status, outcome, taskIds, tasks[], finalized, …
}

In Laravel, exempt the route from CSRF and use $request->getContent() for the raw body. Verification uses hash_equals() (constant-time compare).

Errors & retries

Typed exceptions in Filecheck\Exception: AuthenticationException (401/403 — including API Gateway's bare {"message":"Forbidden"} shape), InvalidRequestException (400), NotFoundException (404), RateLimitException (429), ApiException (5xx / non-JSON bodies), ConnectionException (network/timeout), WebhookSignatureException. The API has no machine-readable error codes — branch on exception class, not message text.

Retries: idempotent GETs only (default 2×, full-jitter backoff, Retry-After honored) on 429/502/503/504 and connection failures. POSTs are never auto-retried — the API has no idempotency keys, and a duplicated POST /jobs creates and bills a second job.

Development

composer install
composer test       # PHPUnit
composer phpstan    # level 8

Test fixtures in fixtures/ are a synced copy of the shared fixtures in the filecheck-integrations monorepo, so this SDK and filecheck assert against identical payloads.

License

MIT