Key concepts
Document types, expect, verdicts, reasons, fields, checks, warnings, test and live modes, credits, storage, webhooks and idempotency in Constaia.
This page sums up each API concept in a few lines. Every section links to the detailed reference.
Document type and catalogue
Each document is classified into a type from the catalogue: es_dni, es_nie, passport, eu_id_card,
medical_certificate_sport, payment_receipt, invoice… If it fits none, the type is generic. The detected
type comes in document.type, with its label (document.label), confidence, side (side) and country.
The catalogue grows over time, so don't copy the list into your code: look it up in the
document catalogue or with GET /v1/document-types (public, no key). Each type lists its fields,
validators and supported checks. See Document types.
expect
expect tells Constaia which document you expect. It can be one type ("es_dni") or a list of up to 20
(["es_dni", "es_nie", "passport"]). Without expect you get classification and data, but verdict is null.
A type that doesn't exist in the catalogue returns 422 invalid_parameter with param: "options.expect".
Verdict and reasons
With expect, every analysis has verdict.status:
| Status | When |
|---|---|
| Válido | The type matches and no reason is warning or error. |
| No válido | At least one reason has severity error. |
| Revisar | No error, but at least one warning. |
verdict.reasons is the list of reasons. Each has a stable code (type_match, not_expired, holder,
max_age_days, low_quality…), a severity (info, warning or error) and a message localised according
to language. The same code appears with different severities depending on the outcome: a valid ID card gives
not_expired with info; an expired one, not_expired with error. Code against code and severity, never
against the text. See Verdicts and reasons.
Fields
fields holds the extracted data. Each field is an object:
{
"value": "12345678Z",
"confidence": 0.99,
"validated": true,
"source": { "page": 1, "bbox": [0.61, 0.12, 0.83, 0.16] }
}value: the normalised value (dates asYYYY-MM-DD).confidence: from 0 to 1.validated:trueorfalseif a deterministic validator checked it (NIF check letter, MRZ, IBAN…);nullif none applies.source.bbox: rectangle[x0, y0, x1, y1]normalised 0 to 1 on the source page. May benull.
With extract you can turn extraction off (false) or pass your own JSON Schema: the fields returned are then
those of your schema. See Analyze.
Checks and reasons
Two things have similar names:
checksin the options: business rules you ask for (not_expired,max_age_days,min_age_years,holder,require_signature,expected_amount,expected_iban…). Their result appears as reasons inverdict.reasons.checksin the response: deterministic validations Constaia always runs when the type allows it (nif_check_digit,mrz_checksums,mrz_matches_visual,iban_checksum,invoice_totals,csv_format,date_consistency). Each one is{ code, passed, message }. If one fails, it also appears as anerrorreason with the same code.
not_expired is on by default for types with an expiry date; send "not_expired": false to turn it off. Checks
only apply if the detected type has the relevant field. See Checks.
Warnings
warnings is a list of signals about the image or document: low_quality, blurry, cropped, glare,
screen_photo_suspected, photocopy_suspected, edited_suspected, multiple_documents, side_missing,
language_mismatch. Quality ones produce the low_quality reason with severity warning, which leads to
review.
They are signals, not proof of fraud. Constaia does no biometrics or face matching.
| Código | Significado |
|---|---|
low_quality | Qualidade baixa em geral. |
blurry | Imagem desfocada. |
cropped | O documento está cortado. |
glare | Reflexos que tapam dados. |
screen_photo_suspected | Possível fotografia de um ecrã. |
photocopy_suspected | Possível fotocópia. |
edited_suspected | Possível edição digital. |
multiple_documents | Há mais de um documento no ficheiro. |
side_missing | Falta uma face. |
language_mismatch | O idioma não é o esperado para o tipo. |
Os warnings são indícios, não prova de autenticidade.
Test mode and live mode
ck_test_... | ck_live_... | |
|---|---|---|
| Result | Deterministic, based on the file name | Real AI analysis |
| Credits | Never consumed (usage.credits: 0) | Consumes credits |
livemode | false | true |
| Requirement | None | Verified email |
Analyses, listings and webhooks are separate per mode. See Test mode and Authentication.
Credits
1 credit = 1 analysis of up to 2 pages; every 2 extra pages, 1 more credit (ceil(pages / 2), minimum 1).
classify costs 0.2 credits. Exports are included. Failures on our side, quality rejections before OCR and test
mode are not charged. The free plan gives 150 credits per month; after that, packs. Each analysis's consumption
is in usage. See Credits and billing and Pricing.
Storage
storage | What happens to the file |
|---|---|
none | Synchronous analysis: processed in memory, never stored. Async or batch: stored encrypted only until it finishes. |
temporary | Deleted after ttl_hours (1–720, default 24). |
persistent | Kept until DELETE /v1/analyses/{id}. |
If you don't set it, your account default (configurable in the dashboard) or none is used. With
keep_results: false results are not stored either. storage.file_deleted_at tells you when the file was
deleted. See Storage and privacy.
Processing profile
With processing you choose which AI providers process each document: sovereign (only providers headquartered and
operated in the EU) or standard (also Claude on AWS Bedrock or Gemini on Vertex AI, in EU regions). If you don't set
it, your account's profile is used. Every analysis returns the processing object with the profile, region and
providers that handled it. See Data residency.
Synchronous and asynchronous
POST /v1/analyze waits up to 30 seconds. If it finishes, it responds 200 with the full analysis. If not, it
responds 202 with status: "queued" or "processing", and the result arrives by webhook or by polling
GET /v1/analyses/{id}.
With async: true the response is always an immediate 202. Use it for long PDFs (up to 200 pages async; 30
sync) or when you don't want to block the request. Batches are always asynchronous.
Webhooks
You register an HTTPS URL with POST /v1/webhook-endpoints and pick events: analysis.completed,
analysis.failed, analysis.review_required, batch.completed, credits.low or *. Each delivery is signed
following Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature) and retried for about 3 days.
Deduplicate on webhook-id. See Webhooks.
Idempotency
Send the Idempotency-Key header on any POST. If you repeat the same key with the same content within 24 hours,
you get the stored response (with Idempotent-Replayed: true) and are not charged twice. The JavaScript and PHP
SDKs generate a key automatically on every POST. See Idempotency.
Metadata
metadata is a string-to-string object (up to 20 keys, key up to 40 characters, value up to 500) stored with the
analysis and returned in the response and in webhooks. Use it to link your IDs ({ "registration_id": "123" })
and filter: GET /v1/analyses?metadata[registration_id]=123. See Analyses.