app/Http/Controllers/EvidenceController.php
use Proxy\ProxyClient;
// Read from configuration. Never in the repository, never in a browser.
$proxy = new ProxyClient(
secretKey: config('services.proxy.secret'),
organisation: config('services.proxy.organisation'),
);
$file = $proxy->storage()->upload(
file: $request->file('evidence'),
metadata: ['claim' => $claim->reference],
);
// A job, so this returns as soon as the work is accepted. The
// idempotency key is yours: only you know which retry is which.
$job = $proxy->processor()->analyse(
file: $file->id,
tasks: ['ocr', 'classify', 'extract'],
idempotencyKey: "claim-{$claim->id}-evidence-{$file->id}",
);
$job->state; // 'queued'
Two calls. The second returns before the work is finished.
resources/js/evidence.js
import { PoweredByProxy } from '@proxy/browser';
const proxy = new PoweredByProxy({
publishableKey: 'pk_live_...',
baseUrl: 'https://api.proxy.example',
});
// The uploader takes no maxFiles option. Its limits are read off the
// token your backend minted, so the page cannot widen them.
await proxy.storage.mountUploader({
token: clientToken,
target: '#evidence',
});
A token for one capability and one claim. A secret key never reaches the page.
GET /api/v1/processor/jobs/01k5m8f2...
{
"data": {
"id": "01k5m8f2r7q0x3v9tzcd4npb6h",
"state": "completed",
"operation": "ocr.document",
"file": "01k5m7z4h9c1e8ptw2yb3rkq5d",
"tasks": ["ocr", "classify", "extract"],
"result": {
"classification": "invoice",
"pages": 3,
"fields": {
"invoice_number": "INV-40912",
"issued_on": "2026-08-14",
"total": "1284.60"
}
},
"usage": { "quantity": 1, "unit": "document", "cost": "15p" },
"actor": { "type": "integration", "name": "Claims portal" },
"completed_at": "2026-08-14T09:41:07+00:00"
}
}
The fields you asked for, the operation you were charged for, and the actor.
GET /api/v1/audit?resource=01k5m7z4...
{
"data": [
{
"event": "storage.object.created",
"resource": "01k5m7z4h9c1e8ptw2yb3rkq5d",
"actor": { "type": "integration", "name": "Claims portal" },
"recorded_at": "2026-08-14T09:40:58+00:00"
},
{
"event": "processor.job.completed",
"resource": "01k5m8f2r7q0x3v9tzcd4npb6h",
"usage": { "operation": "ocr.document", "quantity": 1, "status": "charged" },
"recorded_at": "2026-08-14T09:41:07+00:00"
}
]
}
The same two calls from the evidence side. The half that matters six months later.