Human review from your backend
Bring human review into your own application with the API. Get pending reviews, download the original document, record the decision and apply the final result.
When Constaia can't decide on its own, the analysis is left pending human review. Your team can review it in the dashboard, but if you already have your own internal tool (a back office, a CRM, a validation screen) you can do everything from your backend:
- Analyse the document with
expect. - Find out that it needs review (webhook or periodic polling).
- Download the original document to show it to the reviewer.
- Record the decision at Constaia.
- Apply the final result.
With the default storage, Constaia only keeps the document, encrypted, while it needs reviewing, and deletes it once decided. You don't have to hold a copy yourself.
The flow
Your server Constaia
─────────── ────────
1. POST /v1/analyze { expect, metadata } ─────────▶ review verdict → review.status: "pending"
the original is stored encrypted (storage.kept: true)
2. /webhooks/constaia ◀── analysis.review_required
or GET /v1/reviews?status=pending ─────────▶ list of pending reviews, oldest first
3. GET /v1/analyses/{id}/file ─────────────────────▶ the original document (or file_url, 15 min)
4. POST /v1/analyses/{id}/review { decision } ────▶ verdict.final_status: valid | invalid
(with Idempotency-Key) the original is deleted 24 h later (adjustable)
5. /webhooks/constaia ◀── analysis.reviewedThese endpoints only work with a secret key (ck_…), never from the browser. Full reference in
Reviews.
Step 1: analyse with expect
Review is about the verdict, so the analysis needs expect: without it there is nothing to approve or reject
(409 not_reviewable). Use metadata to link the analysis to the record in your system.
You don't need to send storage: the default mode (review) keeps the original only if the analysis ends up pending
review. If you send storage: "none", the document is never stored and the reviewer will only be able to see the
extracted data.
What ends up pending review:
- By default, analyses with a
reviewverdict. - If you use a template (
template: "tpl_…"), its review policy can also sendinvalidones, or everyvalidone if you'd rather none are approved automatically. The template can also set its own retention windows.
curl https://api.constaia.com/v1/analyze \
-H "Authorization: Bearer $CONSTAIA_API_KEY" \
-F file=@blurry.jpg \
-F 'options={"expect":["es_dni","es_nie","passport"],"metadata":{"registration_id":"124"}}'{
"id": "an_01J9Z8Q3K4M5N6P7Q8R9S0T1V3",
"verdict": { "status": "review", "final_status": null, "…": "…" },
"review": { "status": "pending", "decision": null, "assignee": null, "reviewed_by": null, "reviewed_at": null, "reason": null, "note": null },
"storage": { "mode": "review", "kept": true, "reason": "pending_review", "expires_at": "2026-10-29T10:05:01Z", "file_deleted_at": null },
"file_url": "https://api.constaia.com/v1/files/…",
"metadata": { "registration_id": "124" }
}Step 2: find out about pending reviews
There are two ways, and you can combine them:
analysis.review_requiredwebhook: arrives when an analysis finishes with areviewverdict. It's the most immediate. See Webhooks.- Poll
GET /v1/reviews: returns every pending analysis, oldest first, like a queue. It also includes the ones a template sends to review even when their verdict isvalidorinvalid. Use it in a periodic job or to build the inbox of your internal tool.
If you use both, identify each item by the analysis id so it isn't duplicated.
curl -G https://api.constaia.com/v1/reviews \
-H "Authorization: Bearer $CONSTAIA_API_KEY" \
--data-urlencode "status=pending" \
--data-urlencode "limit=50"Filter with type, template or metadata[key] if each team reviews a different document type or event.
Step 3: show the document to the reviewer
While the analysis is pending, the original is still stored (storage.kept: true). Two ways to get it:
- The analysis
file_url: a signed URL that expires after 15 minutes. Use it to show the image or PDF directly on your review screen. Don't store it: fetch the analysis again withGET /v1/analyses/{id}when you need a new one. GET /v1/analyses/{id}/file: downloads the file with your key, for example to serve it yourself from your backend. TheContent-Typeis the original's (image/jpeg,image/png,image/webp,image/heicorapplication/pdf).
Next to the document, show the verdict reasons (verdict.reasons with warning or error) and the extracted fields
(fields), so the reviewer can compare.
If the original is no longer stored, the download returns 404 file_not_stored: no review was needed, it was
analysed with storage: "none" or it was already deleted after its window. The extracted data is still available, so
the reviewer can decide with it or ask for a new document.
curl -o original \
https://api.constaia.com/v1/analyses/an_01J9Z8Q3K4M5N6P7Q8R9S0T1V3/file \
-H "Authorization: Bearer $CONSTAIA_API_KEY"Step 4: record the decision
When the reviewer decides, send approve or reject with a short reason (reason, up to 200 characters) and,
optionally, an internal note (note, up to 2000). Always send an Idempotency-Key: if the network fails and you
retry, the decision isn't duplicated.
- Approving sets
verdict.final_statustovalid; rejecting, toinvalid. review.reviewed_byholds your API key id (key_…), and the decision is saved in the account's audit log under that key. Store in your system which person decided, or put it innote.- Repeating the same decision returns the analysis as is, with no error.
curl https://api.constaia.com/v1/analyses/an_01J9Z8Q3K4M5N6P7Q8R9S0T1V3/review \
-H "Authorization: Bearer $CONSTAIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: review-an_01J9Z8Q3K4M5N6P7Q8R9S0T1V3" \
-d '{ "decision": "reject", "reason": "The expiry date is not readable in the photo", "note": "Reviewed by Ana" }'Step 5: apply the result
Apply the outcome in your system with verdict.final_status: valid, confirm; invalid, reject or ask for another
document. You can do it with the response to the decision or with the analysis.reviewed webhook, which is sent
whenever a review is decided, from the dashboard or through the API. If part of your team reviews in the
dashboard, listening to this webhook keeps your system up to date.
if (event.type === "analysis.reviewed") {
const analysis = event.data;
await applyDecision(analysis.metadata.registration_id, analysis.verdict.final_status);
}What happens to the document afterwards
- Once decided, the original is kept for 24 more hours (so the decision can be checked) and then deleted.
storage.expires_attells you when. - If nobody decides, it is deleted 30 days after the analysis finished. The extracted data is still available.
- Both windows are changed in the dashboard (Account settings) and, if you need to, per template:
review_retention_hours_after_decision(0–720 hours; with 0 it is deleted on decision) andreview_max_days(1–90 days).
More detail in Storage and privacy.
Common errors
| HTTP | code | What it means | What to do |
|---|---|---|---|
404 | file_not_stored | The original isn't stored (no review was needed, storage: "none" or already deleted). | Review with the extracted data or ask for a new document. |
409 | already_reviewed | The analysis already has a different decision. | Don't retry. Only an owner or admin can change it, from the dashboard. |
409 | not_reviewable | There is no verdict: it was analysed without expect or hasn't finished yet. | Analyse with expect or wait for it to finish. |
403 | publishable_key_not_allowed | You used a publishable key (pk_…). | Use the secret key, always from your backend. |
Every code in Errors.
Test it
With a ck_test_… key, analyse an image called blurry.jpg with expect: "es_dni": the verdict is review and the
analysis shows up in GET /v1/reviews. Decide it with POST /v1/analyses/{id}/review and, if you have a test webhook
endpoint, you will receive analysis.review_required and then analysis.reviewed. More in
Test mode.
Next steps
Human review
What to do with the review verdict. Why it happens, receiving it by webhook, building a review queue, recording the decision and asking for a new photo.
US documents
The United States document types in the Constaia catalogue, PDF417 (AAMVA) barcode reading on driver's licenses, Form I-9 lists, and validation of SSN, EIN and other identifiers.