Constaia
Conceptos

Checks

Referencia de las opciones de checks (vigencia, antigüedad, edad, titular, firma, importes) y de las validaciones deterministas de NIF, MRZ, IBAN y facturas.

Los checks son las reglas de negocio que Constaia evalúa sobre un documento. Se mandan en options.checks de POST /v1/analyze (y en las opciones de los lotes) y cada uno produce uno o varios motivos en verdict.reasons. Cómo se combinan esos motivos en valid, invalid o review está en veredictos.

Hay dos familias:

  • Checks que pides tú (options.checks): vigencia, antigüedad, edad, titular, campos obligatorios, firma, sello, importes, IBAN y referencia.
  • Comprobaciones deterministas (checks[] en la respuesta): las ejecuta Constaia siempre que el tipo de documento las admite (letra del NIF, MRZ, IBAN, totales de factura…). No se configuran.

Los checks necesitan expect

Los checks se evalúan siempre, pero sus motivos viven dentro de verdict, y verdict solo existe si mandas expect. Sin expect verás fields y checks[], pero no el resultado de not_expired, holder y demás.

Ejemplo

Terminal
curl https://api.constaia.com/v1/analyze \
  -H "Authorization: Bearer $CONSTAIA_API_KEY" \
  -F file=@dni_valid.jpg \
  -F 'options={
    "expect": "es_dni",
    "checks": {
      "min_age_years": 18,
      "holder": { "full_name": "María García López", "document_number": "12345678Z" }
    }
  }'

camelCase en el SDK de JavaScript, snake_case en la API

El SDK de JavaScript acepta las opciones en camelCase (notExpired, maxAgeDays, holder.fullName, expectedAmount…) y las convierte a snake_case. La API, el SDK de PHP, Python y el MCP usan snake_case tal cual (not_expired, max_age_days, holder.full_name, expected_amount). Las respuestas son siempre snake_case.

Referencia de opciones

Opción (API)SDK JSTipoPor defectoMotivo que produce
not_expirednotExpiredbooleantrue en tipos con caducidadnot_expired
reference_datereferenceDate"YYYY-MM-DD"hoy(cambia la fecha de las demás reglas)
max_age_daysmaxAgeDaysentero 0–36500—max_age_days
min_age_yearsminAgeYearsentero 0–150—age o min_age_years
max_age_yearsmaxAgeYearsentero 0–150—age o max_age_years
holderholderobjeto—holder
require_fieldsrequireFieldsstring[] (máx. 50)—required_field_missing
require_signaturerequireSignatureboolean—require_signature
require_stamprequireStampboolean—require_stamp
expected_amountexpectedAmountnumber—expected_amount
expected_ibanexpectedIbanstring (máx. 40)—expected_iban
expected_referenceexpectedReferencestring (máx. 140)—expected_reference

Los checks se aplican al tipo detectado (document.type) y leen sus campos. Para saber qué checks tienen sentido en cada tipo, consulta checks y expiry_check_default en GET /v1/document-types/{type} o el catálogo.

not_expired

Comprueba que la fecha de caducidad es igual o posterior a hoy (o a reference_date).

  • Activado por defecto en los tipos con caducidad (expiry_check_default: true, por ejemplo es_dni, es_nie y passport). Para desactivarlo manda "not_expired": false.
  • Solo se evalúa si el tipo tiene fecha de caducidad.
  • info si está vigente, error si ha caducado, warning si no se pudo leer la fecha.
DNI caducado
{ "code": "not_expired", "severity": "error", "message": "Caducado el 15/06/2020." }

reference_date

Fecha YYYY-MM-DD que sustituye a "hoy" en not_expired, max_age_days y en el cálculo de edad. Útil para evaluar a fecha de un evento: "¿estará vigente el DNI el día de la competición?", "¿tendrá 18 años el 1 de enero?".

Opciones
{ "expect": "es_dni", "checks": { "reference_date": "2027-01-01", "min_age_years": 18 } }

max_age_days

La fecha de emisión no puede tener más de N días. Típico en certificados: médico de menos de un año, certificado de delitos sexuales de menos de 90 días.

  • info si cumple, error si es más antiguo (o si la fecha de emisión está más de un día en el futuro), warning si no hay fecha de emisión.
Certificado de delitos sexuales emitido hace 14 días
{ "code": "max_age_days", "severity": "info", "message": "Emitido hace 14 días (máximo 90)." }

min_age_years y max_age_years

Edad del titular calculada con la fecha de nacimiento a hoy (o a reference_date).

  • Si cumple: un motivo age con info ("El titular tiene 36 años.").
  • Si es más joven que el mínimo: min_age_years con error. Si es mayor que el máximo: max_age_years con error.
  • Si no se pudo leer la fecha de nacimiento: age con warning.

holder

Compara los datos del documento con los de la persona que esperas. Todos los subcampos son opcionales; manda solo los que tengas.

Subcampo (API)SDK JSCómo se compara
full_namefullNameNombre completo con normalización (ver abajo).
first_namefirstNameCon last_name, como nombre completo. Solo, basta con que sus palabras aparezcan en el nombre del documento.
last_namelastNameIgual que first_name.
document_numberdocumentNumberNúmero normalizado (sin espacios ni guiones, sin distinguir mayúsculas).
birth_datebirthDateIgualdad exacta en formato YYYY-MM-DD.

Reglas de comparación de nombres:

  • No distingue mayúsculas ni acentos: María García = MARIA GARCIA.
  • Admite distinto orden de apellidos y nombre.
  • Tolera errores tipográficos pequeños.
  • Con solo first_name o solo last_name, las partículas (de, del…) no cuentan.

Resultado:

  • Si todo lo que mandaste coincide: un motivo holder con info que lista los campos ("Los datos del titular coinciden (full_name).").
  • Cada dato que no coincide: holder con error, indicando lo leído y lo esperado.
  • Cada dato que el documento no muestra o no se pudo leer: holder con warning.
Titular distinto (language: en)
{ "code": "holder", "severity": "error", "message": "Holder mismatch: full_name is “MARÍA GARCÍA LÓPEZ”, expected “Juan Pérez”." }

require_fields

Lista de campos que deben venir con valor. Admite rutas con punto para campos anidados (por ejemplo seller.tax_id en invoice). Cada campo vacío produce un motivo required_field_missing con error. Los nombres de campo de cada tipo están en GET /v1/document-types/{type}.

require_signature y require_stamp

Exigen firma o sello. Leen los campos signature_present y stamp_present, que existen en tipos como medical_certificate_sport. info si están, error si no. Úsalos solo en tipos que tengan esos campos: en un tipo sin ellos el resultado es error.

expected_amount, expected_iban, expected_reference

Para justificantes de pago (payment_receipt) y facturas (invoice):

OpciónQué compara
expected_amountamount (o total en invoice) con una tolerancia de medio céntimo.
expected_ibanQue el IBAN aparezca entre los del documento (ordenante o beneficiario). Sin espacios y sin distinguir mayúsculas.
expected_referenceQue el texto esperado, normalizado, esté contenido en reference o en concept.
Opciones
{
  "expect": "payment_receipt",
  "checks": {
    "expected_amount": 45,
    "expected_iban": "ES7921000813610123456789",
    "expected_reference": "INSCRIPCION 123"
  }
}
Motivos
[
  { "code": "type_match", "severity": "info", "message": "El documento es Justificante de pago / transferencia." },
  { "code": "expected_amount", "severity": "info", "message": "amount coincide con lo esperado." },
  { "code": "expected_iban", "severity": "info", "message": "iban coincide con lo esperado." },
  { "code": "expected_reference", "severity": "info", "message": "reference coincide con lo esperado." }
]

Reglas propias de cada tipo

Algunas reglas se aplican siempre, sin pedirlas:

TipoCódigoSeveridad
medical_certificate_sportnot_fit_for_sporterror si el certificado no declara apto.
es_sexual_offences_certificate, es_criminal_record_certificatehas_recordsinfo sin antecedentes, error con antecedentes.

Comprobaciones deterministas

Además de lo que pides, Constaia ejecuta validaciones algorítmicas (sin IA) según el tipo. Aparecen en checks[]:

checks[] de un DNI
"checks": [
  { "code": "nif_check_digit", "passed": true, "message": "La letra del documento 12345678Z es correcta." },
  { "code": "mrz_checksums", "passed": true, "message": "Los dígitos de control de la MRZ son correctos." },
  { "code": "mrz_matches_visual", "passed": true, "message": "La MRZ coincide con los datos impresos." }
]
CódigoQué verifica
nif_check_digitLetra de control de cada DNI, NIE o CIF que aparece en el documento (titular, emisor, comprador…). Los números que no tienen formato español (pasaportes, identificadores extranjeros) se ignoran.
mrz_checksumsDígitos de control de la zona MRZ (DNI, NIE/TIE, pasaporte). En un DNI o NIE con una sola cara, sin MRZ, se añade el aviso side_missing.
mrz_matches_visualCon una MRZ válida: que número, fecha de nacimiento, caducidad y nombre coincidan con lo impreso.
iban_checksumDígitos de control de cada IBAN (una entrada por IBAN: ordenante, beneficiario…).
invoice_totalsQue líneas, base imponible, IVA, retención y total de la factura cuadren.
csv_formatSolo el formato del código seguro de verificación de certificados españoles.
date_consistencyCoherencia de fechas: nacimiento anterior a la emisión, emisión anterior a la caducidad, nacimiento no futuro. Solo aparece cuando falla.

Qué validadores tiene cada tipo lo indica validators en GET /v1/document-types/{type}.

Cuando una comprobación falla (passed: false), se añade a verdict.reasons un motivo con el mismo código y severidad error, así que el veredicto pasa a invalid. Además, el campo afectado queda con fields.<campo>.validated: false; si pasa, validated: true. Los campos sin validador tienen validated: null.

csv_format no consulta al Ministerio

csv_format comprueba que el código tiene un formato válido, no que el certificado exista. Constaia no consulta el servicio de verificación del Ministerio de Justicia. Si necesitas esa garantía, verifica el CSV en la sede electrónica oficial del emisor.

Opciones estrictas: 422

options y options.checks son estrictos. Una clave desconocida, un tipo incorrecto o un valor fuera de rango devuelven 422 con código invalid_parameter. param indica dónde está el problema:

  • Valor no válido: la ruta completa, por ejemplo options.checks.max_age_days.
  • Clave desconocida: la ruta del objeto que la contiene (options u options.checks), y el message nombra la clave.
422: clave desconocida en checks
{
  "error": {
    "type": "invalid_request",
    "code": "invalid_parameter",
    "message": "Unrecognized key: \"not_expire\"",
    "param": "options.checks",
    "request_id": "req_01J..."
  }
}

Así un error tipográfico (not_expire, holder.name) nunca se ignora en silencio. En el SDK de JavaScript llega como InvalidRequestError; en PHP como InvalidRequestException. Ver errores.

Siguientes pasos

En esta página