Constaia
SDKs

PHP SDK

Reference for constaia/constaia-php on PHP 8.1+: Composer install, inputs, options, methods, pagination, exceptions, retries and webhooks.

Cette page n'est pas encore traduite dans votre langue. Voici la version anglaise.

constaia/constaia-php is the official PHP SDK. It requires PHP ≥ 8.1 with ext-curl and ext-json; no Guzzle or other dependencies. It includes a Laravel integration (auto-discovered). Current version: 0.2.0.

composer require constaia/constaia-php

The key lives on the server

Never print the key in a page or pass it to JavaScript. In the browser, use the widget pointing at a route of your backend.

Configuration

bootstrap.php
<?php
require __DIR__ . '/vendor/autoload.php';

$constaia = new \Constaia\Client(null, [ // null → reads CONSTAIA_API_KEY
    'base_url' => 'https://api.constaia.com', // or CONSTAIA_BASE_URL
    'timeout' => 60,                          // seconds per attempt
    'max_retries' => 2,                       // on 408, 409, 429, 5xx and network errors
    'max_concurrency' => 4,                   // parallel requests in analyzeMany()
    'headers' => [],                          // extra headers on every request
]);

Without a key (argument or CONSTAIA_API_KEY), the constructor throws AuthenticationException with code missing_api_key.

Accepted files

InputSent as
Local path '/tmp/id.jpg'multipart (file + options JSON)
Open stream (fopen(…), php://memory)multipart
SplFileInfo, including Laravel or Symfony UploadedFile (uses the original name and type)multipart
URL 'https://…'JSON file_url
['base64' => '…', 'filename' => 'id.jpg']JSON file_base64 + filename
['file_url' => 'https://…']JSON file_url

Use the filename option to force the name (useful with streams and, in test mode, to pick the scenario).

Options

Options use the same snake_case names as the API. Every method also accepts request options that are not sent to the API: filename, idempotency_key, timeout, max_retries and headers.

verify.php
$analysis = $constaia->analyze($path, [
    'expect' => ['es_dni', 'es_nie', 'passport'], // without expect, verdict is null
    'extract' => true,                            // or your own JSON Schema
    'checks' => [
        'not_expired' => true,
        'min_age_years' => 18,
        'holder' => ['full_name' => 'María García López', 'document_number' => '12345678Z'],
    ],
    'storage' => 'none',                          // none | temporary | persistent
    'ttl_hours' => 24,                            // with storage=temporary
    'keep_results' => true,
    'async' => false,                             // true → 202 and webhook
    'export' => ['xlsx'],                         // signed URLs in $analysis->exports
    'metadata' => ['registration_id' => '123'],
    'language' => 'en',                           // es | en | pt | fr

    // Request options (not sent to the API)
    'filename' => 'dni_valid.jpg',
    'idempotency_key' => 'registration-123-id',
    'timeout' => 90,
]);

Every option is explained in POST /v1/analyze and Checks.

Reading the result

analyze() returns a Constaia\Analysis. Objects can be read as properties or as arrays; a missing key returns null without warnings.

$analysis->verdict->status;            // "valid" | "invalid" | "review"
$analysis['verdict']['status'];        // same, array style
$analysis->fields->document_number->value;

$analysis->verdictStatus();            // null if you sent no expect
$analysis->isValid();
$analysis->isInvalid();
$analysis->needsReview();
$analysis->isCompleted();              // false if the API answered 202
$analysis->field('birth_date');        // shortcut for fields.birth_date.value

foreach ($analysis->verdict->reasons as $reason) {
    echo $reason->code, ' (', $reason->severity, '): ', $reason->message, PHP_EOL;
}

$analysis->toArray();
json_encode($analysis);                // JsonSerializable

$constaia->lastResponse->status;       // HTTP status of the last call
$constaia->lastResponse->requestId;    // X-Request-Id: quote it when contacting support

Methods

// Classify (0.2 credits)
$c = $constaia->classify($input, ['expect' => 'invoice']);
$c->document->type;
$c->candidates[0]->confidence;

// Stored analyses
$constaia->analyses->get('an_01J…');
$page = $constaia->analyses->list([
    'limit' => 50,
    'status' => 'completed',
    'type' => 'es_dni',
    'metadata' => ['registration_id' => '123'],
]);
$constaia->analyses->delete('an_01J…');                          // { id, object, deleted: true }
$bytes = $constaia->analyses->export('an_01J…', 'xlsx');         // bytes
$constaia->analyses->export('an_01J…', 'csv', '/tmp/an.csv');    // saves to disk and returns the path

// Batches (up to 100 documents, always asynchronous)
$batch = $constaia->batches->create([
    'items' => [
        ['file_url' => 'https://example.com/a.pdf'],
        ['file_url' => 'https://example.com/b.pdf', 'options' => ['checks' => ['expected_amount' => 45]]],
    ],
    'options' => ['expect' => 'payment_receipt', 'export' => ['xlsx']],
]);
$batch = $constaia->batches->create([
    'files' => ['/tmp/a.pdf', '/tmp/b.pdf'],                      // multipart files[]
    'options' => ['expect' => 'invoice'],
]);
$constaia->batches->get($batch->id);

// Catalogue
$constaia->documentTypes->list(['language' => 'en']);
$constaia->documentTypes->get('es_dni');

// Webhook endpoints (the secret is only returned on creation)
$endpoint = $constaia->webhookEndpoints->create([
    'url' => 'https://example.com/webhooks/constaia',
    'events' => ['analysis.completed', 'analysis.review_required', 'batch.completed'],
]);
$endpoint->secret; // whsec_…
$constaia->webhookEndpoints->list();
$constaia->webhookEndpoints->get('we_01J…');
$constaia->webhookEndpoints->delete('we_01J…');

// Balance and usage
$constaia->balance();
$constaia->usage(['from' => '2026-09-01', 'to' => '2026-09-30']);

Several documents in parallel

analyzeMany() analyzes several documents at once with curl_multi, with at most max_concurrency requests in flight (default 4). It preserves the array keys and, instead of throwing, returns the exception at the position of the document that failed.

use Constaia\Exception\ConstaiaException;

$results = $constaia->analyzeMany(
    ['ana' => '/tmp/id_ana.jpg', 'luis' => '/tmp/id_luis.jpg'],
    ['expect' => 'es_dni', 'checks' => ['not_expired' => true]],
);

foreach ($results as $who => $result) {
    if ($result instanceof ConstaiaException) {
        echo "$who: error {$result->getErrorCode()}", PHP_EOL;
        continue;
    }
    echo "$who: ", $result->verdictStatus(), PHP_EOL;
}

For more than a handful of documents, or when you don't need the answer right away, use batches.

Pagination

analyses->list() returns a Constaia\Collection with the first page. foreach walks that page only; autoPagingIterator() (or all()) walks every page, fetching the next ones with starting_after.

$page = $constaia->analyses->list(['limit' => 100, 'type' => 'es_dni']);

$page->data();     // array of the current page
$page->hasMore();
$next = $page->nextPage(); // null when there are no more

foreach ($page->autoPagingIterator() as $analysis) {
    echo $analysis->id, ' ', $analysis->verdictStatus(), PHP_EOL;
}

More in Pagination.

Webhooks

Constaia\Webhook::verify() checks the Standard Webhooks signature against the raw body and returns the event as an associative array. It accepts headers from getallheaders() or the $_SERVER array (HTTP_WEBHOOK_ID…). Default tolerance: 300 seconds (fourth argument).

webhook.php
<?php
require __DIR__ . '/vendor/autoload.php';

use Constaia\Exception\SignatureVerificationException;
use Constaia\Webhook;

$payload = file_get_contents('php://input');

try {
    $event = Webhook::verify($payload, $_SERVER, getenv('CONSTAIA_WEBHOOK_SECRET'));
} catch (SignatureVerificationException $e) {
    http_response_code(400);
    exit;
}

switch ($event['type']) {
    case 'analysis.completed':
    case 'analysis.review_required':
        // $event['data'] is the analysis
        break;
    case 'analysis.failed':
    case 'batch.completed':
    case 'credits.low':
        break;
}

http_response_code(204);

For your tests, Webhook::headers($payload, $secret) builds signed headers like Constaia's and Webhook::sign($id, $timestamp, $payload, $secret) just the signature. More in Webhooks.

Exceptions

All extend Constaia\Exception\ConstaiaException and expose getType(), getErrorCode(), getParam(), getRequestId() and getHttpStatus() (also as properties type, errorCode, param, requestId, httpStatus).

ExceptionWhen
InvalidRequestException400, 409, 413, 415 and 422 (invalid file or option, size, type, idempotency). Also locally when a file can't be read.
AuthenticationException401, or no key configured.
InsufficientCreditsException402.
PermissionException403.
NotFoundException404.
RateLimitException429 after retries; getRetryAfter() returns the Retry-After seconds.
ApiException5xx after retries (for example 503 live_mode_unavailable).
ConnectionExceptionNetwork error or timeout.
SignatureVerificationExceptionInvalid webhook signature.
use Constaia\Exception;

try {
    $analysis = $constaia->analyze($path, ['expect' => 'es_dni']);
} catch (Exception\InsufficientCreditsException $e) {
    // Alert whoever manages the account and retry later
} catch (Exception\RateLimitException $e) {
    sleep($e->getRetryAfter() ?? 1);
} catch (Exception\InvalidRequestException $e) {
    echo $e->getErrorCode(), ' ', $e->getParam(), ': ', $e->getMessage();
} catch (Exception\ConstaiaException $e) {
    error_log(sprintf('Constaia %s %s (%s)', $e->getHttpStatus(), $e->getErrorCode(), $e->getRequestId()));
}

Every code is listed in Errors.

Retries and idempotency

  • Retries 408, 409 idempotency_in_progress, 429, 5xx (never 501) and network errors up to max_retries times (0 disables them), with exponential backoff and jitter, honouring Retry-After.
  • $constaia->lastRateLimit (Constaia\RateLimit: limit, remaining, reset, policy, retryAfter) keeps the limit headers of the last response.
  • Every POST carries an Idempotency-Key (UUID v4) kept across retries: a retry never charges twice. Pass your own with idempotency_key to dedupe across processes. See Idempotency.

Laravel and WordPress

  • Laravel: the package registers Constaia\Client as a singleton and the Constaia facade. Set CONSTAIA_API_KEY and CONSTAIA_WEBHOOK_SECRET in .env and, optionally, publish the config with php artisan vendor:publish --tag=constaia-config. Full guide in Laravel.
  • WordPress: the SDK repository includes an example plugin with a shortcode and a REST route. See WordPress.

Next steps

Sur cette page