Constaia
Endpoints

POST /v1/classify

Reference for POST /v1/classify: identify a document's type for 0.2 credits, with candidates and a type verdict, to route it before analysing it.

POST https://api.constaia.com/v1/classify answers a single question: which document is this. It does not extract fields or run checks, and it costs 0.2 credits per document, whatever its page count (0 in test mode).

Use it when you receive documents without knowing what they are (a mailbox, a shared folder, a form with a single "attach your documents" field) and need to route them: decide which queue each one goes to, or which expect and checks to apply next in POST /v1/analyze. If you already know which document you expect, call analyze with expect directly: you save one call.

Request

It accepts exactly the same inputs as POST /v1/analyze: multipart/form-data with file and options, or JSON with file_url or file_base64 + filename. Same formats, same 20 MB limit and the same -F expect=… shortcut.

Options are validated with the same strict schema. Those that have an effect on a classification are:

OptionDescription
expectAccepted type or types. With expect you get a type verdict.
languageLanguage of document.label and of the verdict.reasons messages.
metadataYour data (up to 20 keys), returned as sent.
asynctrue to answer 202 immediately and receive the result by webhook.
curl https://api.constaia.com/v1/classify \
  -H "Authorization: Bearer $CONSTAIA_API_KEY" \
  -F file=@dni_valid.jpg \
  -F 'options={"expect":["es_dni","es_nie","passport"],"language":"en"}'

Response: the classification object

200 OK (dni_valid.jpg, test mode, language en)
{
  "id": "an_01J9Z8Q3K4M5N6P7Q8R9S0T1V2",
  "object": "classification",
  "status": "completed",
  "livemode": false,
  "created_at": "2026-09-29T10:00:00Z",
  "completed_at": "2026-09-29T10:00:01Z",
  "file": { "name": "dni_valid.jpg", "mime_type": "image/jpeg", "pages": 1, "size_bytes": 482133 },
  "document": { "type": "es_dni", "label": "Spanish ID card (DNI)", "confidence": 0.97 },
  "candidates": [{ "type": "es_dni", "confidence": 0.97 }],
  "verdict": {
    "expected": ["es_dni", "es_nie", "passport"],
    "match": true,
    "status": "valid",
    "reasons": [{ "code": "type_match", "severity": "info", "message": "The document is Spanish ID card (DNI)." }]
  },
  "warnings": [],
  "usage": { "credits": 0, "pages": 1 },
  "metadata": {}
}
FieldDescription
idId with prefix an_.
objectAlways "classification".
statusqueued, processing, completed or failed.
livemodetrue with a live key.
created_at, completed_atISO 8601 dates; completed_at is null until it finishes.
filename, mime_type, pages, size_bytes.
documentMost likely type: type, label, confidence (0–1). null if it could not be classified.
candidatesCandidate types with their confidence. Useful to decide yourself when confidence is low.
verdictOnly with expect. It is a type verdict: type_match (the type is in expected), type_mismatch (another catalogue type is recognised, invalid) or type_unknown (it could not be identified or confidence is below 0.5, review). It may add low_quality or low_confidence as warning, which lead to review.
warningsQuality warnings detected.
usagecredits (0.2 in live, 0 in test) and pages.
metadataYour metadata.
errorOnly when status is failed.

A classify verdict of valid only says the type is the expected one. It does not check expiry, holder or any other rule: that is what analyze is for.

Like analyze, it can answer 202 if it goes asynchronous; in that case the result arrives in the analysis.completed webhook with data.object: "classification". Errors are the same as in analyze.

Pattern: classify, then analyse

Classify the document with the list of types your flow knows how to handle.

Route on document.type: each type has its own checks. If the verdict is not valid (type_mismatch, type_unknown or low confidence), send it to manual review.

Analyse with expect set to the detected type and that type's checks.

route-document.ts
import { Constaia, type Checks } from "@constaia/sdk";
import { fromPath } from "@constaia/sdk/node";

const constaia = new Constaia();

const checksByType: Record<string, Checks> = {
  payment_receipt: { expectedAmount: 45, expectedReference: "INSCRIPCION 123" },
  medical_certificate_sport: { maxAgeDays: 365, requireSignature: true },
  invoice: {},
};

const file = await fromPath("./attachment.pdf");
const classification = await constaia.classify(file, { expect: Object.keys(checksByType) });

const type = classification.document?.type;
if (classification.verdict?.status !== "valid" || !type) {
  console.log("Send to manual review:", classification.candidates);
} else {
  const analysis = await constaia.analyze(file, { expect: type, checks: checksByType[type] });
  console.log(type, analysis.verdict?.status);
}

With this pattern you pay an extra 0.2 credits per document. If your documents are always of a few types, it is usually cheaper to call analyze directly with expect as a list and branch on document.type, as explained in Document types.

Next steps

On this page