Revisión humana
Qué hacer con el veredicto review. Por qué ocurre, cómo recibirlo por webhook, montar una cola de revisión, registrar la decisión y pedir otra foto.
El veredicto Revisar significa que Constaia no puede decidir con seguridad: la foto está borrosa, un dato no se lee o la confianza es baja. No es un rechazo. La mayoría de las veces el documento es correcto y basta con que una persona lo mire o con pedir otra foto.
Esta guía monta ese circuito en tu aplicación: detectar el review, ponerlo en una cola, enseñarlo a un revisor,
guardar su decisión y, si hace falta, pedir un documento nuevo.
Cuándo sale review
El veredicto es review cuando hay algún motivo con severity: "warning" y ninguno con error:
code con warning | Causa |
|---|---|
low_quality | Hay avisos de calidad o de autenticidad en warnings (ver tabla de abajo). |
low_confidence | La confianza en el tipo o en los campos clave es baja: "La confianza es baja (0.62); conviene revisarlo a mano.". |
type_unknown | No se ha podido identificar el tipo de documento. |
not_expired, max_age_days, age | Pediste el check pero no se encuentra la fecha de caducidad, de emisión o de nacimiento. |
holder | Pediste comparar un dato del titular y no se ha podido leer. |
Si la imagen es tan mala que se rechaza antes del OCR, el análisis termina con document: null, review y el motivo
low_quality. Esos análisis no se cobran.
Avisos que pueden aparecer en warnings:
| Código | Significado |
|---|---|
low_quality | Calidad baja en general. |
blurry | Imagen desenfocada. |
cropped | El documento está recortado. |
glare | Reflejos que tapan datos. |
screen_photo_suspected | Posible foto de una pantalla. |
photocopy_suspected | Posible fotocopia. |
edited_suspected | Posible edición digital. |
multiple_documents | Hay más de un documento en el fichero. |
side_missing | Falta una cara. |
language_mismatch | El idioma no es el esperado para el tipo. |
Los warnings son indicios, no prueba de autenticidad.
Los avisos son señales, no pruebas de fraude. Constaia no es un KYC biométrico ni compara caras.
Paso 1: decide dónde está el documento para el revisor
Un revisor necesita ver el documento. Constaia no tiene un endpoint para descargar el fichero original que subiste (solo las exportaciones de los datos), así que tienes dos opciones:
- Guardar tu propia copia solo mientras haga falta: al recibir el fichero, guárdalo cifrado en tu almacenamiento.
Si el veredicto es
validoinvalid, bórralo al momento; si esreview, consérvalo hasta que el revisor decida. - No guardar nada y pedir otra foto: si no quieres custodiar documentos, en
reviewpide directamente un documento nuevo (paso 5). Es la opción más simple cuando el motivo es sololow_quality.
Guardar el fichero en Constaia (storage: "temporary") no le sirve a tu revisor, porque no puede descargarlo. Si
envías storage: "none", que es lo habitual, recuerda que la copia de referencia es la tuya.
Paso 2: recibir el review
En un análisis síncrono el review llega en la propia respuesta. Además, Constaia envía el webhook
analysis.review_required:
| Origen | Eventos que recibes |
|---|---|
Análisis individual (síncrono o async) | analysis.completed y analysis.review_required |
| Documento de un lote | Solo analysis.review_required (y batch.completed al final del lote) |
Si procesas tanto la respuesta como los webhooks, crea el elemento de la cola con el id del análisis como clave única:
así no se duplica. Usa metadata para saber a qué registro de tu sistema corresponde.
CREATE TABLE review_queue (
analysis_id text PRIMARY KEY,
registration_id text NOT NULL,
reasons jsonb NOT NULL,
warnings jsonb NOT NULL,
file_path text,
status text NOT NULL DEFAULT 'pending', -- pending | approved | rejected | new_document_requested
reviewer text,
decided_at timestamptz,
note text,
created_at timestamptz NOT NULL DEFAULT now()
);import express from "express";
import { Constaia, WebhookVerificationError } from "@constaia/sdk";
import { sql } from "./db.js";
const app = express();
const constaia = new Constaia();
app.post("/webhooks/constaia", express.raw({ type: "application/json" }), async (req, res) => {
let event;
try {
event = await constaia.webhooks.verify(req.body, req.headers, process.env.CONSTAIA_WEBHOOK_SECRET);
} catch (err) {
if (err instanceof WebhookVerificationError) return res.status(400).send("invalid signature");
throw err;
}
if (event.type === "analysis.review_required") {
const analysis = event.data;
await sql`
INSERT INTO review_queue (analysis_id, registration_id, reasons, warnings)
VALUES (${analysis.id}, ${analysis.metadata.registration_id},
${JSON.stringify(analysis.verdict.reasons.filter((r) => r.severity !== "info"))},
${JSON.stringify(analysis.warnings)})
ON CONFLICT (analysis_id) DO NOTHING`;
}
res.sendStatus(200);
});
app.listen(3000);Paso 3: enseñar el documento al revisor
En la pantalla de revisión muestra:
- Tu copia del documento (paso 1).
- Los motivos con
warning: explican por qué no se decidió solo. Ya vienen en el idioma delanguage. - Los campos extraídos y su
confidence, para que el revisor compare con la imagen. Si guardas resultados (keep_results: true, por defecto), léelos cuando los necesites conGET /v1/analyses/{id}; si no, guarda en la cola los campos que el revisor tenga que ver. fields.<campo>.source.bbox(coordenadas normalizadas 0–1 en la páginasource.page), por si quieres resaltar dónde está cada dato en la imagen. Puede sernull.
Paso 4: registrar la decisión
La decisión humana se guarda en tu sistema: Constaia no tiene un endpoint para cambiar el veredicto de un análisis.
Guarda quién decidió, cuándo, qué y por qué, y enlázalo con el analysis_id:
UPDATE review_queue
SET status = 'approved', reviewer = 'ana@club.example', decided_at = now(),
note = 'DNI legible en la copia; datos coinciden con el formulario'
WHERE analysis_id = 'an_01J...';Después aplica la consecuencia (confirmar la inscripción, rechazarla) y borra tu copia del documento. Si guardaste el
fichero o los resultados en Constaia y ya no los necesitas, bórralos con DELETE /v1/analyses/{id}.
Paso 5: pedir una foto nueva
Cuando el motivo es de calidad (low_quality) o el revisor no puede leer el documento, pide otro. Envía a la persona
un enlace a tu página de subida (con el widget, que avisa de fotos borrosas, oscuras o con poca
resolución antes de enviar) y vuelve a analizar. El nuevo análisis tiene otro id: guarda la relación con el anterior
y marca el elemento de la cola como new_document_requested.
Consejos que puedes mostrar según el aviso: blurry (sujeta el móvil firme y enfoca), glare (evita reflejos, sin
flash), cropped (que se vean las cuatro esquinas), side_missing (sube las dos caras),
screen_photo_suspected o photocopy_suspected (fotografía el documento original).
Probarlo
Con una clave ck_test_…, una imagen llamada blurry.jpg (o que contenga blur o borros) devuelve el DNI de
dni_valid con los avisos blurry y low_quality:
{
"verdict": {
"expected": ["es_dni"],
"match": true,
"status": "review",
"reasons": [
{ "code": "type_match", "severity": "info", "message": "El documento es DNI (España)." },
{ "code": "not_expired", "severity": "info", "message": "Vigente hasta el 12/03/2031." },
{ "code": "low_quality", "severity": "warning", "message": "La calidad de la imagen es insuficiente (blurry, low_quality)." }
]
},
"warnings": ["blurry", "low_quality"]
}Si la envías como análisis individual, recibirás analysis.completed y analysis.review_required; dentro de un lote,
solo analysis.review_required. Desde el panel también puedes mandar un evento de prueba a tu endpoint. Más en
Modo test.
Siguientes pasos
Solo analizar sin guardar
Minimización de datos (RGPD) con Constaia. Analiza documentos sin guardar el fichero ni los datos extraídos con storage none y keep_results false.
Documentos de EE. UU.
Los tipos de documento de Estados Unidos del catálogo de Constaia, la lectura del código PDF417 (AAMVA) de permisos de conducir, las listas del formulario I-9 y la validación de SSN, EIN y otros identificadores.